3 * Copyright (c) 2001-2002, Biswapesh Chattopadhyay
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License.
11 * @file tm_workspace.h
12 The TMWorkspace structure is meant to be used as a singleton to store application
15 The workspace is intended to contain a list of global tags
16 and a set of individual source files.
25 #include <sys/types.h>
31 #include <glib/gstdio.h>
33 #include "tm_workspace.h"
34 #include "tm_ctags_wrappers.h"
36 #include "tm_parser.h"
39 /* when changing, always keep the three sort criteria below in sync */
40 static TMTagAttrType workspace_tags_sort_attrs
[] =
42 tm_tag_attr_name_t
, tm_tag_attr_file_t
, tm_tag_attr_line_t
,
43 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
46 /* for file tags the file is always identical, don't use for sorting */
47 static TMTagAttrType file_tags_sort_attrs
[] =
49 tm_tag_attr_name_t
, tm_tag_attr_line_t
,
50 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
53 /* global tags don't have file/line information */
54 static TMTagAttrType global_tags_sort_attrs
[] =
57 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
60 static TMTagType TM_TYPE_WITH_MEMBERS
=
61 tm_tag_class_t
| tm_tag_struct_t
| tm_tag_union_t
|
62 tm_tag_enum_t
| tm_tag_interface_t
;
64 static TMTagType TM_GLOBAL_TYPE_MASK
=
65 tm_tag_class_t
| tm_tag_enum_t
| tm_tag_interface_t
|
66 tm_tag_struct_t
| tm_tag_typedef_t
| tm_tag_union_t
| tm_tag_namespace_t
;
68 static TMWorkspace
*theWorkspace
= NULL
;
71 static gboolean
tm_create_workspace(void)
73 theWorkspace
= g_new(TMWorkspace
, 1);
74 theWorkspace
->tags_array
= g_ptr_array_new();
76 theWorkspace
->global_tags
= g_ptr_array_new();
77 theWorkspace
->source_files
= g_ptr_array_new();
78 theWorkspace
->typename_array
= g_ptr_array_new();
79 theWorkspace
->global_typename_array
= g_ptr_array_new();
82 tm_parser_verify_type_mappings();
88 /* Frees the workspace structure and all child source files. Use only when
89 exiting from the main program.
91 void tm_workspace_free(void)
96 g_message("Workspace destroyed");
99 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
100 tm_source_file_free(theWorkspace
->source_files
->pdata
[i
]);
101 g_ptr_array_free(theWorkspace
->source_files
, TRUE
);
102 tm_tags_array_free(theWorkspace
->global_tags
, TRUE
);
103 g_ptr_array_free(theWorkspace
->tags_array
, TRUE
);
104 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
105 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
106 g_free(theWorkspace
);
111 /* Since TMWorkspace is a singleton, you should not create multiple
112 workspaces, but get a pointer to the workspace whenever required. The first
113 time a pointer is requested, or a source file is added to the workspace,
114 a workspace is created. Subsequent calls to the function will return the
117 const TMWorkspace
*tm_get_workspace(void)
119 if (NULL
== theWorkspace
)
120 tm_create_workspace();
125 static void tm_workspace_merge_tags(GPtrArray
**big_array
, GPtrArray
*small_array
)
127 GPtrArray
*new_tags
= tm_tags_merge(*big_array
, small_array
, workspace_tags_sort_attrs
, FALSE
);
128 /* tags owned by TMSourceFile - free just the pointer array */
129 g_ptr_array_free(*big_array
, TRUE
);
130 *big_array
= new_tags
;
134 static void merge_extracted_tags(GPtrArray
**dest
, GPtrArray
*src
, TMTagType tag_types
)
138 arr
= tm_tags_extract(src
, tag_types
);
139 tm_workspace_merge_tags(dest
, arr
);
140 g_ptr_array_free(arr
, TRUE
);
144 static void update_source_file(TMSourceFile
*source_file
, guchar
* text_buf
,
145 gsize buf_size
, gboolean use_buffer
, gboolean update_workspace
)
148 g_message("Source file updating based on source file %s", source_file
->file_name
);
151 if (update_workspace
)
153 /* tm_source_file_parse() deletes the tag objects - remove the tags from
154 * workspace while they exist and can be scanned */
155 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
156 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
158 tm_source_file_parse(source_file
, text_buf
, buf_size
, use_buffer
);
159 tm_tags_sort(source_file
->tags_array
, file_tags_sort_attrs
, FALSE
, TRUE
);
160 if (update_workspace
)
163 g_message("Updating workspace from source file");
165 tm_workspace_merge_tags(&theWorkspace
->tags_array
, source_file
->tags_array
);
167 merge_extracted_tags(&(theWorkspace
->typename_array
), source_file
->tags_array
, TM_GLOBAL_TYPE_MASK
);
171 g_message("Skipping workspace update because update_workspace is %s",
172 update_workspace
?"TRUE":"FALSE");
178 /** Adds a source file to the workspace, parses it and updates the workspace tags.
179 @param source_file The source file to add to the workspace.
182 void tm_workspace_add_source_file(TMSourceFile
*source_file
)
184 g_return_if_fail(source_file
!= NULL
);
186 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
187 update_source_file(source_file
, NULL
, 0, FALSE
, TRUE
);
191 void tm_workspace_add_source_file_noupdate(TMSourceFile
*source_file
)
193 g_return_if_fail(source_file
!= NULL
);
195 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
199 /* Updates the source file by reparsing the text-buffer passed as parameter.
200 Ctags will use a parsing based on buffer instead of on files.
201 You should call this function when you don't want a previous saving of the file
202 you're editing. It's useful for a "real-time" updating of the tags.
203 The tags array and the tags themselves are destroyed and re-created, hence any
204 other tag arrays pointing to these tags should be rebuilt as well. All sorting
205 information is also lost.
206 @param source_file The source file to update with a buffer.
207 @param text_buf A text buffer. The user should take care of allocate and free it after
209 @param buf_size The size of text_buf.
211 void tm_workspace_update_source_file_buffer(TMSourceFile
*source_file
, guchar
* text_buf
,
214 update_source_file(source_file
, text_buf
, buf_size
, TRUE
, TRUE
);
218 /** Removes a source file from the workspace if it exists. This function also removes
219 the tags belonging to this file from the workspace. To completely free the TMSourceFile
220 pointer call tm_source_file_free() on it.
221 @param source_file Pointer to the source file to be removed.
224 void tm_workspace_remove_source_file(TMSourceFile
*source_file
)
228 g_return_if_fail(source_file
!= NULL
);
230 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
232 if (theWorkspace
->source_files
->pdata
[i
] == source_file
)
234 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
235 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
236 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, i
);
243 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
244 want to globally refresh the workspace. This function does not call tm_source_file_update()
245 which should be called before this function on source files which need to be
248 static void tm_workspace_update(void)
251 TMSourceFile
*source_file
;
254 g_message("Recreating workspace tags array");
257 g_ptr_array_set_size(theWorkspace
->tags_array
, 0);
260 g_message("Total %d objects", theWorkspace
->source_files
->len
);
262 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
264 source_file
= theWorkspace
->source_files
->pdata
[i
];
266 g_message("Adding tags of %s", source_file
->file_name
);
268 if (source_file
->tags_array
->len
> 0)
270 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
272 g_ptr_array_add(theWorkspace
->tags_array
,
273 source_file
->tags_array
->pdata
[j
]);
278 g_message("Total: %d tags", theWorkspace
->tags_array
->len
);
280 tm_tags_sort(theWorkspace
->tags_array
, workspace_tags_sort_attrs
, TRUE
, FALSE
);
282 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
283 theWorkspace
->typename_array
= tm_tags_extract(theWorkspace
->tags_array
, TM_GLOBAL_TYPE_MASK
);
287 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
288 This is more efficient than calling tm_workspace_add_source_file() and
289 tm_workspace_update_source_file() separately for each of the files.
290 @param source_files @elementtype{TMSourceFile} The source files to be added to the workspace.
293 void tm_workspace_add_source_files(GPtrArray
*source_files
)
297 g_return_if_fail(source_files
!= NULL
);
299 for (i
= 0; i
< source_files
->len
; i
++)
301 TMSourceFile
*source_file
= source_files
->pdata
[i
];
303 tm_workspace_add_source_file_noupdate(source_file
);
304 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
307 tm_workspace_update();
311 /** Removes multiple source files from the workspace and updates the workspace tag
312 arrays. This is more efficient than calling tm_workspace_remove_source_file()
313 separately for each of the files. To completely free the TMSourceFile pointers
314 call tm_source_file_free() on each of them.
315 @param source_files @elementtype{TMSourceFile} The source files to be removed from the workspace.
318 void tm_workspace_remove_source_files(GPtrArray
*source_files
)
322 g_return_if_fail(source_files
!= NULL
);
324 //TODO: sort both arrays by pointer value and remove in single pass
325 for (i
= 0; i
< source_files
->len
; i
++)
327 TMSourceFile
*source_file
= source_files
->pdata
[i
];
329 for (j
= 0; j
< theWorkspace
->source_files
->len
; j
++)
331 if (theWorkspace
->source_files
->pdata
[j
] == source_file
)
333 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, j
);
339 tm_workspace_update();
343 /* Loads the global tag list from the specified file. The global tag list should
344 have been first created using tm_workspace_create_global_tags().
345 @param tags_file The file containing global tags.
346 @return TRUE on success, FALSE on failure.
347 @see tm_workspace_create_global_tags()
349 gboolean
tm_workspace_load_global_tags(const char *tags_file
, TMParserType mode
)
351 GPtrArray
*file_tags
, *new_tags
;
353 file_tags
= tm_source_file_read_tags_file(tags_file
, mode
);
357 tm_tags_sort(file_tags
, global_tags_sort_attrs
, TRUE
, TRUE
);
359 /* reorder the whole array, because tm_tags_find expects a sorted array */
360 new_tags
= tm_tags_merge(theWorkspace
->global_tags
,
361 file_tags
, global_tags_sort_attrs
, TRUE
);
362 g_ptr_array_free(theWorkspace
->global_tags
, TRUE
);
363 g_ptr_array_free(file_tags
, TRUE
);
364 theWorkspace
->global_tags
= new_tags
;
366 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
367 theWorkspace
->global_typename_array
= tm_tags_extract(new_tags
, TM_GLOBAL_TYPE_MASK
);
373 static guint
tm_file_inode_hash(gconstpointer key
)
376 const char *filename
= (const char*)key
;
378 if (g_stat(filename
, &file_stat
) == 0)
381 g_message ("Hash for '%s' is '%d'\n", filename
, file_stat
.st_ino
);
383 return g_direct_hash ((gpointer
)(intptr_t)file_stat
.st_ino
);
390 static void tm_move_entries_to_g_list(gpointer key
, gpointer value
, gpointer user_data
)
392 GList
**pp_list
= (GList
**)user_data
;
394 if (user_data
== NULL
)
397 *pp_list
= g_list_prepend(*pp_list
, g_strdup(value
));
401 static gboolean
write_includes_file(const gchar
*outf
, GList
*includes_files
)
403 FILE *fp
= g_fopen(outf
, "w");
404 GList
*node
= includes_files
;
411 char *str
= g_strdup_printf("#include \"%s\"\n", (char*)node
->data
);
412 size_t str_len
= strlen(str
);
414 fwrite(str
, str_len
, 1, fp
);
416 node
= g_list_next(node
);
419 return fclose(fp
) == 0;
423 static gboolean
combine_include_files(const gchar
*outf
, GList
*file_list
)
425 FILE *fp
= g_fopen(outf
, "w");
426 GList
*node
= file_list
;
433 const char *fname
= node
->data
;
438 if (! g_file_get_contents(fname
, &contents
, &length
, &err
))
440 fprintf(stderr
, "Unable to read file: %s\n", err
->message
);
445 fwrite(contents
, length
, 1, fp
);
446 fwrite("\n", 1, 1, fp
); /* in case file doesn't end in newline (e.g. windows). */
449 node
= g_list_next (node
);
452 return fclose(fp
) == 0;
456 static gchar
*create_temp_file(const gchar
*tpl
)
461 fd
= g_file_open_tmp(tpl
, &name
, NULL
);
470 static GList
*lookup_includes(const gchar
**includes
, gint includes_count
)
472 GList
*includes_files
= NULL
;
480 table
= g_hash_table_new_full(tm_file_inode_hash
, g_direct_equal
, NULL
, g_free
);
485 if (includes
[0][0] == '"') /* leading \" char for glob matching */
487 for (i
= 0; i
< includes_count
; i
++)
489 size_t dirty_len
= strlen(includes
[i
]);
495 clean_path
= g_malloc(dirty_len
- 1);
497 strncpy(clean_path
, includes
[i
] + 1, dirty_len
- 1);
498 clean_path
[dirty_len
- 2] = 0;
501 g_message ("[o][%s]\n", clean_path
);
503 glob(clean_path
, 0, NULL
, &globbuf
);
506 g_message ("matches: %d\n", globbuf
.gl_pathc
);
509 for (idx_glob
= 0; idx_glob
< globbuf
.gl_pathc
; idx_glob
++)
512 g_message (">>> %s\n", globbuf
.gl_pathv
[idx_glob
]);
514 if (!g_hash_table_lookup(table
, globbuf
.gl_pathv
[idx_glob
]))
516 gchar
*file_name_copy
= g_strdup(globbuf
.gl_pathv
[idx_glob
]);
518 g_hash_table_insert(table
, file_name_copy
, file_name_copy
);
520 g_message ("Added ...\n");
529 #endif /* HAVE_GLOB_H */
531 /* no glob support or globbing not wanted */
532 for (i
= 0; i
< includes_count
; i
++)
534 if (!g_hash_table_lookup(table
, includes
[i
]))
536 gchar
* file_name_copy
= g_strdup(includes
[i
]);
538 g_hash_table_insert(table
, file_name_copy
, file_name_copy
);
543 g_hash_table_foreach(table
, tm_move_entries_to_g_list
, &includes_files
);
544 g_hash_table_destroy(table
);
546 return includes_files
;
549 static gchar
*pre_process_file(const gchar
*cmd
, const gchar
*inf
)
552 gchar
*outf
= create_temp_file("tmp_XXXXXX.cpp");
554 gchar
*errors
= NULL
;
560 tmp_errfile
= create_temp_file("tmp_XXXXXX");
568 command
= g_strdup_printf("%s %s >%s 2>%s",
569 cmd
, inf
, outf
, tmp_errfile
);
571 g_message("Executing: %s", command
);
573 ret
= system(command
);
576 g_file_get_contents(tmp_errfile
, &errors
, NULL
, NULL
);
577 if (errors
&& *errors
)
578 g_printerr("%s", errors
);
580 g_unlink(tmp_errfile
);
593 /* Creates a list of global tags. Ideally, this should be created once during
594 installations so that all users can use the same file. This is because a full
595 scale global tag list can occupy several megabytes of disk space.
596 @param pre_process The pre-processing command. This is executed via system(),
597 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
598 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
600 @param tags_file The file where the tags will be stored.
601 @param lang The language to use for the tags file.
602 @return TRUE on success, FALSE on failure.
604 gboolean
tm_workspace_create_global_tags(const char *pre_process
, const char **includes
,
605 int includes_count
, const char *tags_file
, TMParserType lang
)
607 gboolean ret
= FALSE
;
608 TMSourceFile
*source_file
;
609 GList
*includes_files
;
610 gchar
*temp_file
= create_temp_file("tmp_XXXXXX.cpp");
615 includes_files
= lookup_includes(includes
, includes_count
);
618 g_message ("writing out files to %s\n", temp_file
);
621 ret
= write_includes_file(temp_file
, includes_files
);
623 ret
= combine_include_files(temp_file
, includes_files
);
625 g_list_free_full(includes_files
, g_free
);
632 gchar
*temp_file2
= pre_process_file(pre_process
, temp_file
);
638 temp_file
= temp_file2
;
644 source_file
= tm_source_file_new(temp_file
, tm_source_file_get_lang_name(lang
));
647 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
648 if (source_file
->tags_array
->len
== 0)
650 tm_source_file_free(source_file
);
654 tm_tags_sort(source_file
->tags_array
, global_tags_sort_attrs
, TRUE
, FALSE
);
655 ret
= tm_source_file_write_tags_file(tags_file
, source_file
->tags_array
);
656 tm_source_file_free(source_file
);
665 static void fill_find_tags_array(GPtrArray
*dst
, const GPtrArray
*src
,
666 const char *name
, const char *scope
, TMTagType type
, TMParserType lang
)
671 if (!src
|| !dst
|| !name
|| !*name
)
674 tag
= tm_tags_find(src
, name
, FALSE
, &num
);
675 for (i
= 0; i
< num
; ++i
)
677 if ((type
& (*tag
)->type
) &&
678 tm_tag_langs_compatible(lang
, (*tag
)->lang
) &&
679 (!scope
|| g_strcmp0((*tag
)->scope
, scope
) == 0))
681 g_ptr_array_add(dst
, *tag
);
688 /* Returns all matching tags found in the workspace.
689 @param name The name of the tag to find.
690 @param scope The scope name of the tag to find, or NULL.
691 @param type The tag types to return (TMTagType). Can be a bitmask.
692 @param attrs The attributes to sort and dedup on (0 terminated integer array).
693 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
695 @return Array of matching tags.
697 GPtrArray
*tm_workspace_find(const char *name
, const char *scope
, TMTagType type
,
698 TMTagAttrType
*attrs
, TMParserType lang
)
700 GPtrArray
*tags
= g_ptr_array_new();
702 fill_find_tags_array(tags
, theWorkspace
->tags_array
, name
, scope
, type
, lang
);
703 fill_find_tags_array(tags
, theWorkspace
->global_tags
, name
, scope
, type
, lang
);
706 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
712 static void fill_find_tags_array_prefix(GPtrArray
*dst
, const GPtrArray
*src
,
713 const char *name
, TMParserType lang
, guint max_num
)
715 TMTag
**tag
, *last
= NULL
;
718 if (!src
|| !dst
|| !name
|| !*name
)
722 tag
= tm_tags_find(src
, name
, TRUE
, &count
);
723 for (i
= 0; i
< count
&& num
< max_num
; ++i
)
725 if (tm_tag_langs_compatible(lang
, (*tag
)->lang
) &&
726 !tm_tag_is_anon(*tag
) &&
727 (!last
|| g_strcmp0(last
->name
, (*tag
)->name
) != 0))
729 g_ptr_array_add(dst
, *tag
);
738 /* Returns tags with the specified prefix sorted by name. If there are several
739 tags with the same name, only one of them appears in the resulting array.
740 @param prefix The prefix of the tag to find.
741 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
743 @param max_num The maximum number of tags to return.
744 @return Array of matching tags sorted by their name.
746 GPtrArray
*tm_workspace_find_prefix(const char *prefix
, TMParserType lang
, guint max_num
)
748 TMTagAttrType attrs
[] = { tm_tag_attr_name_t
, 0 };
749 GPtrArray
*tags
= g_ptr_array_new();
751 fill_find_tags_array_prefix(tags
, theWorkspace
->tags_array
, prefix
, lang
, max_num
);
752 fill_find_tags_array_prefix(tags
, theWorkspace
->global_tags
, prefix
, lang
, max_num
);
754 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
755 if (tags
->len
> max_num
)
762 /* Gets all members of type_tag; search them inside the all array.
763 * The namespace parameter determines whether we are performing the "namespace"
764 * search (user has typed something like "A::" where A is a type) or "scope" search
765 * (user has typed "a." where a is a global struct-like variable). With the
766 * namespace search we return all direct descendants of any type while with the
767 * scope search we return only those which can be invoked on a variable (member,
770 find_scope_members_tags (const GPtrArray
*all
, TMTag
*type_tag
, gboolean
namespace)
772 TMTagType member_types
= tm_tag_max_t
& ~(TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
);
773 GPtrArray
*tags
= g_ptr_array_new();
778 member_types
= tm_tag_max_t
;
780 if (type_tag
->scope
&& *(type_tag
->scope
))
781 scope
= g_strconcat(type_tag
->scope
, tm_tag_context_separator(type_tag
->lang
), type_tag
->name
, NULL
);
783 scope
= g_strdup(type_tag
->name
);
785 for (i
= 0; i
< all
->len
; ++i
)
787 TMTag
*tag
= TM_TAG (all
->pdata
[i
]);
789 if (tag
&& (tag
->type
& member_types
) &&
790 tag
->scope
&& tag
->scope
[0] != '\0' &&
791 tm_tag_langs_compatible(tag
->lang
, type_tag
->lang
) &&
792 strcmp(scope
, tag
->scope
) == 0 &&
793 (!namespace || !tm_tag_is_anon(tag
)))
795 g_ptr_array_add (tags
, tag
);
803 g_ptr_array_free(tags
, TRUE
);
811 static gchar
*strip_type(const gchar
*scoped_name
, TMParserType lang
)
813 if (scoped_name
!= NULL
)
815 /* remove scope prefix */
816 const gchar
*sep
= tm_tag_context_separator(lang
);
817 const gchar
*base
= g_strrstr(scoped_name
, sep
);
818 gchar
*name
= base
? g_strdup(base
+ strlen(sep
)) : g_strdup(scoped_name
);
820 /* remove pointers */
821 g_strdelimit(name
, "*^", ' ');
830 /* Gets all members of the type with the given name; search them inside tags_array */
832 find_scope_members (const GPtrArray
*tags_array
, const gchar
*name
, TMSourceFile
*file
,
833 TMParserType lang
, gboolean
namespace)
835 GPtrArray
*res
= NULL
;
839 g_return_val_if_fail(name
&& *name
, NULL
);
841 type_name
= g_strdup(name
);
843 /* Check if type_name is a type that can possibly contain members.
844 * Try to resolve intermediate typedefs to get the real type name. Also
845 * add scope information to the name if applicable.
846 * The loop below loops only when resolving typedefs - avoid possibly infinite
847 * loop when typedefs create a cycle by adding some limits. */
848 for (i
= 0; i
< 5; i
++)
851 GPtrArray
*type_tags
;
852 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
856 types
&= ~tm_tag_enum_t
;
858 type_tags
= g_ptr_array_new();
859 fill_find_tags_array(type_tags
, tags_array
, type_name
, NULL
, types
, lang
);
861 for (j
= 0; j
< type_tags
->len
; j
++)
863 TMTag
*test_tag
= TM_TAG(type_tags
->pdata
[j
]);
865 /* anonymous type defined in a different file than the variable -
866 * this isn't the type we are looking for */
867 if (tm_tag_is_anon(test_tag
) && (file
!= test_tag
->file
|| test_tag
->file
== NULL
))
872 /* prefer non-typedef tags because we can be sure they contain members */
873 if (test_tag
->type
!= tm_tag_typedef_t
)
877 g_ptr_array_free(type_tags
, TRUE
);
879 if (!tag
) /* not a type that can contain members */
882 /* intermediate typedef - resolve to the real type */
883 if (tag
->type
== tm_tag_typedef_t
)
885 if (tag
->var_type
&& tag
->var_type
[0] != '\0')
888 type_name
= strip_type(tag
->var_type
, tag
->lang
);
894 else /* real type with members */
896 /* use the same file as the composite type if file information available */
897 res
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: tags_array
, tag
, namespace);
908 /* Checks whether a member tag is directly accessible from method with method_scope */
909 static gboolean
member_at_method_scope(const GPtrArray
*tags
, const gchar
*method_scope
, TMTag
*member_tag
,
912 const gchar
*sep
= tm_tag_context_separator(lang
);
913 gboolean ret
= FALSE
;
917 /* method scope is in the form ...::class_name::method_name */
918 comps
= g_strsplit (method_scope
, sep
, 0);
919 len
= g_strv_length(comps
);
922 gchar
*method
, *member_scope
, *cls
, *cls_scope
;
924 /* get method/member scope */
925 method
= comps
[len
- 1];
926 comps
[len
- 1] = NULL
;
927 member_scope
= g_strjoinv(sep
, comps
);
928 comps
[len
- 1] = method
;
930 /* get class scope */
931 cls
= comps
[len
- 2];
932 comps
[len
- 2] = NULL
;
933 cls_scope
= g_strjoinv(sep
, comps
);
934 comps
[len
- 2] = cls
;
935 cls_scope
= strlen(cls_scope
) > 0 ? cls_scope
: NULL
;
937 /* check whether member inside the class */
938 if (g_strcmp0(member_tag
->scope
, member_scope
) == 0)
940 const GPtrArray
*src
= member_tag
->file
? member_tag
->file
->tags_array
: tags
;
941 GPtrArray
*cls_tags
= g_ptr_array_new();
943 /* check whether the class exists */
944 fill_find_tags_array(cls_tags
, src
, cls
, cls_scope
, TM_TYPE_WITH_MEMBERS
| tm_tag_namespace_t
, lang
);
945 ret
= cls_tags
->len
> 0;
946 g_ptr_array_free(cls_tags
, TRUE
);
950 g_free(member_scope
);
958 /* For an array of variable/type tags, find members inside the types */
960 find_scope_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, TMParserType lang
,
961 gboolean member
, const gchar
*current_scope
)
963 GPtrArray
*member_tags
= NULL
;
966 /* there may be several variables/types with the same name - try each of them until
967 * we find something */
968 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
970 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
971 TMTagType member_types
= tm_tag_member_t
| tm_tag_field_t
| tm_tag_method_t
;
972 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
974 if (tag
->type
& types
) /* type: namespace search */
976 if (tag
->type
& tm_tag_typedef_t
)
977 member_tags
= find_scope_members(searched_array
, tag
->name
, tag
->file
, lang
, TRUE
);
979 member_tags
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: searched_array
,
982 else if (tag
->var_type
) /* variable: scope search */
984 /* The question now is whether we should use member tags (such as
985 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
986 * (which means user has typed something like foo.bar.) or if we are
987 * inside a method where foo is a class member, we want scope completion
989 if (!(tag
->type
& member_types
) || member
||
990 member_at_method_scope(tags
, current_scope
, tag
, lang
))
992 gchar
*tag_type
= strip_type(tag
->var_type
, tag
->lang
);
994 member_tags
= find_scope_members(searched_array
, tag_type
, tag
->file
, lang
, FALSE
);
1004 static GPtrArray
*find_namespace_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, TMParserType lang
)
1006 GPtrArray
*member_tags
= NULL
;
1009 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
1011 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
1013 member_tags
= find_scope_members_tags(searched_array
, tag
, TRUE
);
1020 /* Returns all member tags of a struct/union/class if the provided name is a variable
1021 of such a type or the name of the type.
1022 @param source_file TMSourceFile of the edited source file
1023 @param name Name of the variable/type whose members are searched
1024 @param function TRUE if the name is a name of a function
1025 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1026 @param current_scope The current scope in the editor
1027 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1028 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1030 tm_workspace_find_scope_members (TMSourceFile
*source_file
, const char *name
,
1031 gboolean function
, gboolean member
, const gchar
*current_scope
, gboolean search_namespace
)
1033 TMParserType lang
= source_file
? source_file
->lang
: TM_PARSER_NONE
;
1034 GPtrArray
*tags
, *member_tags
= NULL
;
1035 TMTagType function_types
= tm_tag_function_t
| tm_tag_method_t
|
1036 tm_tag_macro_with_arg_t
| tm_tag_prototype_t
;
1037 TMTagType tag_type
= tm_tag_max_t
&
1038 ~(function_types
| tm_tag_enumerator_t
| tm_tag_namespace_t
| tm_tag_package_t
);
1039 TMTagAttrType sort_attr
[] = {tm_tag_attr_name_t
, 0};
1041 if (search_namespace
)
1043 tags
= tm_workspace_find(name
, NULL
, tm_tag_namespace_t
, NULL
, lang
);
1045 member_tags
= find_namespace_members_all(tags
, theWorkspace
->tags_array
, lang
);
1047 member_tags
= find_namespace_members_all(tags
, theWorkspace
->global_tags
, lang
);
1049 g_ptr_array_free(tags
, TRUE
);
1055 tag_type
= function_types
;
1057 /* tags corresponding to the variable/type name */
1058 tags
= tm_workspace_find(name
, NULL
, tag_type
, NULL
, lang
);
1060 /* Start searching inside the source file, continue with workspace tags and
1061 * end with global tags. This way we find the "closest" tag to the current
1062 * file in case there are more of them. */
1064 member_tags
= find_scope_members_all(tags
, source_file
->tags_array
,
1065 lang
, member
, current_scope
);
1067 member_tags
= find_scope_members_all(tags
, theWorkspace
->tags_array
, lang
,
1068 member
, current_scope
);
1070 member_tags
= find_scope_members_all(tags
, theWorkspace
->global_tags
, lang
,
1071 member
, current_scope
);
1073 g_ptr_array_free(tags
, TRUE
);
1076 tm_tags_dedup(member_tags
, sort_attr
, FALSE
);
1084 /* Dumps the workspace tree - useful for debugging */
1085 void tm_workspace_dump(void)
1090 g_message("Dumping TagManager workspace tree..");
1092 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
1094 TMSourceFile
*source_file
= theWorkspace
->source_files
->pdata
[i
];
1095 fprintf(stderr
, "%s", source_file
->file_name
);
1098 #endif /* TM_DEBUG */
1103 /* Returns a list of parent classes for the given class name
1104 @param name Name of the class
1105 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1106 static const GPtrArray
*tm_workspace_get_parents(const gchar
*name
)
1108 static TMTagAttrType type
[] = { tm_tag_attr_name_t
, tm_tag_attr_none_t
};
1109 static GPtrArray
*parents
= NULL
;
1110 const GPtrArray
*matches
;
1117 g_return_val_if_fail(name
&& isalpha(*name
),NULL
);
1119 if (NULL
== parents
)
1120 parents
= g_ptr_array_new();
1122 g_ptr_array_set_size(parents
, 0);
1123 matches
= tm_workspace_find(name
, NULL
, tm_tag_class_t
, type
, -1);
1124 if ((NULL
== matches
) || (0 == matches
->len
))
1126 g_ptr_array_add(parents
, matches
->pdata
[0]);
1127 while (i
< parents
->len
)
1129 tag
= TM_TAG(parents
->pdata
[i
]);
1130 if ((NULL
!= tag
->inheritance
) && (isalpha(tag
->inheritance
[0])))
1132 klasses
= g_strsplit(tag
->inheritance
, ",", 10);
1133 for (klass
= klasses
; (NULL
!= *klass
); ++ klass
)
1135 for (j
=0; j
< parents
->len
; ++j
)
1137 if (0 == strcmp(*klass
, TM_TAG(parents
->pdata
[j
])->name
))
1140 if (parents
->len
== j
)
1142 matches
= tm_workspace_find(*klass
, NULL
, tm_tag_class_t
, type
, -1);
1143 if ((NULL
!= matches
) && (0 < matches
->len
))
1144 g_ptr_array_add(parents
, matches
->pdata
[0]);
1147 g_strfreev(klasses
);