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"
35 #include "tm_parser.h"
38 /* when changing, always keep the three sort criteria below in sync */
39 static TMTagAttrType workspace_tags_sort_attrs
[] =
41 tm_tag_attr_name_t
, tm_tag_attr_file_t
, tm_tag_attr_line_t
,
42 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
45 /* for file tags the file is always identical, don't use for sorting */
46 static TMTagAttrType file_tags_sort_attrs
[] =
48 tm_tag_attr_name_t
, tm_tag_attr_line_t
,
49 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
52 /* global tags don't have file/line information */
53 static TMTagAttrType global_tags_sort_attrs
[] =
56 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
59 static TMTagType TM_TYPE_WITH_MEMBERS
=
60 tm_tag_class_t
| tm_tag_struct_t
| tm_tag_union_t
|
61 tm_tag_enum_t
| tm_tag_interface_t
;
63 static TMTagType TM_GLOBAL_TYPE_MASK
=
64 tm_tag_class_t
| tm_tag_enum_t
| tm_tag_interface_t
|
65 tm_tag_struct_t
| tm_tag_typedef_t
| tm_tag_union_t
| tm_tag_namespace_t
;
67 static TMWorkspace
*theWorkspace
= NULL
;
70 static gboolean
tm_create_workspace(void)
72 theWorkspace
= g_new(TMWorkspace
, 1);
73 theWorkspace
->tags_array
= g_ptr_array_new();
75 theWorkspace
->global_tags
= g_ptr_array_new();
76 theWorkspace
->source_files
= g_ptr_array_new();
77 theWorkspace
->typename_array
= g_ptr_array_new();
78 theWorkspace
->global_typename_array
= g_ptr_array_new();
80 tm_source_file_ctags_init();
86 /* Frees the workspace structure and all child source files. Use only when
87 exiting from the main program.
89 void tm_workspace_free(void)
94 g_message("Workspace destroyed");
97 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
98 tm_source_file_free(theWorkspace
->source_files
->pdata
[i
]);
99 g_ptr_array_free(theWorkspace
->source_files
, TRUE
);
100 tm_tags_array_free(theWorkspace
->global_tags
, TRUE
);
101 g_ptr_array_free(theWorkspace
->tags_array
, TRUE
);
102 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
103 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
104 g_free(theWorkspace
);
109 /* Since TMWorkspace is a singleton, you should not create multiple
110 workspaces, but get a pointer to the workspace whenever required. The first
111 time a pointer is requested, or a source file is added to the workspace,
112 a workspace is created. Subsequent calls to the function will return the
115 const TMWorkspace
*tm_get_workspace(void)
117 if (NULL
== theWorkspace
)
118 tm_create_workspace();
123 static void tm_workspace_merge_tags(GPtrArray
**big_array
, GPtrArray
*small_array
)
125 GPtrArray
*new_tags
= tm_tags_merge(*big_array
, small_array
, workspace_tags_sort_attrs
, FALSE
);
126 /* tags owned by TMSourceFile - free just the pointer array */
127 g_ptr_array_free(*big_array
, TRUE
);
128 *big_array
= new_tags
;
132 static void merge_extracted_tags(GPtrArray
**dest
, GPtrArray
*src
, TMTagType tag_types
)
136 arr
= tm_tags_extract(src
, tag_types
);
137 tm_workspace_merge_tags(dest
, arr
);
138 g_ptr_array_free(arr
, TRUE
);
142 static void update_source_file(TMSourceFile
*source_file
, guchar
* text_buf
,
143 gsize buf_size
, gboolean use_buffer
, gboolean update_workspace
)
146 g_message("Source file updating based on source file %s", source_file
->file_name
);
149 if (update_workspace
)
151 /* tm_source_file_parse() deletes the tag objects - remove the tags from
152 * workspace while they exist and can be scanned */
153 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
154 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
156 tm_source_file_parse(source_file
, text_buf
, buf_size
, use_buffer
);
157 tm_tags_sort(source_file
->tags_array
, file_tags_sort_attrs
, FALSE
, TRUE
);
158 if (update_workspace
)
161 g_message("Updating workspace from source file");
163 tm_workspace_merge_tags(&theWorkspace
->tags_array
, source_file
->tags_array
);
165 merge_extracted_tags(&(theWorkspace
->typename_array
), source_file
->tags_array
, TM_GLOBAL_TYPE_MASK
);
169 g_message("Skipping workspace update because update_workspace is %s",
170 update_workspace
?"TRUE":"FALSE");
176 /** Adds a source file to the workspace, parses it and updates the workspace tags.
177 @param source_file The source file to add to the workspace.
180 void tm_workspace_add_source_file(TMSourceFile
*source_file
)
182 g_return_if_fail(source_file
!= NULL
);
184 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
185 update_source_file(source_file
, NULL
, 0, FALSE
, TRUE
);
189 void tm_workspace_add_source_file_noupdate(TMSourceFile
*source_file
)
191 g_return_if_fail(source_file
!= NULL
);
193 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
197 /* Updates the source file by reparsing the text-buffer passed as parameter.
198 Ctags will use a parsing based on buffer instead of on files.
199 You should call this function when you don't want a previous saving of the file
200 you're editing. It's useful for a "real-time" updating of the tags.
201 The tags array and the tags themselves are destroyed and re-created, hence any
202 other tag arrays pointing to these tags should be rebuilt as well. All sorting
203 information is also lost.
204 @param source_file The source file to update with a buffer.
205 @param text_buf A text buffer. The user should take care of allocate and free it after
207 @param buf_size The size of text_buf.
209 void tm_workspace_update_source_file_buffer(TMSourceFile
*source_file
, guchar
* text_buf
,
212 update_source_file(source_file
, text_buf
, buf_size
, TRUE
, TRUE
);
216 /** Removes a source file from the workspace if it exists. This function also removes
217 the tags belonging to this file from the workspace. To completely free the TMSourceFile
218 pointer call tm_source_file_free() on it.
219 @param source_file Pointer to the source file to be removed.
222 void tm_workspace_remove_source_file(TMSourceFile
*source_file
)
226 g_return_if_fail(source_file
!= NULL
);
228 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
230 if (theWorkspace
->source_files
->pdata
[i
] == source_file
)
232 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
233 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
234 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, i
);
241 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
242 want to globally refresh the workspace. This function does not call tm_source_file_update()
243 which should be called before this function on source files which need to be
246 static void tm_workspace_update(void)
249 TMSourceFile
*source_file
;
252 g_message("Recreating workspace tags array");
255 g_ptr_array_set_size(theWorkspace
->tags_array
, 0);
258 g_message("Total %d objects", theWorkspace
->source_files
->len
);
260 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
262 source_file
= theWorkspace
->source_files
->pdata
[i
];
264 g_message("Adding tags of %s", source_file
->file_name
);
266 if (source_file
->tags_array
->len
> 0)
268 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
270 g_ptr_array_add(theWorkspace
->tags_array
,
271 source_file
->tags_array
->pdata
[j
]);
276 g_message("Total: %d tags", theWorkspace
->tags_array
->len
);
278 tm_tags_sort(theWorkspace
->tags_array
, workspace_tags_sort_attrs
, TRUE
, FALSE
);
280 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
281 theWorkspace
->typename_array
= tm_tags_extract(theWorkspace
->tags_array
, TM_GLOBAL_TYPE_MASK
);
285 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
286 This is more efficient than calling tm_workspace_add_source_file() and
287 tm_workspace_update_source_file() separately for each of the files.
288 @param source_files @elementtype{TMSourceFile} The source files to be added to the workspace.
291 void tm_workspace_add_source_files(GPtrArray
*source_files
)
295 g_return_if_fail(source_files
!= NULL
);
297 for (i
= 0; i
< source_files
->len
; i
++)
299 TMSourceFile
*source_file
= source_files
->pdata
[i
];
301 tm_workspace_add_source_file_noupdate(source_file
);
302 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
305 tm_workspace_update();
309 /** Removes multiple source files from the workspace and updates the workspace tag
310 arrays. This is more efficient than calling tm_workspace_remove_source_file()
311 separately for each of the files. To completely free the TMSourceFile pointers
312 call tm_source_file_free() on each of them.
313 @param source_files @elementtype{TMSourceFile} The source files to be removed from the workspace.
316 void tm_workspace_remove_source_files(GPtrArray
*source_files
)
320 g_return_if_fail(source_files
!= NULL
);
322 //TODO: sort both arrays by pointer value and remove in single pass
323 for (i
= 0; i
< source_files
->len
; i
++)
325 TMSourceFile
*source_file
= source_files
->pdata
[i
];
327 for (j
= 0; j
< theWorkspace
->source_files
->len
; j
++)
329 if (theWorkspace
->source_files
->pdata
[j
] == source_file
)
331 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, j
);
337 tm_workspace_update();
341 /* Loads the global tag list from the specified file. The global tag list should
342 have been first created using tm_workspace_create_global_tags().
343 @param tags_file The file containing global tags.
344 @return TRUE on success, FALSE on failure.
345 @see tm_workspace_create_global_tags()
347 gboolean
tm_workspace_load_global_tags(const char *tags_file
, TMParserType mode
)
349 GPtrArray
*file_tags
, *new_tags
;
351 file_tags
= tm_source_file_read_tags_file(tags_file
, mode
);
355 tm_tags_sort(file_tags
, global_tags_sort_attrs
, TRUE
, TRUE
);
357 /* reorder the whole array, because tm_tags_find expects a sorted array */
358 new_tags
= tm_tags_merge(theWorkspace
->global_tags
,
359 file_tags
, global_tags_sort_attrs
, TRUE
);
360 g_ptr_array_free(theWorkspace
->global_tags
, TRUE
);
361 g_ptr_array_free(file_tags
, TRUE
);
362 theWorkspace
->global_tags
= new_tags
;
364 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
365 theWorkspace
->global_typename_array
= tm_tags_extract(new_tags
, TM_GLOBAL_TYPE_MASK
);
371 static guint
tm_file_inode_hash(gconstpointer key
)
374 const char *filename
= (const char*)key
;
375 if (g_stat(filename
, &file_stat
) == 0)
378 g_message ("Hash for '%s' is '%d'\n", filename
, file_stat
.st_ino
);
380 return g_direct_hash ((gpointer
)(intptr_t)file_stat
.st_ino
);
387 static void tm_move_entries_to_g_list(gpointer key
, gpointer value
, gpointer user_data
)
389 GList
**pp_list
= (GList
**)user_data
;
391 if (user_data
== NULL
)
394 *pp_list
= g_list_prepend(*pp_list
, value
);
398 static void write_includes_file(FILE *fp
, GList
*includes_files
)
402 node
= includes_files
;
405 char *str
= g_strdup_printf("#include \"%s\"\n", (char*)node
->data
);
406 size_t str_len
= strlen(str
);
408 fwrite(str
, str_len
, 1, fp
);
410 node
= g_list_next(node
);
415 static void append_to_temp_file(FILE *fp
, GList
*file_list
)
422 const char *fname
= node
->data
;
427 if (! g_file_get_contents(fname
, &contents
, &length
, &err
))
429 fprintf(stderr
, "Unable to read file: %s\n", err
->message
);
434 fwrite(contents
, length
, 1, fp
);
435 fwrite("\n", 1, 1, fp
); /* in case file doesn't end in newline (e.g. windows). */
438 node
= g_list_next (node
);
443 static gchar
*create_temp_file(const gchar
*tpl
)
448 fd
= g_file_open_tmp(tpl
, &name
, NULL
);
458 /* Creates a list of global tags. Ideally, this should be created once during
459 installations so that all users can use the same file. This is because a full
460 scale global tag list can occupy several megabytes of disk space.
461 @param pre_process The pre-processing command. This is executed via system(),
462 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
463 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
465 @param tags_file The file where the tags will be stored.
466 @param lang The language to use for the tags file.
467 @return TRUE on success, FALSE on failure.
469 gboolean
tm_workspace_create_global_tags(const char *pre_process
, const char **includes
,
470 int includes_count
, const char *tags_file
, TMParserType lang
)
480 TMSourceFile
*source_file
;
481 GPtrArray
*tags_array
;
482 GHashTable
*includes_files_hash
;
483 GList
*includes_files
= NULL
;
484 gchar
*temp_file
= create_temp_file("tmp_XXXXXX.cpp");
485 gchar
*temp_file2
= create_temp_file("tmp_XXXXXX.cpp");
488 if (NULL
== temp_file
|| NULL
== temp_file2
||
489 NULL
== (fp
= g_fopen(temp_file
, "w")))
496 includes_files_hash
= g_hash_table_new_full (tm_file_inode_hash
,
503 if (includes
[0][0] == '"') /* leading \" char for glob matching */
504 for(idx_inc
= 0; idx_inc
< includes_count
; idx_inc
++)
506 size_t dirty_len
= strlen(includes
[idx_inc
]);
507 char *clean_path
= g_malloc(dirty_len
- 1);
509 strncpy(clean_path
, includes
[idx_inc
] + 1, dirty_len
- 1);
510 clean_path
[dirty_len
- 2] = 0;
513 g_message ("[o][%s]\n", clean_path
);
515 glob(clean_path
, 0, NULL
, &globbuf
);
518 g_message ("matches: %d\n", globbuf
.gl_pathc
);
521 for(idx_glob
= 0; idx_glob
< globbuf
.gl_pathc
; idx_glob
++)
524 g_message (">>> %s\n", globbuf
.gl_pathv
[idx_glob
]);
526 if (!g_hash_table_lookup(includes_files_hash
,
527 globbuf
.gl_pathv
[idx_glob
]))
529 char* file_name_copy
= strdup(globbuf
.gl_pathv
[idx_glob
]);
530 g_hash_table_insert(includes_files_hash
, file_name_copy
,
533 g_message ("Added ...\n");
542 /* no glob support or globbing not wanted */
543 for(idx_inc
= 0; idx_inc
< includes_count
; idx_inc
++)
545 if (!g_hash_table_lookup(includes_files_hash
,
548 char* file_name_copy
= strdup(includes
[idx_inc
]);
549 g_hash_table_insert(includes_files_hash
, file_name_copy
,
554 /* Checks for duplicate file entries which would case trouble */
555 g_hash_table_foreach(includes_files_hash
, tm_move_entries_to_g_list
,
558 includes_files
= g_list_reverse (includes_files
);
561 g_message ("writing out files to %s\n", temp_file
);
563 if (pre_process
!= NULL
)
564 write_includes_file(fp
, includes_files
);
566 append_to_temp_file(fp
, includes_files
);
568 g_list_free (includes_files
);
569 g_hash_table_destroy(includes_files_hash
);
570 includes_files_hash
= NULL
;
571 includes_files
= NULL
;
574 if (pre_process
!= NULL
)
577 gchar
*tmp_errfile
= create_temp_file("tmp_XXXXXX");
578 gchar
*errors
= NULL
;
579 command
= g_strdup_printf("%s %s >%s 2>%s",
580 pre_process
, temp_file
, temp_file2
, tmp_errfile
);
582 g_message("Executing: %s", command
);
584 ret
= system(command
);
588 g_file_get_contents(tmp_errfile
, &errors
, NULL
, NULL
);
589 if (errors
&& *errors
)
590 g_printerr("%s", errors
);
592 g_unlink(tmp_errfile
);
596 g_unlink(temp_file2
);
602 /* no pre-processing needed, so temp_file2 = temp_file */
603 g_unlink(temp_file2
);
605 temp_file2
= temp_file
;
608 source_file
= tm_source_file_new(temp_file2
, tm_source_file_get_lang_name(lang
));
609 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
610 if (NULL
== source_file
)
612 g_unlink(temp_file2
);
615 g_unlink(temp_file2
);
617 if (0 == source_file
->tags_array
->len
)
619 tm_source_file_free(source_file
);
622 tags_array
= tm_tags_extract(source_file
->tags_array
, tm_tag_max_t
);
623 if ((NULL
== tags_array
) || (0 == tags_array
->len
))
626 g_ptr_array_free(tags_array
, TRUE
);
627 tm_source_file_free(source_file
);
630 if (FALSE
== tm_tags_sort(tags_array
, global_tags_sort_attrs
, TRUE
, FALSE
))
632 tm_source_file_free(source_file
);
635 ret
= tm_source_file_write_tags_file(tags_file
, tags_array
);
636 tm_source_file_free(source_file
);
637 g_ptr_array_free(tags_array
, TRUE
);
642 static void fill_find_tags_array(GPtrArray
*dst
, const GPtrArray
*src
,
643 const char *name
, const char *scope
, TMTagType type
, TMParserType lang
)
648 if (!src
|| !dst
|| !name
|| !*name
)
651 tag
= tm_tags_find(src
, name
, FALSE
, &num
);
652 for (i
= 0; i
< num
; ++i
)
654 if ((type
& (*tag
)->type
) &&
655 tm_tag_langs_compatible(lang
, (*tag
)->lang
) &&
656 (!scope
|| g_strcmp0((*tag
)->scope
, scope
) == 0))
658 g_ptr_array_add(dst
, *tag
);
665 /* Returns all matching tags found in the workspace.
666 @param name The name of the tag to find.
667 @param scope The scope name of the tag to find, or NULL.
668 @param type The tag types to return (TMTagType). Can be a bitmask.
669 @param attrs The attributes to sort and dedup on (0 terminated integer array).
670 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
672 @return Array of matching tags.
674 GPtrArray
*tm_workspace_find(const char *name
, const char *scope
, TMTagType type
,
675 TMTagAttrType
*attrs
, TMParserType lang
)
677 GPtrArray
*tags
= g_ptr_array_new();
679 fill_find_tags_array(tags
, theWorkspace
->tags_array
, name
, scope
, type
, lang
);
680 fill_find_tags_array(tags
, theWorkspace
->global_tags
, name
, scope
, type
, lang
);
683 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
689 static void fill_find_tags_array_prefix(GPtrArray
*dst
, const GPtrArray
*src
,
690 const char *name
, TMParserType lang
, guint max_num
)
692 TMTag
**tag
, *last
= NULL
;
695 if (!src
|| !dst
|| !name
|| !*name
)
699 tag
= tm_tags_find(src
, name
, TRUE
, &count
);
700 for (i
= 0; i
< count
&& num
< max_num
; ++i
)
702 if (tm_tag_langs_compatible(lang
, (*tag
)->lang
) &&
703 !tm_tag_is_anon(*tag
) &&
704 (!last
|| g_strcmp0(last
->name
, (*tag
)->name
) != 0))
706 g_ptr_array_add(dst
, *tag
);
715 /* Returns tags with the specified prefix sorted by name. If there are several
716 tags with the same name, only one of them appears in the resulting array.
717 @param prefix The prefix of the tag to find.
718 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
720 @param max_num The maximum number of tags to return.
721 @return Array of matching tags sorted by their name.
723 GPtrArray
*tm_workspace_find_prefix(const char *prefix
, TMParserType lang
, guint max_num
)
725 TMTagAttrType attrs
[] = { tm_tag_attr_name_t
, 0 };
726 GPtrArray
*tags
= g_ptr_array_new();
728 fill_find_tags_array_prefix(tags
, theWorkspace
->tags_array
, prefix
, lang
, max_num
);
729 fill_find_tags_array_prefix(tags
, theWorkspace
->global_tags
, prefix
, lang
, max_num
);
731 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
732 if (tags
->len
> max_num
)
739 /* Gets all members of type_tag; search them inside the all array.
740 * The namespace parameter determines whether we are performing the "namespace"
741 * search (user has typed something like "A::" where A is a type) or "scope" search
742 * (user has typed "a." where a is a global struct-like variable). With the
743 * namespace search we return all direct descendants of any type while with the
744 * scope search we return only those which can be invoked on a variable (member,
747 find_scope_members_tags (const GPtrArray
*all
, TMTag
*type_tag
, gboolean
namespace)
749 TMTagType member_types
= tm_tag_max_t
& ~(TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
);
750 GPtrArray
*tags
= g_ptr_array_new();
755 member_types
= tm_tag_max_t
;
757 if (type_tag
->scope
&& *(type_tag
->scope
))
758 scope
= g_strconcat(type_tag
->scope
, tm_tag_context_separator(type_tag
->lang
), type_tag
->name
, NULL
);
760 scope
= g_strdup(type_tag
->name
);
762 for (i
= 0; i
< all
->len
; ++i
)
764 TMTag
*tag
= TM_TAG (all
->pdata
[i
]);
766 if (tag
&& (tag
->type
& member_types
) &&
767 tag
->scope
&& tag
->scope
[0] != '\0' &&
768 tm_tag_langs_compatible(tag
->lang
, type_tag
->lang
) &&
769 strcmp(scope
, tag
->scope
) == 0 &&
770 (!namespace || !tm_tag_is_anon(tag
)))
772 g_ptr_array_add (tags
, tag
);
780 g_ptr_array_free(tags
, TRUE
);
788 static gchar
*strip_type(const gchar
*scoped_name
, TMParserType lang
)
790 if (scoped_name
!= NULL
)
792 /* remove scope prefix */
793 const gchar
*sep
= tm_tag_context_separator(lang
);
794 const gchar
*base
= g_strrstr(scoped_name
, sep
);
795 gchar
*name
= base
? g_strdup(base
+ strlen(sep
)) : g_strdup(scoped_name
);
797 /* remove pointers */
798 g_strdelimit(name
, "*^", ' ');
807 /* Gets all members of the type with the given name; search them inside tags_array */
809 find_scope_members (const GPtrArray
*tags_array
, const gchar
*name
, TMSourceFile
*file
,
810 TMParserType lang
, gboolean
namespace)
812 GPtrArray
*res
= NULL
;
816 g_return_val_if_fail(name
&& *name
, NULL
);
818 type_name
= g_strdup(name
);
820 /* Check if type_name is a type that can possibly contain members.
821 * Try to resolve intermediate typedefs to get the real type name. Also
822 * add scope information to the name if applicable.
823 * The loop below loops only when resolving typedefs - avoid possibly infinite
824 * loop when typedefs create a cycle by adding some limits. */
825 for (i
= 0; i
< 5; i
++)
828 GPtrArray
*type_tags
;
829 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
833 types
&= ~tm_tag_enum_t
;
835 type_tags
= g_ptr_array_new();
836 fill_find_tags_array(type_tags
, tags_array
, type_name
, NULL
, types
, lang
);
838 for (j
= 0; j
< type_tags
->len
; j
++)
840 TMTag
*test_tag
= TM_TAG(type_tags
->pdata
[j
]);
842 /* anonymous type defined in a different file than the variable -
843 * this isn't the type we are looking for */
844 if (tm_tag_is_anon(test_tag
) && (file
!= test_tag
->file
|| test_tag
->file
== NULL
))
849 /* prefer non-typedef tags because we can be sure they contain members */
850 if (test_tag
->type
!= tm_tag_typedef_t
)
854 g_ptr_array_free(type_tags
, TRUE
);
856 if (!tag
) /* not a type that can contain members */
859 /* intermediate typedef - resolve to the real type */
860 if (tag
->type
== tm_tag_typedef_t
)
862 if (tag
->var_type
&& tag
->var_type
[0] != '\0')
865 type_name
= strip_type(tag
->var_type
, tag
->lang
);
871 else /* real type with members */
873 /* use the same file as the composite type if file information available */
874 res
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: tags_array
, tag
, namespace);
885 /* Checks whether a member tag is directly accessible from method with method_scope */
886 static gboolean
member_at_method_scope(const GPtrArray
*tags
, const gchar
*method_scope
, TMTag
*member_tag
,
889 const gchar
*sep
= tm_tag_context_separator(lang
);
890 gboolean ret
= FALSE
;
894 /* method scope is in the form ...::class_name::method_name */
895 comps
= g_strsplit (method_scope
, sep
, 0);
896 len
= g_strv_length(comps
);
899 gchar
*method
, *member_scope
, *cls
, *cls_scope
;
901 /* get method/member scope */
902 method
= comps
[len
- 1];
903 comps
[len
- 1] = NULL
;
904 member_scope
= g_strjoinv(sep
, comps
);
905 comps
[len
- 1] = method
;
907 /* get class scope */
908 cls
= comps
[len
- 2];
909 comps
[len
- 2] = NULL
;
910 cls_scope
= g_strjoinv(sep
, comps
);
911 comps
[len
- 2] = cls
;
912 cls_scope
= strlen(cls_scope
) > 0 ? cls_scope
: NULL
;
914 /* check whether member inside the class */
915 if (g_strcmp0(member_tag
->scope
, member_scope
) == 0)
917 const GPtrArray
*src
= member_tag
->file
? member_tag
->file
->tags_array
: tags
;
918 GPtrArray
*cls_tags
= g_ptr_array_new();
920 /* check whether the class exists */
921 fill_find_tags_array(cls_tags
, src
, cls
, cls_scope
, TM_TYPE_WITH_MEMBERS
| tm_tag_namespace_t
, lang
);
922 ret
= cls_tags
->len
> 0;
923 g_ptr_array_free(cls_tags
, TRUE
);
927 g_free(member_scope
);
935 /* For an array of variable/type tags, find members inside the types */
937 find_scope_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, TMParserType lang
,
938 gboolean member
, const gchar
*current_scope
)
940 GPtrArray
*member_tags
= NULL
;
943 /* there may be several variables/types with the same name - try each of them until
944 * we find something */
945 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
947 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
948 TMTagType member_types
= tm_tag_member_t
| tm_tag_field_t
| tm_tag_method_t
;
949 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
951 if (tag
->type
& types
) /* type: namespace search */
953 if (tag
->type
& tm_tag_typedef_t
)
954 member_tags
= find_scope_members(searched_array
, tag
->name
, tag
->file
, lang
, TRUE
);
956 member_tags
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: searched_array
,
959 else if (tag
->var_type
) /* variable: scope search */
961 /* The question now is whether we should use member tags (such as
962 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
963 * (which means user has typed something like foo.bar.) or if we are
964 * inside a method where foo is a class member, we want scope completion
966 if (!(tag
->type
& member_types
) || member
||
967 member_at_method_scope(tags
, current_scope
, tag
, lang
))
969 gchar
*tag_type
= strip_type(tag
->var_type
, tag
->lang
);
971 member_tags
= find_scope_members(searched_array
, tag_type
, tag
->file
, lang
, FALSE
);
981 static GPtrArray
*find_namespace_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, TMParserType lang
)
983 GPtrArray
*member_tags
= NULL
;
986 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
988 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
990 member_tags
= find_scope_members_tags(searched_array
, tag
, TRUE
);
997 /* Returns all member tags of a struct/union/class if the provided name is a variable
998 of such a type or the name of the type.
999 @param source_file TMSourceFile of the edited source file
1000 @param name Name of the variable/type whose members are searched
1001 @param function TRUE if the name is a name of a function
1002 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1003 @param current_scope The current scope in the editor
1004 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1005 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1007 tm_workspace_find_scope_members (TMSourceFile
*source_file
, const char *name
,
1008 gboolean function
, gboolean member
, const gchar
*current_scope
, gboolean search_namespace
)
1010 TMParserType lang
= source_file
? source_file
->lang
: TM_PARSER_NONE
;
1011 GPtrArray
*tags
, *member_tags
= NULL
;
1012 TMTagType function_types
= tm_tag_function_t
| tm_tag_method_t
|
1013 tm_tag_macro_with_arg_t
| tm_tag_prototype_t
;
1014 TMTagType tag_type
= tm_tag_max_t
&
1015 ~(function_types
| tm_tag_enumerator_t
| tm_tag_namespace_t
| tm_tag_package_t
);
1016 TMTagAttrType sort_attr
[] = {tm_tag_attr_name_t
, 0};
1018 if (search_namespace
)
1020 tags
= tm_workspace_find(name
, NULL
, tm_tag_namespace_t
, NULL
, lang
);
1022 member_tags
= find_namespace_members_all(tags
, theWorkspace
->tags_array
, lang
);
1024 member_tags
= find_namespace_members_all(tags
, theWorkspace
->global_tags
, lang
);
1026 g_ptr_array_free(tags
, TRUE
);
1032 tag_type
= function_types
;
1034 /* tags corresponding to the variable/type name */
1035 tags
= tm_workspace_find(name
, NULL
, tag_type
, NULL
, lang
);
1037 /* Start searching inside the source file, continue with workspace tags and
1038 * end with global tags. This way we find the "closest" tag to the current
1039 * file in case there are more of them. */
1041 member_tags
= find_scope_members_all(tags
, source_file
->tags_array
,
1042 lang
, member
, current_scope
);
1044 member_tags
= find_scope_members_all(tags
, theWorkspace
->tags_array
, lang
,
1045 member
, current_scope
);
1047 member_tags
= find_scope_members_all(tags
, theWorkspace
->global_tags
, lang
,
1048 member
, current_scope
);
1050 g_ptr_array_free(tags
, TRUE
);
1053 tm_tags_dedup(member_tags
, sort_attr
, FALSE
);
1061 /* Dumps the workspace tree - useful for debugging */
1062 void tm_workspace_dump(void)
1067 g_message("Dumping TagManager workspace tree..");
1069 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
1071 TMSourceFile
*source_file
= theWorkspace
->source_files
->pdata
[i
];
1072 fprintf(stderr
, "%s", source_file
->file_name
);
1075 #endif /* TM_DEBUG */
1080 /* Returns a list of parent classes for the given class name
1081 @param name Name of the class
1082 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1083 static const GPtrArray
*tm_workspace_get_parents(const gchar
*name
)
1085 static TMTagAttrType type
[] = { tm_tag_attr_name_t
, tm_tag_attr_none_t
};
1086 static GPtrArray
*parents
= NULL
;
1087 const GPtrArray
*matches
;
1094 g_return_val_if_fail(name
&& isalpha(*name
),NULL
);
1096 if (NULL
== parents
)
1097 parents
= g_ptr_array_new();
1099 g_ptr_array_set_size(parents
, 0);
1100 matches
= tm_workspace_find(name
, NULL
, tm_tag_class_t
, type
, -1);
1101 if ((NULL
== matches
) || (0 == matches
->len
))
1103 g_ptr_array_add(parents
, matches
->pdata
[0]);
1104 while (i
< parents
->len
)
1106 tag
= TM_TAG(parents
->pdata
[i
]);
1107 if ((NULL
!= tag
->inheritance
) && (isalpha(tag
->inheritance
[0])))
1109 klasses
= g_strsplit(tag
->inheritance
, ",", 10);
1110 for (klass
= klasses
; (NULL
!= *klass
); ++ klass
)
1112 for (j
=0; j
< parents
->len
; ++j
)
1114 if (0 == strcmp(*klass
, TM_TAG(parents
->pdata
[j
])->name
))
1117 if (parents
->len
== j
)
1119 matches
= tm_workspace_find(*klass
, NULL
, tm_tag_class_t
, type
, -1);
1120 if ((NULL
!= matches
) && (0 < matches
->len
))
1121 g_ptr_array_add(parents
, matches
->pdata
[0]);
1124 g_strfreev(klasses
);