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.
197 @param source_file The source file to update with a buffer.
198 @param text_buf A text buffer. The user should take care of allocate and free it after
200 @param buf_size The size of text_buf.
202 void tm_workspace_update_source_file_buffer(TMSourceFile
*source_file
, guchar
* text_buf
,
205 update_source_file(source_file
, text_buf
, buf_size
, TRUE
, TRUE
);
209 /** Removes a source file from the workspace if it exists. This function also removes
210 the tags belonging to this file from the workspace. To completely free the TMSourceFile
211 pointer call tm_source_file_free() on it.
212 @param source_file Pointer to the source file to be removed.
215 void tm_workspace_remove_source_file(TMSourceFile
*source_file
)
219 g_return_if_fail(source_file
!= NULL
);
221 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
223 if (theWorkspace
->source_files
->pdata
[i
] == source_file
)
225 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
226 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
227 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, i
);
234 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
235 want to globally refresh the workspace. This function does not call tm_source_file_update()
236 which should be called before this function on source files which need to be
239 static void tm_workspace_update(void)
242 TMSourceFile
*source_file
;
245 g_message("Recreating workspace tags array");
248 g_ptr_array_set_size(theWorkspace
->tags_array
, 0);
251 g_message("Total %d objects", theWorkspace
->source_files
->len
);
253 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
255 source_file
= theWorkspace
->source_files
->pdata
[i
];
257 g_message("Adding tags of %s", source_file
->file_name
);
259 if (source_file
->tags_array
->len
> 0)
261 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
263 g_ptr_array_add(theWorkspace
->tags_array
,
264 source_file
->tags_array
->pdata
[j
]);
269 g_message("Total: %d tags", theWorkspace
->tags_array
->len
);
271 tm_tags_sort(theWorkspace
->tags_array
, workspace_tags_sort_attrs
, TRUE
, FALSE
);
273 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
274 theWorkspace
->typename_array
= tm_tags_extract(theWorkspace
->tags_array
, TM_GLOBAL_TYPE_MASK
);
278 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
279 This is more efficient than calling tm_workspace_add_source_file() and
280 tm_workspace_update_source_file() separately for each of the files.
281 @param source_files The source files to be added to the workspace.
284 void tm_workspace_add_source_files(GPtrArray
*source_files
)
288 g_return_if_fail(source_files
!= NULL
);
290 for (i
= 0; i
< source_files
->len
; i
++)
292 TMSourceFile
*source_file
= source_files
->pdata
[i
];
294 tm_workspace_add_source_file_noupdate(source_file
);
295 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
298 tm_workspace_update();
302 /** Removes multiple source files from the workspace and updates the workspace tag
303 arrays. This is more efficient than calling tm_workspace_remove_source_file()
304 separately for each of the files. To completely free the TMSourceFile pointers
305 call tm_source_file_free() on each of them.
306 @param source_files The source files to be removed from the workspace.
309 void tm_workspace_remove_source_files(GPtrArray
*source_files
)
313 g_return_if_fail(source_files
!= NULL
);
315 //TODO: sort both arrays by pointer value and remove in single pass
316 for (i
= 0; i
< source_files
->len
; i
++)
318 TMSourceFile
*source_file
= source_files
->pdata
[i
];
320 for (j
= 0; j
< theWorkspace
->source_files
->len
; j
++)
322 if (theWorkspace
->source_files
->pdata
[j
] == source_file
)
324 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, j
);
330 tm_workspace_update();
334 /* Loads the global tag list from the specified file. The global tag list should
335 have been first created using tm_workspace_create_global_tags().
336 @param tags_file The file containing global tags.
337 @return TRUE on success, FALSE on failure.
338 @see tm_workspace_create_global_tags()
340 gboolean
tm_workspace_load_global_tags(const char *tags_file
, TMParserType mode
)
344 GPtrArray
*file_tags
, *new_tags
;
346 TMFileFormat format
= TM_FILE_FORMAT_TAGMANAGER
;
348 if (NULL
== (fp
= g_fopen(tags_file
, "r")))
350 if ((NULL
== fgets((gchar
*) buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
353 return FALSE
; /* early out on error */
356 { /* We read the first line for the format specification. */
357 if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=pipe") != NULL
)
358 format
= TM_FILE_FORMAT_PIPE
;
359 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=tagmanager") != NULL
)
360 format
= TM_FILE_FORMAT_TAGMANAGER
;
361 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=ctags") != NULL
)
362 format
= TM_FILE_FORMAT_CTAGS
;
363 else if (strncmp((gchar
*) buf
, "!_TAG_", 6) == 0)
364 format
= TM_FILE_FORMAT_CTAGS
;
366 { /* We didn't find a valid format specification, so we try to auto-detect the format
367 * by counting the pipe characters on the first line and asumme pipe format when
368 * we find more than one pipe on the line. */
369 guint i
, pipe_cnt
= 0, tab_cnt
= 0;
370 for (i
= 0; i
< BUFSIZ
&& buf
[i
] != '\0' && pipe_cnt
< 2; i
++)
374 else if (buf
[i
] == '\t')
378 format
= TM_FILE_FORMAT_PIPE
;
379 else if (tab_cnt
> 1)
380 format
= TM_FILE_FORMAT_CTAGS
;
382 rewind(fp
); /* reset the file pointer, to start reading again from the beginning */
385 file_tags
= g_ptr_array_new();
386 while (NULL
!= (tag
= tm_tag_new_from_file(NULL
, fp
, mode
, format
)))
387 g_ptr_array_add(file_tags
, tag
);
390 tm_tags_sort(file_tags
, global_tags_sort_attrs
, TRUE
, TRUE
);
392 /* reorder the whole array, because tm_tags_find expects a sorted array */
393 new_tags
= tm_tags_merge(theWorkspace
->global_tags
,
394 file_tags
, global_tags_sort_attrs
, TRUE
);
395 g_ptr_array_free(theWorkspace
->global_tags
, TRUE
);
396 g_ptr_array_free(file_tags
, TRUE
);
397 theWorkspace
->global_tags
= new_tags
;
399 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
400 theWorkspace
->global_typename_array
= tm_tags_extract(new_tags
, TM_GLOBAL_TYPE_MASK
);
406 static guint
tm_file_inode_hash(gconstpointer key
)
409 const char *filename
= (const char*)key
;
410 if (g_stat(filename
, &file_stat
) == 0)
413 g_message ("Hash for '%s' is '%d'\n", filename
, file_stat
.st_ino
);
415 return g_direct_hash ((gpointer
)(intptr_t)file_stat
.st_ino
);
422 static void tm_move_entries_to_g_list(gpointer key
, gpointer value
, gpointer user_data
)
424 GList
**pp_list
= (GList
**)user_data
;
426 if (user_data
== NULL
)
429 *pp_list
= g_list_prepend(*pp_list
, value
);
433 static void write_includes_file(FILE *fp
, GList
*includes_files
)
437 node
= includes_files
;
440 char *str
= g_strdup_printf("#include \"%s\"\n", (char*)node
->data
);
441 size_t str_len
= strlen(str
);
443 fwrite(str
, str_len
, 1, fp
);
445 node
= g_list_next(node
);
450 static void append_to_temp_file(FILE *fp
, GList
*file_list
)
457 const char *fname
= node
->data
;
462 if (! g_file_get_contents(fname
, &contents
, &length
, &err
))
464 fprintf(stderr
, "Unable to read file: %s\n", err
->message
);
469 fwrite(contents
, length
, 1, fp
);
470 fwrite("\n", 1, 1, fp
); /* in case file doesn't end in newline (e.g. windows). */
473 node
= g_list_next (node
);
478 static gchar
*create_temp_file(const gchar
*tpl
)
483 fd
= g_file_open_tmp(tpl
, &name
, NULL
);
493 /* Creates a list of global tags. Ideally, this should be created once during
494 installations so that all users can use the same file. This is because a full
495 scale global tag list can occupy several megabytes of disk space.
496 @param pre_process The pre-processing command. This is executed via system(),
497 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
498 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
500 @param tags_file The file where the tags will be stored.
501 @param lang The language to use for the tags file.
502 @return TRUE on success, FALSE on failure.
504 gboolean
tm_workspace_create_global_tags(const char *pre_process
, const char **includes
,
505 int includes_count
, const char *tags_file
, TMParserType lang
)
515 TMSourceFile
*source_file
;
516 GPtrArray
*tags_array
;
517 GHashTable
*includes_files_hash
;
518 GList
*includes_files
= NULL
;
519 gchar
*temp_file
= create_temp_file("tmp_XXXXXX.cpp");
520 gchar
*temp_file2
= create_temp_file("tmp_XXXXXX.cpp");
522 if (NULL
== temp_file
|| NULL
== temp_file2
||
523 NULL
== (fp
= g_fopen(temp_file
, "w")))
530 includes_files_hash
= g_hash_table_new_full (tm_file_inode_hash
,
537 if (includes
[0][0] == '"') /* leading \" char for glob matching */
538 for(idx_inc
= 0; idx_inc
< includes_count
; idx_inc
++)
540 size_t dirty_len
= strlen(includes
[idx_inc
]);
541 char *clean_path
= g_malloc(dirty_len
- 1);
543 strncpy(clean_path
, includes
[idx_inc
] + 1, dirty_len
- 1);
544 clean_path
[dirty_len
- 2] = 0;
547 g_message ("[o][%s]\n", clean_path
);
549 glob(clean_path
, 0, NULL
, &globbuf
);
552 g_message ("matches: %d\n", globbuf
.gl_pathc
);
555 for(idx_glob
= 0; idx_glob
< globbuf
.gl_pathc
; idx_glob
++)
558 g_message (">>> %s\n", globbuf
.gl_pathv
[idx_glob
]);
560 if (!g_hash_table_lookup(includes_files_hash
,
561 globbuf
.gl_pathv
[idx_glob
]))
563 char* file_name_copy
= strdup(globbuf
.gl_pathv
[idx_glob
]);
564 g_hash_table_insert(includes_files_hash
, file_name_copy
,
567 g_message ("Added ...\n");
576 /* no glob support or globbing not wanted */
577 for(idx_inc
= 0; idx_inc
< includes_count
; idx_inc
++)
579 if (!g_hash_table_lookup(includes_files_hash
,
582 char* file_name_copy
= strdup(includes
[idx_inc
]);
583 g_hash_table_insert(includes_files_hash
, file_name_copy
,
588 /* Checks for duplicate file entries which would case trouble */
589 g_hash_table_foreach(includes_files_hash
, tm_move_entries_to_g_list
,
592 includes_files
= g_list_reverse (includes_files
);
595 g_message ("writing out files to %s\n", temp_file
);
597 if (pre_process
!= NULL
)
598 write_includes_file(fp
, includes_files
);
600 append_to_temp_file(fp
, includes_files
);
602 g_list_free (includes_files
);
603 g_hash_table_destroy(includes_files_hash
);
604 includes_files_hash
= NULL
;
605 includes_files
= NULL
;
608 if (pre_process
!= NULL
)
611 gchar
*tmp_errfile
= create_temp_file("tmp_XXXXXX");
612 gchar
*errors
= NULL
;
613 command
= g_strdup_printf("%s %s >%s 2>%s",
614 pre_process
, temp_file
, temp_file2
, tmp_errfile
);
616 g_message("Executing: %s", command
);
618 ret
= system(command
);
622 g_file_get_contents(tmp_errfile
, &errors
, NULL
, NULL
);
623 if (errors
&& *errors
)
624 g_printerr("%s", errors
);
626 g_unlink(tmp_errfile
);
630 g_unlink(temp_file2
);
636 /* no pre-processing needed, so temp_file2 = temp_file */
637 g_unlink(temp_file2
);
639 temp_file2
= temp_file
;
642 source_file
= tm_source_file_new(temp_file2
, tm_source_file_get_lang_name(lang
));
643 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
644 if (NULL
== source_file
)
646 g_unlink(temp_file2
);
649 g_unlink(temp_file2
);
651 if (0 == source_file
->tags_array
->len
)
653 tm_source_file_free(source_file
);
656 tags_array
= tm_tags_extract(source_file
->tags_array
, tm_tag_max_t
);
657 if ((NULL
== tags_array
) || (0 == tags_array
->len
))
660 g_ptr_array_free(tags_array
, TRUE
);
661 tm_source_file_free(source_file
);
664 if (FALSE
== tm_tags_sort(tags_array
, global_tags_sort_attrs
, TRUE
, FALSE
))
666 tm_source_file_free(source_file
);
669 if (NULL
== (fp
= g_fopen(tags_file
, "w")))
671 tm_source_file_free(source_file
);
674 fprintf(fp
, "# format=tagmanager\n");
675 for (i
= 0; i
< tags_array
->len
; ++i
)
677 tm_tag_write(TM_TAG(tags_array
->pdata
[i
]), fp
, tm_tag_attr_type_t
678 | tm_tag_attr_scope_t
| tm_tag_attr_arglist_t
| tm_tag_attr_vartype_t
679 | tm_tag_attr_pointer_t
);
682 tm_source_file_free(source_file
);
683 g_ptr_array_free(tags_array
, TRUE
);
688 static void fill_find_tags_array(GPtrArray
*dst
, const GPtrArray
*src
,
689 const char *name
, const char *scope
, TMTagType type
, TMParserType lang
)
694 if (!src
|| !dst
|| !name
|| !*name
)
697 tag
= tm_tags_find(src
, name
, FALSE
, &num
);
698 for (i
= 0; i
< num
; ++i
)
700 if ((type
& (*tag
)->type
) &&
701 tm_tag_langs_compatible(lang
, (*tag
)->lang
) &&
702 (!scope
|| g_strcmp0((*tag
)->scope
, scope
) == 0))
704 g_ptr_array_add(dst
, *tag
);
711 /* Returns all matching tags found in the workspace.
712 @param name The name of the tag to find.
713 @param scope The scope name of the tag to find, or NULL.
714 @param type The tag types to return (TMTagType). Can be a bitmask.
715 @param attrs The attributes to sort and dedup on (0 terminated integer array).
716 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
718 @return Array of matching tags.
720 GPtrArray
*tm_workspace_find(const char *name
, const char *scope
, TMTagType type
,
721 TMTagAttrType
*attrs
, TMParserType lang
)
723 GPtrArray
*tags
= g_ptr_array_new();
725 fill_find_tags_array(tags
, theWorkspace
->tags_array
, name
, scope
, type
, lang
);
726 fill_find_tags_array(tags
, theWorkspace
->global_tags
, name
, scope
, type
, lang
);
729 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
735 static void fill_find_tags_array_prefix(GPtrArray
*dst
, const GPtrArray
*src
,
736 const char *name
, TMParserType lang
, guint max_num
)
738 TMTag
**tag
, *last
= NULL
;
741 if (!src
|| !dst
|| !name
|| !*name
)
745 tag
= tm_tags_find(src
, name
, TRUE
, &count
);
746 for (i
= 0; i
< count
&& num
< max_num
; ++i
)
748 if (tm_tag_langs_compatible(lang
, (*tag
)->lang
) &&
749 !tm_tag_is_anon(*tag
) &&
750 (!last
|| g_strcmp0(last
->name
, (*tag
)->name
) != 0))
752 g_ptr_array_add(dst
, *tag
);
761 /* Returns tags with the specified prefix sorted by name. If there are several
762 tags with the same name, only one of them appears in the resulting array.
763 @param prefix The prefix of the tag to find.
764 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
766 @param max_num The maximum number of tags to return.
767 @return Array of matching tags sorted by their name.
769 GPtrArray
*tm_workspace_find_prefix(const char *prefix
, TMParserType lang
, guint max_num
)
771 TMTagAttrType attrs
[] = { tm_tag_attr_name_t
, 0 };
772 GPtrArray
*tags
= g_ptr_array_new();
774 fill_find_tags_array_prefix(tags
, theWorkspace
->tags_array
, prefix
, lang
, max_num
);
775 fill_find_tags_array_prefix(tags
, theWorkspace
->global_tags
, prefix
, lang
, max_num
);
777 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
778 if (tags
->len
> max_num
)
785 /* Gets all members of type_tag; search them inside the all array.
786 * The namespace parameter determines whether we are performing the "namespace"
787 * search (user has typed something like "A::" where A is a type) or "scope" search
788 * (user has typed "a." where a is a global struct-like variable). With the
789 * namespace search we return all direct descendants of any type while with the
790 * scope search we return only those which can be invoked on a variable (member,
793 find_scope_members_tags (const GPtrArray
*all
, TMTag
*type_tag
, gboolean
namespace)
795 TMTagType member_types
= tm_tag_max_t
& ~(TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
);
796 GPtrArray
*tags
= g_ptr_array_new();
801 member_types
= tm_tag_max_t
;
803 if (type_tag
->scope
&& *(type_tag
->scope
))
804 scope
= g_strconcat(type_tag
->scope
, tm_tag_context_separator(type_tag
->lang
), type_tag
->name
, NULL
);
806 scope
= g_strdup(type_tag
->name
);
808 for (i
= 0; i
< all
->len
; ++i
)
810 TMTag
*tag
= TM_TAG (all
->pdata
[i
]);
812 if (tag
&& (tag
->type
& member_types
) &&
813 tag
->scope
&& tag
->scope
[0] != '\0' &&
814 tm_tag_langs_compatible(tag
->lang
, type_tag
->lang
) &&
815 strcmp(scope
, tag
->scope
) == 0 &&
816 (!namespace || !tm_tag_is_anon(tag
)))
818 g_ptr_array_add (tags
, tag
);
826 g_ptr_array_free(tags
, TRUE
);
834 static gchar
*strip_type(const gchar
*scoped_name
, TMParserType lang
)
836 if (scoped_name
!= NULL
)
838 /* remove scope prefix */
839 const gchar
*sep
= tm_tag_context_separator(lang
);
840 const gchar
*base
= g_strrstr(scoped_name
, sep
);
841 gchar
*name
= base
? g_strdup(base
+ strlen(sep
)) : g_strdup(scoped_name
);
843 /* remove pointers */
844 g_strdelimit(name
, "*^", ' ');
853 /* Gets all members of the type with the given name; search them inside tags_array */
855 find_scope_members (const GPtrArray
*tags_array
, const gchar
*name
, TMSourceFile
*file
,
856 TMParserType lang
, gboolean
namespace)
858 GPtrArray
*res
= NULL
;
862 g_return_val_if_fail(name
&& *name
, NULL
);
864 type_name
= g_strdup(name
);
866 /* Check if type_name is a type that can possibly contain members.
867 * Try to resolve intermediate typedefs to get the real type name. Also
868 * add scope information to the name if applicable.
869 * The loop below loops only when resolving typedefs - avoid possibly infinite
870 * loop when typedefs create a cycle by adding some limits. */
871 for (i
= 0; i
< 5; i
++)
874 GPtrArray
*type_tags
;
875 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
879 types
&= ~tm_tag_enum_t
;
881 type_tags
= g_ptr_array_new();
882 fill_find_tags_array(type_tags
, tags_array
, type_name
, NULL
, types
, lang
);
884 for (j
= 0; j
< type_tags
->len
; j
++)
886 TMTag
*test_tag
= TM_TAG(type_tags
->pdata
[j
]);
888 /* anonymous type defined in a different file than the variable -
889 * this isn't the type we are looking for */
890 if (tm_tag_is_anon(test_tag
) && (file
!= test_tag
->file
|| test_tag
->file
== NULL
))
895 /* prefer non-typedef tags because we can be sure they contain members */
896 if (test_tag
->type
!= tm_tag_typedef_t
)
900 g_ptr_array_free(type_tags
, TRUE
);
902 if (!tag
) /* not a type that can contain members */
905 /* intermediate typedef - resolve to the real type */
906 if (tag
->type
== tm_tag_typedef_t
)
908 if (tag
->var_type
&& tag
->var_type
[0] != '\0')
911 type_name
= strip_type(tag
->var_type
, tag
->lang
);
917 else /* real type with members */
919 /* use the same file as the composite type if file information available */
920 res
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: tags_array
, tag
, namespace);
931 /* Checks whether a member tag is directly accessible from method with method_scope */
932 static gboolean
member_at_method_scope(const GPtrArray
*tags
, const gchar
*method_scope
, TMTag
*member_tag
,
935 const gchar
*sep
= tm_tag_context_separator(lang
);
936 gboolean ret
= FALSE
;
940 /* method scope is in the form ...::class_name::method_name */
941 comps
= g_strsplit (method_scope
, sep
, 0);
942 len
= g_strv_length(comps
);
945 gchar
*method
, *member_scope
, *cls
, *cls_scope
;
947 /* get method/member scope */
948 method
= comps
[len
- 1];
949 comps
[len
- 1] = NULL
;
950 member_scope
= g_strjoinv(sep
, comps
);
951 comps
[len
- 1] = method
;
953 /* get class scope */
954 cls
= comps
[len
- 2];
955 comps
[len
- 2] = NULL
;
956 cls_scope
= g_strjoinv(sep
, comps
);
957 comps
[len
- 2] = cls
;
958 cls_scope
= strlen(cls_scope
) > 0 ? cls_scope
: NULL
;
960 /* check whether member inside the class */
961 if (g_strcmp0(member_tag
->scope
, member_scope
) == 0)
963 const GPtrArray
*src
= member_tag
->file
? member_tag
->file
->tags_array
: tags
;
964 GPtrArray
*cls_tags
= g_ptr_array_new();
966 /* check whether the class exists */
967 fill_find_tags_array(cls_tags
, src
, cls
, cls_scope
, TM_TYPE_WITH_MEMBERS
| tm_tag_namespace_t
, lang
);
968 ret
= cls_tags
->len
> 0;
969 g_ptr_array_free(cls_tags
, TRUE
);
973 g_free(member_scope
);
981 /* For an array of variable/type tags, find members inside the types */
983 find_scope_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, TMParserType lang
,
984 gboolean member
, const gchar
*current_scope
)
986 GPtrArray
*member_tags
= NULL
;
989 /* there may be several variables/types with the same name - try each of them until
990 * we find something */
991 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
993 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
994 TMTagType member_types
= tm_tag_member_t
| tm_tag_field_t
| tm_tag_method_t
;
995 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
997 if (tag
->type
& types
) /* type: namespace search */
999 if (tag
->type
& tm_tag_typedef_t
)
1000 member_tags
= find_scope_members(searched_array
, tag
->name
, tag
->file
, lang
, TRUE
);
1002 member_tags
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: searched_array
,
1005 else if (tag
->var_type
) /* variable: scope search */
1007 /* The question now is whether we should use member tags (such as
1008 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
1009 * (which means user has typed something like foo.bar.) or if we are
1010 * inside a method where foo is a class member, we want scope completion
1012 if (!(tag
->type
& member_types
) || member
||
1013 member_at_method_scope(tags
, current_scope
, tag
, lang
))
1015 gchar
*tag_type
= strip_type(tag
->var_type
, tag
->lang
);
1017 member_tags
= find_scope_members(searched_array
, tag_type
, tag
->file
, lang
, FALSE
);
1027 static GPtrArray
*find_namespace_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, TMParserType lang
)
1029 GPtrArray
*member_tags
= NULL
;
1032 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
1034 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
1036 member_tags
= find_scope_members_tags(searched_array
, tag
, TRUE
);
1043 /* Returns all member tags of a struct/union/class if the provided name is a variable
1044 of such a type or the name of the type.
1045 @param source_file TMSourceFile of the edited source file
1046 @param name Name of the variable/type whose members are searched
1047 @param function TRUE if the name is a name of a function
1048 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1049 @param current_scope The current scope in the editor
1050 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1051 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1053 tm_workspace_find_scope_members (TMSourceFile
*source_file
, const char *name
,
1054 gboolean function
, gboolean member
, const gchar
*current_scope
, gboolean search_namespace
)
1056 TMParserType lang
= source_file
->lang
;
1057 GPtrArray
*tags
, *member_tags
= NULL
;
1058 TMTagType function_types
= tm_tag_function_t
| tm_tag_method_t
|
1059 tm_tag_macro_with_arg_t
| tm_tag_prototype_t
;
1060 TMTagType tag_type
= tm_tag_max_t
&
1061 ~(function_types
| tm_tag_enumerator_t
| tm_tag_namespace_t
| tm_tag_package_t
);
1062 TMTagAttrType sort_attr
[] = {tm_tag_attr_name_t
, 0};
1064 if (search_namespace
)
1066 tags
= tm_workspace_find(name
, NULL
, tm_tag_namespace_t
, NULL
, lang
);
1068 member_tags
= find_namespace_members_all(tags
, theWorkspace
->tags_array
, lang
);
1070 member_tags
= find_namespace_members_all(tags
, theWorkspace
->global_tags
, lang
);
1072 g_ptr_array_free(tags
, TRUE
);
1078 tag_type
= function_types
;
1080 /* tags corresponding to the variable/type name */
1081 tags
= tm_workspace_find(name
, NULL
, tag_type
, NULL
, lang
);
1083 /* Start searching inside the source file, continue with workspace tags and
1084 * end with global tags. This way we find the "closest" tag to the current
1085 * file in case there are more of them. */
1087 member_tags
= find_scope_members_all(tags
, source_file
->tags_array
,
1088 lang
, member
, current_scope
);
1090 member_tags
= find_scope_members_all(tags
, theWorkspace
->tags_array
, lang
,
1091 member
, current_scope
);
1093 member_tags
= find_scope_members_all(tags
, theWorkspace
->global_tags
, lang
,
1094 member
, current_scope
);
1096 g_ptr_array_free(tags
, TRUE
);
1099 tm_tags_dedup(member_tags
, sort_attr
, FALSE
);
1107 /* Dumps the workspace tree - useful for debugging */
1108 void tm_workspace_dump(void)
1113 g_message("Dumping TagManager workspace tree..");
1115 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
1117 TMSourceFile
*source_file
= theWorkspace
->source_files
->pdata
[i
];
1118 fprintf(stderr
, "%s", source_file
->file_name
);
1121 #endif /* TM_DEBUG */
1126 /* Returns a list of parent classes for the given class name
1127 @param name Name of the class
1128 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1129 static const GPtrArray
*tm_workspace_get_parents(const gchar
*name
)
1131 static TMTagAttrType type
[] = { tm_tag_attr_name_t
, tm_tag_attr_none_t
};
1132 static GPtrArray
*parents
= NULL
;
1133 const GPtrArray
*matches
;
1140 g_return_val_if_fail(name
&& isalpha(*name
),NULL
);
1142 if (NULL
== parents
)
1143 parents
= g_ptr_array_new();
1145 g_ptr_array_set_size(parents
, 0);
1146 matches
= tm_workspace_find(name
, NULL
, tm_tag_class_t
, type
, -1);
1147 if ((NULL
== matches
) || (0 == matches
->len
))
1149 g_ptr_array_add(parents
, matches
->pdata
[0]);
1150 while (i
< parents
->len
)
1152 tag
= TM_TAG(parents
->pdata
[i
]);
1153 if ((NULL
!= tag
->inheritance
) && (isalpha(tag
->inheritance
[0])))
1155 klasses
= g_strsplit(tag
->inheritance
, ",", 10);
1156 for (klass
= klasses
; (NULL
!= *klass
); ++ klass
)
1158 for (j
=0; j
< parents
->len
; ++j
)
1160 if (0 == strcmp(*klass
, TM_TAG(parents
->pdata
[j
])->name
))
1163 if (parents
->len
== j
)
1165 matches
= tm_workspace_find(*klass
, NULL
, tm_tag_class_t
, type
, -1);
1166 if ((NULL
!= matches
) && (0 < matches
->len
))
1167 g_ptr_array_add(parents
, matches
->pdata
[0]);
1170 g_strfreev(klasses
);