Ignore local tags for autocompletion in other files
[geany-mirror.git] / src / tagmanager / tm_workspace.c
blob0bc44e6682d90e00af39b0ccc8b07b9cb24e5524
1 /*
3 * Copyright (c) 2001-2002, Biswapesh Chattopadhyay
4 * Copyright 2005 The Geany contributors
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 */
11 /**
12 * @file tm_workspace.h
13 The TMWorkspace structure is meant to be used as a singleton to store application
14 wide tag information.
16 The workspace is intended to contain a list of global tags
17 and a set of individual source files.
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <ctype.h>
24 #include <sys/types.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <glib/gstdio.h>
29 #include "tm_workspace.h"
30 #include "tm_ctags.h"
31 #include "tm_tag.h"
32 #include "tm_parser.h"
35 /* when changing, always keep the three sort criteria below in sync */
36 static TMTagAttrType workspace_tags_sort_attrs[] =
38 tm_tag_attr_name_t, tm_tag_attr_file_t, tm_tag_attr_line_t,
39 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
42 /* for file tags the file is always identical, don't use for sorting */
43 static TMTagAttrType file_tags_sort_attrs[] =
45 tm_tag_attr_name_t, tm_tag_attr_line_t,
46 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
49 /* global tags don't have file/line information */
50 static TMTagAttrType global_tags_sort_attrs[] =
52 tm_tag_attr_name_t,
53 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
56 static TMTagType TM_TYPE_WITH_MEMBERS =
57 tm_tag_class_t | tm_tag_struct_t | tm_tag_union_t |
58 tm_tag_enum_t | tm_tag_interface_t;
60 static TMTagType TM_GLOBAL_TYPE_MASK =
61 tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t |
62 tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t;
64 static TMWorkspace *theWorkspace = NULL;
67 static gboolean tm_create_workspace(void)
69 theWorkspace = g_new(TMWorkspace, 1);
70 theWorkspace->tags_array = g_ptr_array_new();
72 theWorkspace->global_tags = g_ptr_array_new();
73 theWorkspace->source_files = g_ptr_array_new();
74 theWorkspace->typename_array = g_ptr_array_new();
75 theWorkspace->global_typename_array = g_ptr_array_new();
77 tm_ctags_init();
78 tm_parser_verify_type_mappings();
80 return TRUE;
84 /* Frees the workspace structure and all child source files. Use only when
85 exiting from the main program.
87 void tm_workspace_free(void)
89 guint i;
91 #ifdef TM_DEBUG
92 g_message("Workspace destroyed");
93 #endif
95 for (i=0; i < theWorkspace->source_files->len; ++i)
96 tm_source_file_free(theWorkspace->source_files->pdata[i]);
97 g_ptr_array_free(theWorkspace->source_files, TRUE);
98 tm_tags_array_free(theWorkspace->global_tags, TRUE);
99 g_ptr_array_free(theWorkspace->tags_array, TRUE);
100 g_ptr_array_free(theWorkspace->typename_array, TRUE);
101 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
102 g_free(theWorkspace);
103 theWorkspace = NULL;
107 /* Since TMWorkspace is a singleton, you should not create multiple
108 workspaces, but get a pointer to the workspace whenever required. The first
109 time a pointer is requested, or a source file is added to the workspace,
110 a workspace is created. Subsequent calls to the function will return the
111 created workspace.
113 const TMWorkspace *tm_get_workspace(void)
115 if (NULL == theWorkspace)
116 tm_create_workspace();
117 return theWorkspace;
121 static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_array)
123 GPtrArray *new_tags = tm_tags_merge(*big_array, small_array, workspace_tags_sort_attrs, FALSE);
124 /* tags owned by TMSourceFile - free just the pointer array */
125 g_ptr_array_free(*big_array, TRUE);
126 *big_array = new_tags;
130 static void merge_extracted_tags(GPtrArray **dest, GPtrArray *src, TMTagType tag_types)
132 GPtrArray *arr;
134 arr = tm_tags_extract(src, tag_types);
135 tm_workspace_merge_tags(dest, arr);
136 g_ptr_array_free(arr, TRUE);
140 static void update_source_file(TMSourceFile *source_file, guchar* text_buf,
141 gsize buf_size, gboolean use_buffer, gboolean update_workspace)
143 #ifdef TM_DEBUG
144 g_message("Source file updating based on source file %s", source_file->file_name);
145 #endif
147 if (update_workspace)
149 /* tm_source_file_parse() deletes the tag objects - remove the tags from
150 * workspace while they exist and can be scanned */
151 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
152 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
154 tm_source_file_parse(source_file, text_buf, buf_size, use_buffer);
155 tm_tags_sort(source_file->tags_array, file_tags_sort_attrs, FALSE, TRUE);
156 if (update_workspace)
158 #ifdef TM_DEBUG
159 g_message("Updating workspace from source file");
160 #endif
161 tm_workspace_merge_tags(&theWorkspace->tags_array, source_file->tags_array);
163 merge_extracted_tags(&(theWorkspace->typename_array), source_file->tags_array, TM_GLOBAL_TYPE_MASK);
165 #ifdef TM_DEBUG
166 else
167 g_message("Skipping workspace update because update_workspace is %s",
168 update_workspace?"TRUE":"FALSE");
170 #endif
174 /** Adds a source file to the workspace, parses it and updates the workspace tags.
175 @param source_file The source file to add to the workspace.
177 GEANY_API_SYMBOL
178 void tm_workspace_add_source_file(TMSourceFile *source_file)
180 g_return_if_fail(source_file != NULL);
182 g_ptr_array_add(theWorkspace->source_files, source_file);
183 update_source_file(source_file, NULL, 0, FALSE, TRUE);
187 void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file)
189 g_return_if_fail(source_file != NULL);
191 g_ptr_array_add(theWorkspace->source_files, source_file);
195 /* Updates the source file by reparsing the text-buffer passed as parameter.
196 Ctags will use a parsing based on buffer instead of on files.
197 You should call this function when you don't want a previous saving of the file
198 you're editing. It's useful for a "real-time" updating of the tags.
199 The tags array and the tags themselves are destroyed and re-created, hence any
200 other tag arrays pointing to these tags should be rebuilt as well. All sorting
201 information is also lost.
202 @param source_file The source file to update with a buffer.
203 @param text_buf A text buffer. The user should take care of allocate and free it after
204 the use here.
205 @param buf_size The size of text_buf.
207 void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* text_buf,
208 gsize buf_size)
210 update_source_file(source_file, text_buf, buf_size, TRUE, TRUE);
214 /** Removes a source file from the workspace if it exists. This function also removes
215 the tags belonging to this file from the workspace. To completely free the TMSourceFile
216 pointer call tm_source_file_free() on it.
217 @param source_file Pointer to the source file to be removed.
219 GEANY_API_SYMBOL
220 void tm_workspace_remove_source_file(TMSourceFile *source_file)
222 guint i;
224 g_return_if_fail(source_file != NULL);
226 for (i=0; i < theWorkspace->source_files->len; ++i)
228 if (theWorkspace->source_files->pdata[i] == source_file)
230 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
231 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
232 g_ptr_array_remove_index_fast(theWorkspace->source_files, i);
233 return;
239 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
240 want to globally refresh the workspace. This function does not call tm_source_file_update()
241 which should be called before this function on source files which need to be
242 reparsed.
244 static void tm_workspace_update(void)
246 guint i, j;
247 TMSourceFile *source_file;
249 #ifdef TM_DEBUG
250 g_message("Recreating workspace tags array");
251 #endif
253 g_ptr_array_set_size(theWorkspace->tags_array, 0);
255 #ifdef TM_DEBUG
256 g_message("Total %d objects", theWorkspace->source_files->len);
257 #endif
258 for (i=0; i < theWorkspace->source_files->len; ++i)
260 source_file = theWorkspace->source_files->pdata[i];
261 #ifdef TM_DEBUG
262 g_message("Adding tags of %s", source_file->file_name);
263 #endif
264 if (source_file->tags_array->len > 0)
266 for (j = 0; j < source_file->tags_array->len; ++j)
268 g_ptr_array_add(theWorkspace->tags_array,
269 source_file->tags_array->pdata[j]);
273 #ifdef TM_DEBUG
274 g_message("Total: %d tags", theWorkspace->tags_array->len);
275 #endif
276 tm_tags_sort(theWorkspace->tags_array, workspace_tags_sort_attrs, TRUE, FALSE);
278 g_ptr_array_free(theWorkspace->typename_array, TRUE);
279 theWorkspace->typename_array = tm_tags_extract(theWorkspace->tags_array, TM_GLOBAL_TYPE_MASK);
283 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
284 This is more efficient than calling tm_workspace_add_source_file() and
285 tm_workspace_update_source_file() separately for each of the files.
286 @param source_files @elementtype{TMSourceFile} The source files to be added to the workspace.
288 GEANY_API_SYMBOL
289 void tm_workspace_add_source_files(GPtrArray *source_files)
291 guint i;
293 g_return_if_fail(source_files != NULL);
295 for (i = 0; i < source_files->len; i++)
297 TMSourceFile *source_file = source_files->pdata[i];
299 tm_workspace_add_source_file_noupdate(source_file);
300 update_source_file(source_file, NULL, 0, FALSE, FALSE);
303 tm_workspace_update();
307 /** Removes multiple source files from the workspace and updates the workspace tag
308 arrays. This is more efficient than calling tm_workspace_remove_source_file()
309 separately for each of the files. To completely free the TMSourceFile pointers
310 call tm_source_file_free() on each of them.
311 @param source_files @elementtype{TMSourceFile} The source files to be removed from the workspace.
313 GEANY_API_SYMBOL
314 void tm_workspace_remove_source_files(GPtrArray *source_files)
316 guint i, j;
318 g_return_if_fail(source_files != NULL);
320 //TODO: sort both arrays by pointer value and remove in single pass
321 for (i = 0; i < source_files->len; i++)
323 TMSourceFile *source_file = source_files->pdata[i];
325 for (j = 0; j < theWorkspace->source_files->len; j++)
327 if (theWorkspace->source_files->pdata[j] == source_file)
329 g_ptr_array_remove_index_fast(theWorkspace->source_files, j);
330 break;
335 tm_workspace_update();
339 /* Loads the global tag list from the specified file. The global tag list should
340 have been first created using tm_workspace_create_global_tags().
341 @param tags_file The file containing global tags.
342 @return TRUE on success, FALSE on failure.
343 @see tm_workspace_create_global_tags()
345 gboolean tm_workspace_load_global_tags(const char *tags_file, TMParserType mode)
347 GPtrArray *file_tags, *new_tags;
349 file_tags = tm_source_file_read_tags_file(tags_file, mode);
350 if (!file_tags)
351 return FALSE;
353 tm_tags_sort(file_tags, global_tags_sort_attrs, TRUE, TRUE);
355 /* reorder the whole array, because tm_tags_find expects a sorted array */
356 new_tags = tm_tags_merge(theWorkspace->global_tags,
357 file_tags, global_tags_sort_attrs, TRUE);
358 g_ptr_array_free(theWorkspace->global_tags, TRUE);
359 g_ptr_array_free(file_tags, TRUE);
360 theWorkspace->global_tags = new_tags;
362 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
363 theWorkspace->global_typename_array = tm_tags_extract(new_tags, TM_GLOBAL_TYPE_MASK);
365 return TRUE;
369 static gboolean write_includes_file(const gchar *outf, GList *includes_files)
371 FILE *fp = g_fopen(outf, "w");
372 GList *node = includes_files;
374 if (!fp)
375 return FALSE;
377 while (node)
379 char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data);
380 size_t str_len = strlen(str);
382 fwrite(str, str_len, 1, fp);
383 g_free(str);
384 node = g_list_next(node);
387 return fclose(fp) == 0;
391 static gboolean combine_include_files(const gchar *outf, GList *file_list)
393 FILE *fp = g_fopen(outf, "w");
394 GList *node = file_list;
396 if (!fp)
397 return FALSE;
399 while (node)
401 const char *fname = node->data;
402 char *contents;
403 size_t length;
404 GError *err = NULL;
406 if (! g_file_get_contents(fname, &contents, &length, &err))
408 fprintf(stderr, "Unable to read file: %s\n", err->message);
409 g_error_free(err);
411 else
413 fwrite(contents, length, 1, fp);
414 fwrite("\n", 1, 1, fp); /* in case file doesn't end in newline (e.g. windows). */
415 g_free(contents);
417 node = g_list_next (node);
420 return fclose(fp) == 0;
424 static gchar *create_temp_file(const gchar *tpl)
426 gchar *name;
427 gint fd;
429 fd = g_file_open_tmp(tpl, &name, NULL);
430 if (fd < 0)
431 name = NULL;
432 else
433 close(fd);
435 return name;
438 static GList *lookup_includes(const gchar **includes, gint includes_count)
440 GList *includes_files = NULL;
441 GHashTable *table; /* used for deduping */
442 gint i;
444 table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
446 for (i = 0; i < includes_count; i++)
448 if (!g_hash_table_lookup(table, includes[i]))
450 gchar* file_name_copy = g_strdup(includes[i]);
452 includes_files = g_list_prepend(includes_files, file_name_copy);
453 g_hash_table_insert(table, file_name_copy, file_name_copy);
457 g_hash_table_destroy(table);
459 return g_list_reverse(includes_files);
462 static gchar *pre_process_file(const gchar *cmd, const gchar *inf)
464 gint ret;
465 gchar *outf = create_temp_file("tmp_XXXXXX.cpp");
466 gchar *tmp_errfile;
467 gchar *errors = NULL;
468 gchar *command;
470 if (!outf)
471 return NULL;
473 tmp_errfile = create_temp_file("tmp_XXXXXX");
474 if (!tmp_errfile)
476 g_unlink(outf);
477 g_free(outf);
478 return NULL;
481 command = g_strdup_printf("%s %s >%s 2>%s",
482 cmd, inf, outf, tmp_errfile);
483 #ifdef TM_DEBUG
484 g_message("Executing: %s", command);
485 #endif
486 ret = system(command);
487 g_free(command);
489 g_file_get_contents(tmp_errfile, &errors, NULL, NULL);
490 if (errors && *errors)
491 g_printerr("%s\n", errors);
492 g_free(errors);
493 g_unlink(tmp_errfile);
494 g_free(tmp_errfile);
496 if (ret == -1)
498 g_unlink(outf);
499 g_free(outf);
500 return NULL;
503 return outf;
506 /* Creates a list of global tags. Ideally, this should be created once during
507 installations so that all users can use the same file. This is because a full
508 scale global tag list can occupy several megabytes of disk space.
509 @param pre_process The pre-processing command. This is executed via system(),
510 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
511 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
512 are allowed.
513 @param tags_file The file where the tags will be stored.
514 @param lang The language to use for the tags file.
515 @return TRUE on success, FALSE on failure.
517 gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes,
518 int includes_count, const char *tags_file, TMParserType lang)
520 gboolean ret = FALSE;
521 TMSourceFile *source_file;
522 GList *includes_files;
523 gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
524 GPtrArray *filtered_tags;
526 if (!temp_file)
527 return FALSE;
529 includes_files = lookup_includes(includes, includes_count);
531 #ifdef TM_DEBUG
532 g_message ("writing out files to %s\n", temp_file);
533 #endif
534 if (pre_process)
535 ret = write_includes_file(temp_file, includes_files);
536 else
537 ret = combine_include_files(temp_file, includes_files);
539 g_list_free_full(includes_files, g_free);
540 if (!ret)
541 goto cleanup;
542 ret = FALSE;
544 if (pre_process)
546 gchar *temp_file2 = pre_process_file(pre_process, temp_file);
548 if (temp_file2)
550 g_unlink(temp_file);
551 g_free(temp_file);
552 temp_file = temp_file2;
554 else
555 goto cleanup;
558 source_file = tm_source_file_new(temp_file, tm_source_file_get_lang_name(lang));
559 if (!source_file)
560 goto cleanup;
561 update_source_file(source_file, NULL, 0, FALSE, FALSE);
562 if (source_file->tags_array->len == 0)
564 tm_source_file_free(source_file);
565 goto cleanup;
568 tm_tags_sort(source_file->tags_array, global_tags_sort_attrs, TRUE, FALSE);
569 filtered_tags = tm_tags_extract(source_file->tags_array, ~tm_tag_local_var_t);
570 ret = tm_source_file_write_tags_file(tags_file, filtered_tags);
571 g_ptr_array_free(filtered_tags, TRUE);
572 tm_source_file_free(source_file);
574 cleanup:
575 g_unlink(temp_file);
576 g_free(temp_file);
577 return ret;
581 static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
582 const char *name, const char *scope, TMTagType type, TMParserType lang)
584 TMTag **tag;
585 guint i, num;
587 if (!src || !dst || !name || !*name)
588 return;
590 tag = tm_tags_find(src, name, FALSE, &num);
591 for (i = 0; i < num; ++i)
593 if ((type & (*tag)->type) &&
594 tm_parser_langs_compatible(lang, (*tag)->lang) &&
595 (!scope || g_strcmp0((*tag)->scope, scope) == 0))
597 g_ptr_array_add(dst, *tag);
599 tag++;
604 /* Returns all matching tags found in the workspace.
605 @param name The name of the tag to find.
606 @param scope The scope name of the tag to find, or NULL.
607 @param type The tag types to return (TMTagType). Can be a bitmask.
608 @param attrs The attributes to sort and dedup on (0 terminated integer array).
609 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
610 -1 for all
611 @return Array of matching tags.
613 GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
614 TMTagAttrType *attrs, TMParserType lang)
616 GPtrArray *tags = g_ptr_array_new();
618 fill_find_tags_array(tags, theWorkspace->tags_array, name, scope, type, lang);
619 fill_find_tags_array(tags, theWorkspace->global_tags, name, scope, type, lang);
621 if (attrs)
622 tm_tags_sort(tags, attrs, TRUE, FALSE);
624 return tags;
628 gboolean tm_workspace_is_autocomplete_tag(TMTag *tag,
629 TMSourceFile *current_file,
630 guint current_line,
631 const gchar *current_scope)
633 TMParserType lang = current_file ? current_file->lang : TM_PARSER_NONE;
635 /* ignore local variables from other files/functions or after current line */
636 gboolean valid = !(tag->type & tm_tag_local_var_t) ||
637 (current_file == tag->file &&
638 current_line >= tag->line &&
639 g_strcmp0(current_scope, tag->scope) == 0);
641 /* tag->local indicates per-file-only visibility such as static C functions */
642 gboolean valid_local = !tag->local || current_file == tag->file;
644 return valid && valid_local &&
645 !tm_tag_is_anon(tag) && tm_parser_langs_compatible(lang, tag->lang);
649 typedef struct
651 TMSourceFile *file;
652 guint line;
653 const gchar *scope;
654 } CopyInfo;
656 static gboolean is_any_tag(TMTag *tag, CopyInfo *info)
658 return TRUE;
661 static gboolean is_local_tag(TMTag *tag, CopyInfo *info)
663 return tag->type & tm_tag_local_var_t;
666 static gboolean is_non_local_tag(TMTag *tag, CopyInfo *info)
668 return !is_local_tag(tag, info);
671 /* non-local tag not from current file */
672 static gboolean is_workspace_tag(TMTag *tag, CopyInfo *info)
674 return tag->file != info->file &&
675 is_non_local_tag(tag, info);
679 static guint copy_tags(GPtrArray *dst, TMTag **src, guint src_len, GHashTable *name_table,
680 gint num, gboolean (*predicate) (TMTag *, CopyInfo *), CopyInfo *info)
682 guint i;
684 g_return_val_if_fail(src && dst, 0);
686 for (i = 0; i < src_len && num > 0; i++)
688 TMTag *tag = *src;
689 if (predicate(tag, info) &&
690 tm_workspace_is_autocomplete_tag(tag, info->file, info->line, info->scope) &&
691 !g_hash_table_contains(name_table, tag->name))
693 g_ptr_array_add(dst, tag);
694 g_hash_table_add(name_table, tag->name);
695 num--;
697 src++;
702 static void fill_find_tags_array_prefix(GPtrArray *dst, const char *name,
703 CopyInfo *info, guint max_num)
705 TMTag **found;
706 guint count;
707 GHashTable *name_table;
709 if (!dst || !name || !*name)
710 return;
712 name_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
714 if (info->file)
716 found = tm_tags_find(info->file->tags_array, name, TRUE, &count);
717 if (found)
719 copy_tags(dst, found, count, name_table, max_num - dst->len, is_local_tag, info);
720 if (dst->len < max_num)
721 copy_tags(dst, found, count, name_table, max_num - dst->len, is_non_local_tag, info);
724 if (dst->len < max_num)
726 found = tm_tags_find(theWorkspace->tags_array, name, TRUE, &count);
727 if (found)
728 copy_tags(dst, found, count, name_table, max_num - dst->len, is_workspace_tag, info);
730 if (dst->len < max_num)
732 found = tm_tags_find(theWorkspace->global_tags, name, TRUE, &count);
733 if (found)
734 copy_tags(dst, found, count, name_table, max_num - dst->len, is_any_tag, info);
737 g_hash_table_unref(name_table);
741 typedef struct
743 TMSourceFile *file;
744 gboolean sort_by_name;
745 } SortInfo;
748 static gint sort_found_tags(gconstpointer a, gconstpointer b, gpointer user_data)
750 SortInfo *info = user_data;
751 const TMTag *t1 = *((TMTag **) a);
752 const TMTag *t2 = *((TMTag **) b);
754 /* sort local vars first (with highest line number first), followed
755 * by tags from current file, followed by workspace tags, followed by
756 * global tags */
757 if (t1->type & tm_tag_local_var_t && t2->type & tm_tag_local_var_t)
758 return info->sort_by_name ? g_strcmp0(t1->name, t2->name) : t2->line - t1->line;
759 else if (t1->type & tm_tag_local_var_t)
760 return -1;
761 else if (t2->type & tm_tag_local_var_t)
762 return 1;
763 else if (t1->file == info->file && t2->file != info->file)
764 return -1;
765 else if (t2->file == info->file && t1->file != info->file)
766 return 1;
767 else if (t1->file && !t2->file)
768 return -1;
769 else if (t2->file && !t1->file)
770 return 1;
771 return g_strcmp0(t1->name, t2->name);
775 /* Returns tags with the specified prefix sorted by name, ignoring local
776 variables from other files/functions or after current line. 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 tm_parsers.h) of the tags to be found,
780 -1 for all.
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,
785 TMSourceFile *current_file,
786 guint current_line,
787 const gchar *current_scope,
788 guint max_num)
790 TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
791 GPtrArray *tags = g_ptr_array_new();
792 SortInfo sort_info;
793 CopyInfo copy_info;
795 copy_info.file = current_file;
796 copy_info.line = current_line;
797 copy_info.scope = current_scope;
798 fill_find_tags_array_prefix(tags, prefix, &copy_info, max_num);
800 /* sort based on how "close" the tag is to current line with local
801 * variables first */
802 sort_info.file = current_file;
803 sort_info.sort_by_name = TRUE;
804 g_ptr_array_sort_with_data(tags, sort_found_tags, &sort_info);
806 return tags;
810 static gboolean replace_with_char(gchar *haystack, const gchar *needle, char replacement)
812 gchar *pos = strstr(haystack, needle);
813 if (pos)
815 while (*needle)
817 *pos = replacement;
818 needle++;
819 pos++;
821 return TRUE;
823 return FALSE;
827 static gboolean replace_parens_with_char(gchar *haystack, gchar paren_begin, gchar paren_end, char replacement)
829 gchar needle[2] = {paren_begin, '\0'};
830 gchar *pos = strstr(haystack, needle);
831 gint nesting = 0;
833 if (pos)
835 while (*pos)
837 if (*pos == paren_begin)
838 nesting++;
839 else if (*pos == paren_end)
840 nesting--;
841 *pos = replacement;
842 if (nesting == 0)
843 break;
844 pos++;
846 return TRUE;
848 return FALSE;
852 static gchar *strip_type(const gchar *scoped_name, TMParserType lang, gboolean remove_scope)
854 if (scoped_name != NULL)
856 const gchar *sep = tm_parser_scope_separator(lang);
857 gchar *name = g_strdup(scoped_name);
858 gchar *scope_suffix;
860 /* remove pointers, parens and keywords appearing in types */
861 g_strdelimit(name, "*^&", ' ');
862 while (replace_parens_with_char(name, '[', ']', ' ')) {}
863 while (replace_parens_with_char(name, '<', '>', ' ')) {}
864 while (replace_with_char(name, "const ", ' ')) {}
865 while (replace_with_char(name, " const", ' ')) {}
866 while (replace_with_char(name, " struct", ' ')) {}
867 /* remove everything before final scope separator */
868 if (remove_scope && (scope_suffix = g_strrstr(name, sep)))
870 scope_suffix += strlen(sep);
871 scope_suffix = g_strdup(scope_suffix);
872 g_free(name);
873 name = scope_suffix;
875 g_strstrip(name);
877 return name;
879 return NULL;
883 /* Gets all members of type_tag; search them inside the all array.
884 * The namespace parameter determines whether we are performing the "namespace"
885 * search (user has typed something like "A::" where A is a type) or "scope" search
886 * (user has typed "a." where a is a global struct-like variable). With the
887 * namespace search we return all direct descendants of any type while with the
888 * scope search we return only those which can be invoked on a variable (member,
889 * method, etc.). */
890 static GPtrArray *
891 find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace, guint depth)
893 TMTagType member_types = tm_tag_max_t & ~(TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t);
894 GPtrArray *tags;
895 gchar *scope;
896 guint i;
898 if (depth == 10)
899 return NULL; /* simple inheritence cycle avoidance */
901 tags = g_ptr_array_new();
903 if (namespace)
904 member_types = tm_tag_max_t;
906 if (type_tag->scope && *(type_tag->scope))
907 scope = g_strconcat(type_tag->scope, tm_parser_scope_separator(type_tag->lang), type_tag->name, NULL);
908 else
909 scope = g_strdup(type_tag->name);
911 for (i = 0; i < all->len; ++i)
913 TMTag *tag = TM_TAG (all->pdata[i]);
915 if (tag && (tag->type & member_types) &&
916 tag->scope && tag->scope[0] != '\0' &&
917 tm_parser_langs_compatible(tag->lang, type_tag->lang) &&
918 strcmp(scope, tag->scope) == 0 &&
919 (!namespace || !tm_tag_is_anon(tag)))
921 g_ptr_array_add (tags, tag);
925 /* add members from parent classes */
926 if (!namespace && (type_tag->type & (tm_tag_class_t | tm_tag_struct_t)) &&
927 type_tag->inheritance && *type_tag->inheritance)
929 gchar *stripped = strip_type(type_tag->inheritance, type_tag->lang, FALSE);
930 gchar **split_strv = g_strsplit(stripped, ",", -1); /* parent classes */
931 const gchar *parent;
933 g_free(stripped);
935 for (i = 0; parent = split_strv[i]; i++)
937 GPtrArray *parent_tags;
939 stripped = strip_type(parent, type_tag->lang, TRUE);
940 parent_tags = tm_workspace_find(stripped, NULL, tm_tag_class_t | tm_tag_struct_t,
941 NULL, type_tag->lang);
943 if (parent_tags->len > 0)
945 TMTag *parent_tag = parent_tags->pdata[0];
946 GPtrArray *parent_members = find_scope_members_tags(
947 parent_tag->file ? parent_tag->file->tags_array : all, parent_tag,
948 FALSE, depth + 1);
950 if (parent_members)
952 guint j;
953 for (j = 0; j < parent_members->len; j++)
954 g_ptr_array_add (tags, parent_members->pdata[j]);
955 g_ptr_array_free(parent_members, TRUE);
959 g_ptr_array_free(parent_tags, TRUE);
960 g_free(stripped);
963 g_strfreev(split_strv);
966 g_free(scope);
968 if (tags->len == 0)
970 g_ptr_array_free(tags, TRUE);
971 return NULL;
974 if (depth == 0)
976 TMTagAttrType sort_attrs[] = {tm_tag_attr_name_t, 0};
977 tm_tags_sort(tags, sort_attrs, TRUE, FALSE);
980 return tags;
984 /* Gets all members of the type with the given name; search them inside tags_array */
985 static GPtrArray *
986 find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile *file,
987 TMParserType lang, gboolean namespace)
989 GPtrArray *res = NULL;
990 gchar *type_name;
991 guint i;
993 g_return_val_if_fail(name && *name, NULL);
995 type_name = g_strdup(name);
997 /* Check if type_name is a type that can possibly contain members.
998 * Try to resolve intermediate typedefs to get the real type name. Also
999 * add scope information to the name if applicable.
1000 * The loop below loops only when resolving typedefs - avoid possibly infinite
1001 * loop when typedefs create a cycle by adding some limits. */
1002 for (i = 0; i < 5; i++)
1004 guint j;
1005 GPtrArray *type_tags;
1006 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
1007 TMTag *tag = NULL;
1009 if (!namespace)
1010 types &= ~tm_tag_enum_t;
1012 type_tags = g_ptr_array_new();
1013 fill_find_tags_array(type_tags, tags_array, type_name, NULL, types, lang);
1015 for (j = 0; j < type_tags->len; j++)
1017 TMTag *test_tag = TM_TAG(type_tags->pdata[j]);
1019 /* anonymous type defined in a different file than the variable -
1020 * this isn't the type we are looking for */
1021 if (tm_tag_is_anon(test_tag) && (file != test_tag->file || test_tag->file == NULL))
1022 continue;
1024 tag = test_tag;
1026 /* prefer non-typedef tags because we can be sure they contain members */
1027 if (test_tag->type != tm_tag_typedef_t)
1028 break;
1031 g_ptr_array_free(type_tags, TRUE);
1033 if (!tag) /* not a type that can contain members */
1034 break;
1036 /* intermediate typedef - resolve to the real type */
1037 if (tag->type == tm_tag_typedef_t)
1039 if (tag->var_type && tag->var_type[0] != '\0')
1041 g_free(type_name);
1042 type_name = strip_type(tag->var_type, tag->lang, TRUE);
1043 file = tag->file;
1044 continue;
1046 break;
1048 else /* real type with members */
1050 /* use the same file as the composite type if file information available */
1051 res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace, 0);
1052 break;
1056 g_free(type_name);
1058 return res;
1062 /* Checks whether a member tag is directly accessible from method */
1063 static gboolean member_accessible(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag,
1064 TMParserType lang)
1066 const gchar *sep = tm_parser_scope_separator(lang);
1067 gboolean ret = FALSE;
1068 gchar **comps;
1069 guint len;
1071 /* method scope is in the form ...::class_name::method_name */
1072 comps = g_strsplit (method_scope, sep, 0);
1073 len = g_strv_length(comps);
1074 if (len > 1)
1076 gchar *cls = comps[len - 2];
1078 if (*cls)
1080 /* find method's class members */
1081 GPtrArray *cls_tags = find_scope_members(tags, cls, NULL, lang, FALSE);
1083 if (cls_tags)
1085 guint i;
1087 /* check if one of the class members is member_tag */
1088 for (i = 0; i < cls_tags->len; i++)
1090 TMTag *t = cls_tags->pdata[i];
1092 if (t == member_tag)
1094 ret = TRUE;
1095 break;
1098 g_ptr_array_free(cls_tags, TRUE);
1103 g_strfreev(comps);
1104 return ret;
1108 /* For an array of variable/type tags, find members inside the types */
1109 static GPtrArray *
1110 find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang,
1111 gboolean member, const gchar *current_scope)
1113 GPtrArray *member_tags = NULL;
1114 guint i;
1116 /* there may be several variables/types with the same name - try each of them until
1117 * we find something */
1118 for (i = 0; i < tags->len && !member_tags; i++)
1120 TMTag *tag = TM_TAG(tags->pdata[i]);
1121 TMTagType member_types = tm_tag_member_t | tm_tag_field_t | tm_tag_method_t;
1122 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
1124 if (tag->type & types) /* type: namespace search */
1126 if (tag->type & tm_tag_typedef_t)
1127 member_tags = find_scope_members(searched_array, tag->name, tag->file, lang, TRUE);
1128 else
1129 member_tags = find_scope_members_tags(tag->file ? tag->file->tags_array : searched_array,
1130 tag, TRUE, 0);
1132 else if (tag->var_type) /* variable: scope search */
1134 /* The question now is whether we should use member tags (such as
1135 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
1136 * (which means user has typed something like foo.bar.) or if we are
1137 * inside a method where foo is a class member, we want scope completion
1138 * for foo. */
1139 if (!(tag->type & member_types) || member ||
1140 member_accessible(searched_array, current_scope, tag, lang))
1142 gchar *tag_type = strip_type(tag->var_type, tag->lang, TRUE);
1144 member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE);
1145 g_free(tag_type);
1150 return member_tags;
1154 static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array)
1156 GPtrArray *member_tags = NULL;
1157 guint i;
1159 for (i = 0; i < tags->len && !member_tags; i++)
1161 TMTag *tag = TM_TAG(tags->pdata[i]);
1163 member_tags = find_scope_members_tags(searched_array, tag, TRUE, 0);
1166 return member_tags;
1170 /* Returns all member tags of a struct/union/class if the provided name is a variable
1171 of such a type or the name of the type.
1172 @param source_file TMSourceFile of the edited source file
1173 @param name Name of the variable/type whose members are searched
1174 @param function TRUE if the name is a name of a function
1175 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1176 @param current_scope The current scope in the editor
1177 @param current_line The current line in the editor
1178 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1179 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1180 GPtrArray *
1181 tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
1182 gboolean function, gboolean member, const gchar *current_scope, guint current_line,
1183 gboolean search_namespace)
1185 TMParserType lang = source_file ? source_file->lang : TM_PARSER_NONE;
1186 GPtrArray *tags, *member_tags = NULL;
1187 TMTagType function_types = tm_tag_function_t | tm_tag_method_t |
1188 tm_tag_macro_with_arg_t | tm_tag_prototype_t;
1189 TMTagType tag_type = tm_tag_max_t &
1190 ~(function_types | tm_tag_enumerator_t | tm_tag_namespace_t | tm_tag_package_t);
1191 TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0};
1193 if (search_namespace)
1195 tags = tm_workspace_find(name, NULL, tm_tag_namespace_t, NULL, lang);
1197 member_tags = find_namespace_members_all(tags, theWorkspace->tags_array);
1198 if (!member_tags)
1199 member_tags = find_namespace_members_all(tags, theWorkspace->global_tags);
1201 g_ptr_array_free(tags, TRUE);
1204 if (!member_tags)
1206 SortInfo info;
1207 guint i;
1209 if (function)
1210 tag_type = function_types;
1212 /* tags corresponding to the variable/type name */
1213 tags = tm_workspace_find(name, NULL, tag_type, NULL, lang);
1215 /* remove invalid local tags and sort tags so "nearest" tags are first */
1216 for (i = 0; i < tags->len; i++)
1218 TMTag *tag = tags->pdata[i];
1219 if (!tm_workspace_is_autocomplete_tag(tag, source_file, current_line, current_scope))
1220 tags->pdata[i] = NULL;
1222 tm_tags_prune(tags);
1224 info.file = source_file;
1225 info.sort_by_name = FALSE;
1226 g_ptr_array_sort_with_data(tags, sort_found_tags, &info);
1228 /* Start searching inside the source file, continue with workspace tags and
1229 * end with global tags. This way we find the "closest" tag to the current
1230 * file in case there are more of them. */
1231 if (source_file)
1232 member_tags = find_scope_members_all(tags, source_file->tags_array,
1233 lang, member, current_scope);
1234 if (!member_tags)
1235 member_tags = find_scope_members_all(tags, theWorkspace->tags_array, lang,
1236 member, current_scope);
1237 if (!member_tags)
1238 member_tags = find_scope_members_all(tags, theWorkspace->global_tags, lang,
1239 member, current_scope);
1241 g_ptr_array_free(tags, TRUE);
1244 if (member_tags)
1245 tm_tags_dedup(member_tags, sort_attr, FALSE);
1247 return member_tags;
1251 #ifdef TM_DEBUG
1253 /* Dumps the workspace tree - useful for debugging */
1254 void tm_workspace_dump(void)
1256 guint i;
1258 #ifdef TM_DEBUG
1259 g_message("Dumping TagManager workspace tree..");
1260 #endif
1261 for (i=0; i < theWorkspace->source_files->len; ++i)
1263 TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
1264 fprintf(stderr, "%s", source_file->file_name);
1267 #endif /* TM_DEBUG */