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 TMWorkspace
*theWorkspace
= NULL
;
66 static gboolean
tm_create_workspace(void)
68 theWorkspace
= g_new(TMWorkspace
, 1);
69 theWorkspace
->tags_array
= g_ptr_array_new();
71 theWorkspace
->global_tags
= g_ptr_array_new();
72 theWorkspace
->source_files
= g_ptr_array_new();
73 theWorkspace
->typename_array
= g_ptr_array_new();
74 theWorkspace
->global_typename_array
= g_ptr_array_new();
79 /* Frees the workspace structure and all child source files. Use only when
80 exiting from the main program.
82 void tm_workspace_free(void)
87 g_message("Workspace destroyed");
90 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
91 tm_source_file_free(theWorkspace
->source_files
->pdata
[i
]);
92 g_ptr_array_free(theWorkspace
->source_files
, TRUE
);
93 tm_tags_array_free(theWorkspace
->global_tags
, TRUE
);
94 g_ptr_array_free(theWorkspace
->tags_array
, TRUE
);
95 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
96 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
102 /* Since TMWorkspace is a singleton, you should not create multiple
103 workspaces, but get a pointer to the workspace whenever required. The first
104 time a pointer is requested, or a source file is added to the workspace,
105 a workspace is created. Subsequent calls to the function will return the
108 const TMWorkspace
*tm_get_workspace(void)
110 if (NULL
== theWorkspace
)
111 tm_create_workspace();
116 static void tm_workspace_merge_tags(GPtrArray
**big_array
, GPtrArray
*small_array
)
118 GPtrArray
*new_tags
= tm_tags_merge(*big_array
, small_array
, workspace_tags_sort_attrs
, FALSE
);
119 /* tags owned by TMSourceFile - free just the pointer array */
120 g_ptr_array_free(*big_array
, TRUE
);
121 *big_array
= new_tags
;
125 static void merge_extracted_tags(GPtrArray
**dest
, GPtrArray
*src
, TMTagType tag_types
)
129 arr
= tm_tags_extract(src
, tag_types
);
130 tm_workspace_merge_tags(dest
, arr
);
131 g_ptr_array_free(arr
, TRUE
);
135 static void update_source_file(TMSourceFile
*source_file
, guchar
* text_buf
,
136 gsize buf_size
, gboolean use_buffer
, gboolean update_workspace
)
139 g_message("Source file updating based on source file %s", source_file
->file_name
);
142 if (update_workspace
)
144 /* tm_source_file_parse() deletes the tag objects - remove the tags from
145 * workspace while they exist and can be scanned */
146 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
147 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
149 tm_source_file_parse(source_file
, text_buf
, buf_size
, use_buffer
);
150 tm_tags_sort(source_file
->tags_array
, file_tags_sort_attrs
, FALSE
, TRUE
);
151 if (update_workspace
)
154 g_message("Updating workspace from source file");
156 tm_workspace_merge_tags(&theWorkspace
->tags_array
, source_file
->tags_array
);
158 merge_extracted_tags(&(theWorkspace
->typename_array
), source_file
->tags_array
, TM_GLOBAL_TYPE_MASK
);
162 g_message("Skipping workspace update because update_workspace is %s",
163 update_workspace
?"TRUE":"FALSE");
169 /** Adds a source file to the workspace, parses it and updates the workspace tags.
170 @param source_file The source file to add to the workspace.
173 void tm_workspace_add_source_file(TMSourceFile
*source_file
)
175 g_return_if_fail(source_file
!= NULL
);
177 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
178 update_source_file(source_file
, NULL
, 0, FALSE
, TRUE
);
182 void tm_workspace_add_source_file_noupdate(TMSourceFile
*source_file
)
184 g_return_if_fail(source_file
!= NULL
);
186 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
190 /* Updates the source file by reparsing the text-buffer passed as parameter.
191 Ctags will use a parsing based on buffer instead of on files.
192 You should call this function when you don't want a previous saving of the file
193 you're editing. It's useful for a "real-time" updating of the tags.
194 The tags array and the tags themselves are destroyed and re-created, hence any
195 other tag arrays pointing to these tags should be rebuilt as well. All sorting
196 information is also lost. The language parameter is automatically detected
197 the first time the file is parsed if it is set to LANG_AUTO.
198 @param source_file The source file to update with a buffer.
199 @param text_buf A text buffer. The user should take care of allocate and free it after
201 @param buf_size The size of text_buf.
203 void tm_workspace_update_source_file_buffer(TMSourceFile
*source_file
, guchar
* text_buf
,
206 update_source_file(source_file
, text_buf
, buf_size
, TRUE
, TRUE
);
210 /** Removes a source file from the workspace if it exists. This function also removes
211 the tags belonging to this file from the workspace. To completely free the TMSourceFile
212 pointer call tm_source_file_free() on it.
213 @param source_file Pointer to the source file to be removed.
216 void tm_workspace_remove_source_file(TMSourceFile
*source_file
)
220 g_return_if_fail(source_file
!= NULL
);
222 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
224 if (theWorkspace
->source_files
->pdata
[i
] == source_file
)
226 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
227 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
228 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, i
);
235 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
236 want to globally refresh the workspace. This function does not call tm_source_file_update()
237 which should be called before this function on source files which need to be
240 static void tm_workspace_update(void)
243 TMSourceFile
*source_file
;
246 g_message("Recreating workspace tags array");
249 g_ptr_array_set_size(theWorkspace
->tags_array
, 0);
252 g_message("Total %d objects", theWorkspace
->source_files
->len
);
254 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
256 source_file
= theWorkspace
->source_files
->pdata
[i
];
258 g_message("Adding tags of %s", source_file
->file_name
);
260 if (source_file
->tags_array
->len
> 0)
262 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
264 g_ptr_array_add(theWorkspace
->tags_array
,
265 source_file
->tags_array
->pdata
[j
]);
270 g_message("Total: %d tags", theWorkspace
->tags_array
->len
);
272 tm_tags_sort(theWorkspace
->tags_array
, workspace_tags_sort_attrs
, TRUE
, FALSE
);
274 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
275 theWorkspace
->typename_array
= tm_tags_extract(theWorkspace
->tags_array
, TM_GLOBAL_TYPE_MASK
);
279 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
280 This is more efficient than calling tm_workspace_add_source_file() and
281 tm_workspace_update_source_file() separately for each of the files.
282 @param source_files The source files to be added to the workspace.
285 void tm_workspace_add_source_files(GPtrArray
*source_files
)
289 g_return_if_fail(source_files
!= NULL
);
291 for (i
= 0; i
< source_files
->len
; i
++)
293 TMSourceFile
*source_file
= source_files
->pdata
[i
];
295 tm_workspace_add_source_file_noupdate(source_file
);
296 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
299 tm_workspace_update();
303 /** Removes multiple source files from the workspace and updates the workspace tag
304 arrays. This is more efficient than calling tm_workspace_remove_source_file()
305 separately for each of the files. To completely free the TMSourceFile pointers
306 call tm_source_file_free() on each of them.
307 @param source_files The source files to be removed from the workspace.
310 void tm_workspace_remove_source_files(GPtrArray
*source_files
)
314 g_return_if_fail(source_files
!= NULL
);
316 //TODO: sort both arrays by pointer value and remove in single pass
317 for (i
= 0; i
< source_files
->len
; i
++)
319 TMSourceFile
*source_file
= source_files
->pdata
[i
];
321 for (j
= 0; j
< theWorkspace
->source_files
->len
; j
++)
323 if (theWorkspace
->source_files
->pdata
[j
] == source_file
)
325 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, j
);
331 tm_workspace_update();
335 /* Loads the global tag list from the specified file. The global tag list should
336 have been first created using tm_workspace_create_global_tags().
337 @param tags_file The file containing global tags.
338 @return TRUE on success, FALSE on failure.
339 @see tm_workspace_create_global_tags()
341 gboolean
tm_workspace_load_global_tags(const char *tags_file
, gint mode
)
345 GPtrArray
*file_tags
, *new_tags
;
347 TMFileFormat format
= TM_FILE_FORMAT_TAGMANAGER
;
349 if (NULL
== (fp
= g_fopen(tags_file
, "r")))
351 if ((NULL
== fgets((gchar
*) buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
354 return FALSE
; /* early out on error */
357 { /* We read the first line for the format specification. */
358 if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=pipe") != NULL
)
359 format
= TM_FILE_FORMAT_PIPE
;
360 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=tagmanager") != NULL
)
361 format
= TM_FILE_FORMAT_TAGMANAGER
;
362 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=ctags") != NULL
)
363 format
= TM_FILE_FORMAT_CTAGS
;
364 else if (strncmp((gchar
*) buf
, "!_TAG_", 6) == 0)
365 format
= TM_FILE_FORMAT_CTAGS
;
367 { /* We didn't find a valid format specification, so we try to auto-detect the format
368 * by counting the pipe characters on the first line and asumme pipe format when
369 * we find more than one pipe on the line. */
370 guint i
, pipe_cnt
= 0, tab_cnt
= 0;
371 for (i
= 0; i
< BUFSIZ
&& buf
[i
] != '\0' && pipe_cnt
< 2; i
++)
375 else if (buf
[i
] == '\t')
379 format
= TM_FILE_FORMAT_PIPE
;
380 else if (tab_cnt
> 1)
381 format
= TM_FILE_FORMAT_CTAGS
;
383 rewind(fp
); /* reset the file pointer, to start reading again from the beginning */
386 file_tags
= g_ptr_array_new();
387 while (NULL
!= (tag
= tm_tag_new_from_file(NULL
, fp
, mode
, format
)))
388 g_ptr_array_add(file_tags
, tag
);
391 tm_tags_sort(file_tags
, global_tags_sort_attrs
, TRUE
, TRUE
);
393 /* reorder the whole array, because tm_tags_find expects a sorted array */
394 new_tags
= tm_tags_merge(theWorkspace
->global_tags
,
395 file_tags
, global_tags_sort_attrs
, TRUE
);
396 g_ptr_array_free(theWorkspace
->global_tags
, TRUE
);
397 g_ptr_array_free(file_tags
, TRUE
);
398 theWorkspace
->global_tags
= new_tags
;
400 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
401 theWorkspace
->global_typename_array
= tm_tags_extract(new_tags
, TM_GLOBAL_TYPE_MASK
);
407 static guint
tm_file_inode_hash(gconstpointer key
)
410 const char *filename
= (const char*)key
;
411 if (g_stat(filename
, &file_stat
) == 0)
414 g_message ("Hash for '%s' is '%d'\n", filename
, file_stat
.st_ino
);
416 return g_direct_hash ((gpointer
)(intptr_t)file_stat
.st_ino
);
423 static void tm_move_entries_to_g_list(gpointer key
, gpointer value
, gpointer user_data
)
425 GList
**pp_list
= (GList
**)user_data
;
427 if (user_data
== NULL
)
430 *pp_list
= g_list_prepend(*pp_list
, value
);
434 static void write_includes_file(FILE *fp
, GList
*includes_files
)
438 node
= includes_files
;
441 char *str
= g_strdup_printf("#include \"%s\"\n", (char*)node
->data
);
442 size_t str_len
= strlen(str
);
444 fwrite(str
, str_len
, 1, fp
);
446 node
= g_list_next(node
);
451 static void append_to_temp_file(FILE *fp
, GList
*file_list
)
458 const char *fname
= node
->data
;
463 if (! g_file_get_contents(fname
, &contents
, &length
, &err
))
465 fprintf(stderr
, "Unable to read file: %s\n", err
->message
);
470 fwrite(contents
, length
, 1, fp
);
471 fwrite("\n", 1, 1, fp
); /* in case file doesn't end in newline (e.g. windows). */
474 node
= g_list_next (node
);
479 static gchar
*create_temp_file(const gchar
*tpl
)
484 fd
= g_file_open_tmp(tpl
, &name
, NULL
);
494 /* Creates a list of global tags. Ideally, this should be created once during
495 installations so that all users can use the same file. This is because a full
496 scale global tag list can occupy several megabytes of disk space.
497 @param pre_process The pre-processing command. This is executed via system(),
498 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
499 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
501 @param tags_file The file where the tags will be stored.
502 @param lang The language to use for the tags file.
503 @return TRUE on success, FALSE on failure.
505 gboolean
tm_workspace_create_global_tags(const char *pre_process
, const char **includes
,
506 int includes_count
, const char *tags_file
, int lang
)
516 TMSourceFile
*source_file
;
517 GPtrArray
*tags_array
;
518 GHashTable
*includes_files_hash
;
519 GList
*includes_files
= NULL
;
520 gchar
*temp_file
= create_temp_file("tmp_XXXXXX.cpp");
521 gchar
*temp_file2
= create_temp_file("tmp_XXXXXX.cpp");
523 if (NULL
== temp_file
|| NULL
== temp_file2
||
524 NULL
== (fp
= g_fopen(temp_file
, "w")))
531 includes_files_hash
= g_hash_table_new_full (tm_file_inode_hash
,
538 if (includes
[0][0] == '"') /* leading \" char for glob matching */
539 for(idx_inc
= 0; idx_inc
< includes_count
; idx_inc
++)
541 size_t dirty_len
= strlen(includes
[idx_inc
]);
542 char *clean_path
= g_malloc(dirty_len
- 1);
544 strncpy(clean_path
, includes
[idx_inc
] + 1, dirty_len
- 1);
545 clean_path
[dirty_len
- 2] = 0;
548 g_message ("[o][%s]\n", clean_path
);
550 glob(clean_path
, 0, NULL
, &globbuf
);
553 g_message ("matches: %d\n", globbuf
.gl_pathc
);
556 for(idx_glob
= 0; idx_glob
< globbuf
.gl_pathc
; idx_glob
++)
559 g_message (">>> %s\n", globbuf
.gl_pathv
[idx_glob
]);
561 if (!g_hash_table_lookup(includes_files_hash
,
562 globbuf
.gl_pathv
[idx_glob
]))
564 char* file_name_copy
= strdup(globbuf
.gl_pathv
[idx_glob
]);
565 g_hash_table_insert(includes_files_hash
, file_name_copy
,
568 g_message ("Added ...\n");
577 /* no glob support or globbing not wanted */
578 for(idx_inc
= 0; idx_inc
< includes_count
; idx_inc
++)
580 if (!g_hash_table_lookup(includes_files_hash
,
583 char* file_name_copy
= strdup(includes
[idx_inc
]);
584 g_hash_table_insert(includes_files_hash
, file_name_copy
,
589 /* Checks for duplicate file entries which would case trouble */
590 g_hash_table_foreach(includes_files_hash
, tm_move_entries_to_g_list
,
593 includes_files
= g_list_reverse (includes_files
);
596 g_message ("writing out files to %s\n", temp_file
);
598 if (pre_process
!= NULL
)
599 write_includes_file(fp
, includes_files
);
601 append_to_temp_file(fp
, includes_files
);
603 g_list_free (includes_files
);
604 g_hash_table_destroy(includes_files_hash
);
605 includes_files_hash
= NULL
;
606 includes_files
= NULL
;
609 if (pre_process
!= NULL
)
612 gchar
*tmp_errfile
= create_temp_file("tmp_XXXXXX");
613 gchar
*errors
= NULL
;
614 command
= g_strdup_printf("%s %s >%s 2>%s",
615 pre_process
, temp_file
, temp_file2
, tmp_errfile
);
617 g_message("Executing: %s", command
);
619 ret
= system(command
);
623 g_file_get_contents(tmp_errfile
, &errors
, NULL
, NULL
);
624 if (errors
&& *errors
)
625 g_printerr("%s", errors
);
627 g_unlink(tmp_errfile
);
631 g_unlink(temp_file2
);
637 /* no pre-processing needed, so temp_file2 = temp_file */
638 g_unlink(temp_file2
);
640 temp_file2
= temp_file
;
643 source_file
= tm_source_file_new(temp_file2
, tm_source_file_get_lang_name(lang
));
644 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
645 if (NULL
== source_file
)
647 g_unlink(temp_file2
);
650 g_unlink(temp_file2
);
652 if (0 == source_file
->tags_array
->len
)
654 tm_source_file_free(source_file
);
657 tags_array
= tm_tags_extract(source_file
->tags_array
, tm_tag_max_t
);
658 if ((NULL
== tags_array
) || (0 == tags_array
->len
))
661 g_ptr_array_free(tags_array
, TRUE
);
662 tm_source_file_free(source_file
);
665 if (FALSE
== tm_tags_sort(tags_array
, global_tags_sort_attrs
, TRUE
, FALSE
))
667 tm_source_file_free(source_file
);
670 if (NULL
== (fp
= g_fopen(tags_file
, "w")))
672 tm_source_file_free(source_file
);
675 fprintf(fp
, "# format=tagmanager\n");
676 for (i
= 0; i
< tags_array
->len
; ++i
)
678 tm_tag_write(TM_TAG(tags_array
->pdata
[i
]), fp
, tm_tag_attr_type_t
679 | tm_tag_attr_scope_t
| tm_tag_attr_arglist_t
| tm_tag_attr_vartype_t
680 | tm_tag_attr_pointer_t
);
683 tm_source_file_free(source_file
);
684 g_ptr_array_free(tags_array
, TRUE
);
689 static gboolean
langs_compatible(langType lang
, langType other
)
691 if (lang
== other
|| lang
== -1 || other
== -1)
693 /* Accept CPP tags for C lang and vice versa */
694 else if (lang
== TM_PARSER_C
&& other
== TM_PARSER_CPP
)
696 else if (lang
== TM_PARSER_CPP
&& other
== TM_PARSER_C
)
703 static void fill_find_tags_array(GPtrArray
*dst
, const GPtrArray
*src
,
704 const char *name
, const char *scope
, TMTagType type
, langType lang
)
709 if (!src
|| !dst
|| !name
|| !*name
)
712 tag
= tm_tags_find(src
, name
, FALSE
, &num
);
713 for (i
= 0; i
< num
; ++i
)
715 if ((type
& (*tag
)->type
) &&
716 langs_compatible(lang
, (*tag
)->lang
) &&
717 (!scope
|| g_strcmp0((*tag
)->scope
, scope
) == 0))
719 g_ptr_array_add(dst
, *tag
);
726 /* Returns all matching tags found in the workspace.
727 @param name The name of the tag to find.
728 @param scope The scope name of the tag to find, or NULL.
729 @param type The tag types to return (TMTagType). Can be a bitmask.
730 @param attrs The attributes to sort and dedup on (0 terminated integer array).
731 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
733 @return Array of matching tags.
735 GPtrArray
*tm_workspace_find(const char *name
, const char *scope
, TMTagType type
,
736 TMTagAttrType
*attrs
, langType lang
)
738 GPtrArray
*tags
= g_ptr_array_new();
740 fill_find_tags_array(tags
, theWorkspace
->tags_array
, name
, scope
, type
, lang
);
741 fill_find_tags_array(tags
, theWorkspace
->global_tags
, name
, scope
, type
, lang
);
744 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
750 static void fill_find_tags_array_prefix(GPtrArray
*dst
, const GPtrArray
*src
,
751 const char *name
, langType lang
, guint max_num
)
753 TMTag
**tag
, *last
= NULL
;
756 if (!src
|| !dst
|| !name
|| !*name
)
760 tag
= tm_tags_find(src
, name
, TRUE
, &count
);
761 for (i
= 0; i
< count
&& num
< max_num
; ++i
)
763 if (langs_compatible(lang
, (*tag
)->lang
) &&
764 !tm_tag_is_anon(*tag
) &&
765 (!last
|| g_strcmp0(last
->name
, (*tag
)->name
) != 0))
767 g_ptr_array_add(dst
, *tag
);
776 /* Returns tags with the specified prefix sorted by name. If there are several
777 tags with the same name, only one of them appears in the resulting array.
778 @param prefix The prefix of the tag to find.
779 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
781 @param max_num The maximum number of tags to return.
782 @return Array of matching tags sorted by their name.
784 GPtrArray
*tm_workspace_find_prefix(const char *prefix
, langType lang
, guint max_num
)
786 TMTagAttrType attrs
[] = { tm_tag_attr_name_t
, 0 };
787 GPtrArray
*tags
= g_ptr_array_new();
789 fill_find_tags_array_prefix(tags
, theWorkspace
->tags_array
, prefix
, lang
, max_num
);
790 fill_find_tags_array_prefix(tags
, theWorkspace
->global_tags
, prefix
, lang
, max_num
);
792 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
793 if (tags
->len
> max_num
)
800 /* Gets all members of type_tag; search them inside the all array.
801 * The namespace parameter determines whether we are performing the "namespace"
802 * search (user has typed something like "A::" where A is a type) or "scope" search
803 * (user has typed "a." where a is a global struct-like variable). With the
804 * namespace search we return all direct descendants of any type while with the
805 * scope search we return only those which can be invoked on a variable (member,
808 find_scope_members_tags (const GPtrArray
*all
, TMTag
*type_tag
, gboolean
namespace)
810 TMTagType member_types
= tm_tag_max_t
& ~(TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
);
811 GPtrArray
*tags
= g_ptr_array_new();
816 member_types
= tm_tag_max_t
;
818 if (type_tag
->scope
&& *(type_tag
->scope
))
819 scope
= g_strconcat(type_tag
->scope
, tm_tag_context_separator(type_tag
->lang
), type_tag
->name
, NULL
);
821 scope
= g_strdup(type_tag
->name
);
823 for (i
= 0; i
< all
->len
; ++i
)
825 TMTag
*tag
= TM_TAG (all
->pdata
[i
]);
827 if (tag
&& (tag
->type
& member_types
) &&
828 tag
->scope
&& tag
->scope
[0] != '\0' &&
829 langs_compatible(tag
->lang
, type_tag
->lang
) &&
830 strcmp(scope
, tag
->scope
) == 0 &&
831 (!namespace || !tm_tag_is_anon(tag
)))
833 g_ptr_array_add (tags
, tag
);
841 g_ptr_array_free(tags
, TRUE
);
849 static gchar
*strip_type(const gchar
*scoped_name
, langType lang
)
851 if (scoped_name
!= NULL
)
853 /* remove scope prefix */
854 const gchar
*sep
= tm_tag_context_separator(lang
);
855 const gchar
*base
= g_strrstr(scoped_name
, sep
);
856 gchar
*name
= base
? g_strdup(base
+ strlen(sep
)) : g_strdup(scoped_name
);
858 /* remove pointers */
859 g_strdelimit(name
, "*^", ' ');
868 /* Gets all members of the type with the given name; search them inside tags_array */
870 find_scope_members (const GPtrArray
*tags_array
, const gchar
*name
, TMSourceFile
*file
,
871 langType lang
, gboolean
namespace)
873 GPtrArray
*res
= NULL
;
877 g_return_val_if_fail(name
&& *name
, NULL
);
879 type_name
= g_strdup(name
);
881 /* Check if type_name is a type that can possibly contain members.
882 * Try to resolve intermediate typedefs to get the real type name. Also
883 * add scope information to the name if applicable.
884 * The loop below loops only when resolving typedefs - avoid possibly infinite
885 * loop when typedefs create a cycle by adding some limits. */
886 for (i
= 0; i
< 5; i
++)
889 GPtrArray
*type_tags
;
890 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
894 types
&= ~tm_tag_enum_t
;
896 type_tags
= g_ptr_array_new();
897 fill_find_tags_array(type_tags
, tags_array
, type_name
, NULL
, types
, lang
);
899 for (j
= 0; j
< type_tags
->len
; j
++)
901 TMTag
*test_tag
= TM_TAG(type_tags
->pdata
[j
]);
903 /* anonymous type defined in a different file than the variable -
904 * this isn't the type we are looking for */
905 if (tm_tag_is_anon(test_tag
) && (file
!= test_tag
->file
|| test_tag
->file
== NULL
))
910 /* prefer non-typedef tags because we can be sure they contain members */
911 if (test_tag
->type
!= tm_tag_typedef_t
)
915 g_ptr_array_free(type_tags
, TRUE
);
917 if (!tag
) /* not a type that can contain members */
920 /* intermediate typedef - resolve to the real type */
921 if (tag
->type
== tm_tag_typedef_t
)
923 if (tag
->var_type
&& tag
->var_type
[0] != '\0')
926 type_name
= strip_type(tag
->var_type
, tag
->lang
);
932 else /* real type with members */
934 /* use the same file as the composite type if file information available */
935 res
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: tags_array
, tag
, namespace);
946 /* Checks whether a member tag is directly accessible from method with method_scope */
947 static gboolean
member_at_method_scope(const GPtrArray
*tags
, const gchar
*method_scope
, TMTag
*member_tag
,
950 const gchar
*sep
= tm_tag_context_separator(lang
);
951 gboolean ret
= FALSE
;
955 /* method scope is in the form ...::class_name::method_name */
956 comps
= g_strsplit (method_scope
, sep
, 0);
957 len
= g_strv_length(comps
);
960 gchar
*method
, *member_scope
, *cls
, *cls_scope
;
962 /* get method/member scope */
963 method
= comps
[len
- 1];
964 comps
[len
- 1] = NULL
;
965 member_scope
= g_strjoinv(sep
, comps
);
966 comps
[len
- 1] = method
;
968 /* get class scope */
969 cls
= comps
[len
- 2];
970 comps
[len
- 2] = NULL
;
971 cls_scope
= g_strjoinv(sep
, comps
);
972 comps
[len
- 2] = cls
;
973 cls_scope
= strlen(cls_scope
) > 0 ? cls_scope
: NULL
;
975 /* check whether member inside the class */
976 if (g_strcmp0(member_tag
->scope
, member_scope
) == 0)
978 const GPtrArray
*src
= member_tag
->file
? member_tag
->file
->tags_array
: tags
;
979 GPtrArray
*cls_tags
= g_ptr_array_new();
981 /* check whether the class exists */
982 fill_find_tags_array(cls_tags
, src
, cls
, cls_scope
, TM_TYPE_WITH_MEMBERS
| tm_tag_namespace_t
, lang
);
983 ret
= cls_tags
->len
> 0;
984 g_ptr_array_free(cls_tags
, TRUE
);
988 g_free(member_scope
);
996 /* For an array of variable/type tags, find members inside the types */
998 find_scope_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, langType lang
,
999 gboolean member
, const gchar
*current_scope
)
1001 GPtrArray
*member_tags
= NULL
;
1004 /* there may be several variables/types with the same name - try each of them until
1005 * we find something */
1006 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
1008 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
1009 TMTagType member_types
= tm_tag_member_t
| tm_tag_field_t
| tm_tag_method_t
;
1010 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
1012 if (tag
->type
& types
) /* type: namespace search */
1014 if (tag
->type
& tm_tag_typedef_t
)
1015 member_tags
= find_scope_members(searched_array
, tag
->name
, tag
->file
, lang
, TRUE
);
1017 member_tags
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: searched_array
,
1020 else if (tag
->var_type
) /* variable: scope search */
1022 /* The question now is whether we should use member tags (such as
1023 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
1024 * (which means user has typed something like foo.bar.) or if we are
1025 * inside a method where foo is a class member, we want scope completion
1027 if (!(tag
->type
& member_types
) || member
||
1028 member_at_method_scope(tags
, current_scope
, tag
, lang
))
1030 gchar
*tag_type
= strip_type(tag
->var_type
, tag
->lang
);
1032 member_tags
= find_scope_members(searched_array
, tag_type
, tag
->file
, lang
, FALSE
);
1042 static GPtrArray
*find_namespace_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, langType lang
)
1044 GPtrArray
*member_tags
= NULL
;
1047 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
1049 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
1051 member_tags
= find_scope_members_tags(searched_array
, tag
, TRUE
);
1058 /* Returns all member tags of a struct/union/class if the provided name is a variable
1059 of such a type or the name of the type.
1060 @param source_file TMSourceFile of the edited source file or NULL if not available
1061 @param name Name of the variable/type whose members are searched
1062 @param function TRUE if the name is a name of a function
1063 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1064 @param current_scope The current scope in the editor
1065 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1066 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1068 tm_workspace_find_scope_members (TMSourceFile
*source_file
, const char *name
,
1069 gboolean function
, gboolean member
, const gchar
*current_scope
, gboolean search_namespace
)
1071 langType lang
= source_file
? source_file
->lang
: -1;
1072 GPtrArray
*tags
, *member_tags
= NULL
;
1073 TMTagType function_types
= tm_tag_function_t
| tm_tag_method_t
|
1074 tm_tag_macro_with_arg_t
| tm_tag_prototype_t
;
1075 TMTagType tag_type
= tm_tag_max_t
&
1076 ~(function_types
| tm_tag_enumerator_t
| tm_tag_namespace_t
| tm_tag_package_t
);
1077 TMTagAttrType sort_attr
[] = {tm_tag_attr_name_t
, 0};
1079 if (search_namespace
)
1081 tags
= tm_workspace_find(name
, NULL
, tm_tag_namespace_t
, NULL
, lang
);
1083 member_tags
= find_namespace_members_all(tags
, theWorkspace
->tags_array
, lang
);
1085 member_tags
= find_namespace_members_all(tags
, theWorkspace
->global_tags
, lang
);
1087 g_ptr_array_free(tags
, TRUE
);
1093 tag_type
= function_types
;
1095 /* tags corresponding to the variable/type name */
1096 tags
= tm_workspace_find(name
, NULL
, tag_type
, NULL
, lang
);
1098 /* Start searching inside the source file, continue with workspace tags and
1099 * end with global tags. This way we find the "closest" tag to the current
1100 * file in case there are more of them. */
1102 member_tags
= find_scope_members_all(tags
, source_file
->tags_array
,
1103 lang
, member
, current_scope
);
1105 member_tags
= find_scope_members_all(tags
, theWorkspace
->tags_array
, lang
,
1106 member
, current_scope
);
1108 member_tags
= find_scope_members_all(tags
, theWorkspace
->global_tags
, lang
,
1109 member
, current_scope
);
1111 g_ptr_array_free(tags
, TRUE
);
1114 tm_tags_dedup(member_tags
, sort_attr
, FALSE
);
1122 /* Dumps the workspace tree - useful for debugging */
1123 void tm_workspace_dump(void)
1128 g_message("Dumping TagManager workspace tree..");
1130 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
1132 TMSourceFile
*source_file
= theWorkspace
->source_files
->pdata
[i
];
1133 fprintf(stderr
, "%s", source_file
->file_name
);
1136 #endif /* TM_DEBUG */
1141 /* Returns a list of parent classes for the given class name
1142 @param name Name of the class
1143 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1144 static const GPtrArray
*tm_workspace_get_parents(const gchar
*name
)
1146 static TMTagAttrType type
[] = { tm_tag_attr_name_t
, tm_tag_attr_none_t
};
1147 static GPtrArray
*parents
= NULL
;
1148 const GPtrArray
*matches
;
1155 g_return_val_if_fail(name
&& isalpha(*name
),NULL
);
1157 if (NULL
== parents
)
1158 parents
= g_ptr_array_new();
1160 g_ptr_array_set_size(parents
, 0);
1161 matches
= tm_workspace_find(name
, NULL
, tm_tag_class_t
, type
, -1);
1162 if ((NULL
== matches
) || (0 == matches
->len
))
1164 g_ptr_array_add(parents
, matches
->pdata
[0]);
1165 while (i
< parents
->len
)
1167 tag
= TM_TAG(parents
->pdata
[i
]);
1168 if ((NULL
!= tag
->inheritance
) && (isalpha(tag
->inheritance
[0])))
1170 klasses
= g_strsplit(tag
->inheritance
, ",", 10);
1171 for (klass
= klasses
; (NULL
!= *klass
); ++ klass
)
1173 for (j
=0; j
< parents
->len
; ++j
)
1175 if (0 == strcmp(*klass
, TM_TAG(parents
->pdata
[j
])->name
))
1178 if (parents
->len
== j
)
1180 matches
= tm_workspace_find(*klass
, NULL
, tm_tag_class_t
, type
, -1);
1181 if ((NULL
!= matches
) && (0 < matches
->len
))
1182 g_ptr_array_add(parents
, matches
->pdata
[0]);
1185 g_strfreev(klasses
);