"Fix" some GCC -Wparentheses warnings
[geany-mirror.git] / src / tagmanager / tm_workspace.c
blob204f286e98d425acd874a972c3c84f037b8e9611
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 void free_ptr_array(gpointer arr)
69 g_ptr_array_free(arr, TRUE);
73 static gboolean tm_create_workspace(void)
75 theWorkspace = g_new(TMWorkspace, 1);
76 theWorkspace->tags_array = g_ptr_array_new();
78 theWorkspace->global_tags = g_ptr_array_new();
79 theWorkspace->source_files = g_ptr_array_new();
80 theWorkspace->typename_array = g_ptr_array_new();
81 theWorkspace->global_typename_array = g_ptr_array_new();
82 theWorkspace->source_file_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
83 free_ptr_array);
85 tm_ctags_init();
86 tm_parser_verify_type_mappings();
88 return TRUE;
92 /* Frees the workspace structure and all child source files. Use only when
93 exiting from the main program.
95 void tm_workspace_free(void)
97 guint i;
99 #ifdef TM_DEBUG
100 g_message("Workspace destroyed");
101 #endif
103 g_hash_table_destroy(theWorkspace->source_file_map);
104 for (i=0; i < theWorkspace->source_files->len; ++i)
105 tm_source_file_free(theWorkspace->source_files->pdata[i]);
106 g_ptr_array_free(theWorkspace->source_files, TRUE);
107 tm_tags_array_free(theWorkspace->global_tags, TRUE);
108 g_ptr_array_free(theWorkspace->tags_array, TRUE);
109 g_ptr_array_free(theWorkspace->typename_array, TRUE);
110 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
111 g_free(theWorkspace);
112 theWorkspace = NULL;
116 /* Since TMWorkspace is a singleton, you should not create multiple
117 workspaces, but get a pointer to the workspace whenever required. The first
118 time a pointer is requested, or a source file is added to the workspace,
119 a workspace is created. Subsequent calls to the function will return the
120 created workspace.
122 const TMWorkspace *tm_get_workspace(void)
124 if (NULL == theWorkspace)
125 tm_create_workspace();
126 return theWorkspace;
130 static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_array)
132 GPtrArray *new_tags = tm_tags_merge(*big_array, small_array, workspace_tags_sort_attrs, FALSE);
133 /* tags owned by TMSourceFile - free just the pointer array */
134 g_ptr_array_free(*big_array, TRUE);
135 *big_array = new_tags;
139 static void merge_extracted_tags(GPtrArray **dest, GPtrArray *src, TMTagType tag_types)
141 GPtrArray *arr;
143 arr = tm_tags_extract(src, tag_types);
144 tm_workspace_merge_tags(dest, arr);
145 g_ptr_array_free(arr, TRUE);
149 static void update_source_file(TMSourceFile *source_file, guchar* text_buf,
150 gsize buf_size, gboolean use_buffer, gboolean update_workspace)
152 #ifdef TM_DEBUG
153 g_message("Source file updating based on source file %s", source_file->file_name);
154 #endif
156 if (update_workspace)
158 /* tm_source_file_parse() deletes the tag objects - remove the tags from
159 * workspace while they exist and can be scanned */
160 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
161 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
163 tm_source_file_parse(source_file, text_buf, buf_size, use_buffer);
164 tm_tags_sort(source_file->tags_array, file_tags_sort_attrs, FALSE, TRUE);
165 if (update_workspace)
167 #ifdef TM_DEBUG
168 g_message("Updating workspace from source file");
169 #endif
170 tm_workspace_merge_tags(&theWorkspace->tags_array, source_file->tags_array);
172 merge_extracted_tags(&(theWorkspace->typename_array), source_file->tags_array, TM_GLOBAL_TYPE_MASK);
174 #ifdef TM_DEBUG
175 else
176 g_message("Skipping workspace update because update_workspace is %s",
177 update_workspace?"TRUE":"FALSE");
179 #endif
183 void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file)
185 GPtrArray *file_arr;
187 g_return_if_fail(source_file != NULL);
189 g_ptr_array_add(theWorkspace->source_files, source_file);
191 file_arr = g_hash_table_lookup(theWorkspace->source_file_map, source_file->short_name);
192 if (!file_arr)
194 file_arr = g_ptr_array_new();
195 g_hash_table_insert(theWorkspace->source_file_map, g_strdup(source_file->short_name), file_arr);
197 g_ptr_array_add(file_arr, source_file);
201 /** Adds a source file to the workspace, parses it and updates the workspace tags.
202 @param source_file The source file to add to the workspace.
204 GEANY_API_SYMBOL
205 void tm_workspace_add_source_file(TMSourceFile *source_file)
207 g_return_if_fail(source_file != NULL);
209 tm_workspace_add_source_file_noupdate(source_file);
210 update_source_file(source_file, NULL, 0, FALSE, TRUE);
214 /* Updates the source file by reparsing the text-buffer passed as parameter.
215 Ctags will use a parsing based on buffer instead of on files.
216 You should call this function when you don't want a previous saving of the file
217 you're editing. It's useful for a "real-time" updating of the tags.
218 The tags array and the tags themselves are destroyed and re-created, hence any
219 other tag arrays pointing to these tags should be rebuilt as well. All sorting
220 information is also lost.
221 @param source_file The source file to update with a buffer.
222 @param text_buf A text buffer. The user should take care of allocate and free it after
223 the use here.
224 @param buf_size The size of text_buf.
226 void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* text_buf,
227 gsize buf_size)
229 update_source_file(source_file, text_buf, buf_size, TRUE, TRUE);
233 static void remove_source_file_map(TMSourceFile *source_file)
235 GPtrArray *file_arr = g_hash_table_lookup(theWorkspace->source_file_map, source_file->short_name);
237 if (file_arr)
238 g_ptr_array_remove_fast(file_arr, source_file);
242 /** Removes a source file from the workspace if it exists. This function also removes
243 the tags belonging to this file from the workspace. To completely free the TMSourceFile
244 pointer call tm_source_file_free() on it.
245 @param source_file Pointer to the source file to be removed.
247 GEANY_API_SYMBOL
248 void tm_workspace_remove_source_file(TMSourceFile *source_file)
250 guint i;
252 g_return_if_fail(source_file != NULL);
254 for (i=0; i < theWorkspace->source_files->len; ++i)
256 if (theWorkspace->source_files->pdata[i] == source_file)
258 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
259 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
260 remove_source_file_map(source_file);
261 g_ptr_array_remove_index_fast(theWorkspace->source_files, i);
262 return;
268 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
269 want to globally refresh the workspace. This function does not call tm_source_file_update()
270 which should be called before this function on source files which need to be
271 reparsed.
273 static void tm_workspace_update(void)
275 guint i, j;
276 TMSourceFile *source_file;
278 #ifdef TM_DEBUG
279 g_message("Recreating workspace tags array");
280 #endif
282 g_ptr_array_set_size(theWorkspace->tags_array, 0);
284 #ifdef TM_DEBUG
285 g_message("Total %d objects", theWorkspace->source_files->len);
286 #endif
287 for (i=0; i < theWorkspace->source_files->len; ++i)
289 source_file = theWorkspace->source_files->pdata[i];
290 #ifdef TM_DEBUG
291 g_message("Adding tags of %s", source_file->file_name);
292 #endif
293 if (source_file->tags_array->len > 0)
295 for (j = 0; j < source_file->tags_array->len; ++j)
297 g_ptr_array_add(theWorkspace->tags_array,
298 source_file->tags_array->pdata[j]);
302 #ifdef TM_DEBUG
303 g_message("Total: %d tags", theWorkspace->tags_array->len);
304 #endif
305 tm_tags_sort(theWorkspace->tags_array, workspace_tags_sort_attrs, TRUE, FALSE);
307 g_ptr_array_free(theWorkspace->typename_array, TRUE);
308 theWorkspace->typename_array = tm_tags_extract(theWorkspace->tags_array, TM_GLOBAL_TYPE_MASK);
312 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
313 This is more efficient than calling tm_workspace_add_source_file() and
314 tm_workspace_update_source_file() separately for each of the files.
315 @param source_files @elementtype{TMSourceFile} The source files to be added to the workspace.
317 GEANY_API_SYMBOL
318 void tm_workspace_add_source_files(GPtrArray *source_files)
320 guint i;
322 g_return_if_fail(source_files != NULL);
324 for (i = 0; i < source_files->len; i++)
326 TMSourceFile *source_file = source_files->pdata[i];
328 tm_workspace_add_source_file_noupdate(source_file);
329 update_source_file(source_file, NULL, 0, FALSE, FALSE);
332 tm_workspace_update();
336 /** Removes multiple source files from the workspace and updates the workspace tag
337 arrays. This is more efficient than calling tm_workspace_remove_source_file()
338 separately for each of the files. To completely free the TMSourceFile pointers
339 call tm_source_file_free() on each of them.
340 @param source_files @elementtype{TMSourceFile} The source files to be removed from the workspace.
342 GEANY_API_SYMBOL
343 void tm_workspace_remove_source_files(GPtrArray *source_files)
345 guint i, j;
347 g_return_if_fail(source_files != NULL);
349 //TODO: sort both arrays by pointer value and remove in single pass
350 for (i = 0; i < source_files->len; i++)
352 TMSourceFile *source_file = source_files->pdata[i];
354 for (j = 0; j < theWorkspace->source_files->len; j++)
356 if (theWorkspace->source_files->pdata[j] == source_file)
358 remove_source_file_map(source_file);
359 g_ptr_array_remove_index_fast(theWorkspace->source_files, j);
360 break;
365 tm_workspace_update();
369 /* Loads the global tag list from the specified file. The global tag list should
370 have been first created using tm_workspace_create_global_tags().
371 @param tags_file The file containing global tags.
372 @return TRUE on success, FALSE on failure.
373 @see tm_workspace_create_global_tags()
375 gboolean tm_workspace_load_global_tags(const char *tags_file, TMParserType mode)
377 GPtrArray *file_tags, *new_tags;
379 file_tags = tm_source_file_read_tags_file(tags_file, mode);
380 if (!file_tags)
381 return FALSE;
383 tm_tags_sort(file_tags, global_tags_sort_attrs, TRUE, TRUE);
385 /* reorder the whole array, because tm_tags_find expects a sorted array */
386 new_tags = tm_tags_merge(theWorkspace->global_tags,
387 file_tags, global_tags_sort_attrs, TRUE);
388 g_ptr_array_free(theWorkspace->global_tags, TRUE);
389 g_ptr_array_free(file_tags, TRUE);
390 theWorkspace->global_tags = new_tags;
392 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
393 theWorkspace->global_typename_array = tm_tags_extract(new_tags, TM_GLOBAL_TYPE_MASK);
395 return TRUE;
399 static gboolean write_includes_file(const gchar *outf, GList *includes_files)
401 FILE *fp = g_fopen(outf, "w");
402 GList *node = includes_files;
404 if (!fp)
405 return FALSE;
407 while (node)
409 char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data);
410 size_t str_len = strlen(str);
412 fwrite(str, str_len, 1, fp);
413 g_free(str);
414 node = g_list_next(node);
417 return fclose(fp) == 0;
420 static gchar *create_temp_file(const gchar *tpl)
422 gchar *name;
423 gint fd;
425 fd = g_file_open_tmp(tpl, &name, NULL);
426 if (fd < 0)
427 name = NULL;
428 else
429 close(fd);
431 return name;
434 static GList *lookup_sources(const gchar **sources, gint sources_count)
436 GList *source_files = NULL;
437 GHashTable *table; /* used for deduping */
438 gint i;
440 table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
442 for (i = 0; i < sources_count; i++)
444 if (!g_hash_table_lookup(table, sources[i]))
446 gchar* file_name_copy = g_strdup(sources[i]);
448 source_files = g_list_prepend(source_files, file_name_copy);
449 g_hash_table_insert(table, file_name_copy, file_name_copy);
453 g_hash_table_destroy(table);
455 return g_list_reverse(source_files);
458 static gchar *pre_process_file(const gchar *cmd, const gchar *inf)
460 gint ret;
461 gchar *outf = create_temp_file("tmp_XXXXXX.cpp");
462 gchar *tmp_errfile;
463 gchar *errors = NULL;
464 gchar *command;
466 if (!outf)
467 return NULL;
469 tmp_errfile = create_temp_file("tmp_XXXXXX");
470 if (!tmp_errfile)
472 g_unlink(outf);
473 g_free(outf);
474 return NULL;
477 command = g_strdup_printf("%s %s >%s 2>%s",
478 cmd, inf, outf, tmp_errfile);
479 #ifdef TM_DEBUG
480 g_message("Executing: %s", command);
481 #endif
482 ret = system(command);
483 g_free(command);
485 g_file_get_contents(tmp_errfile, &errors, NULL, NULL);
486 if (errors && *errors)
487 g_printerr("%s\n", errors);
488 g_free(errors);
489 g_unlink(tmp_errfile);
490 g_free(tmp_errfile);
492 if (ret == -1)
494 g_unlink(outf);
495 g_free(outf);
496 return NULL;
499 return outf;
502 static gboolean create_global_tags_preprocessed(const char *pre_process_cmd,
503 GList *source_files, const char *tags_file, TMParserType lang)
505 TMSourceFile *source_file;
506 gboolean ret = FALSE;
507 gchar *temp_file2;
508 gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
509 GPtrArray *filtered_tags;
511 #ifdef TM_DEBUG
512 g_message ("writing out files to %s\n", temp_file);
513 #endif
515 if (!temp_file)
516 return FALSE;
518 if (!write_includes_file(temp_file, source_files))
519 goto cleanup;
521 temp_file2 = pre_process_file(pre_process_cmd, temp_file);
523 if (temp_file2)
525 g_unlink(temp_file);
526 g_free(temp_file);
527 temp_file = temp_file2;
529 else
530 goto cleanup;
532 source_file = tm_source_file_new(temp_file, tm_source_file_get_lang_name(lang));
533 if (!source_file)
534 goto cleanup;
535 update_source_file(source_file, NULL, 0, FALSE, FALSE);
536 if (source_file->tags_array->len == 0)
538 tm_source_file_free(source_file);
539 goto cleanup;
542 tm_tags_sort(source_file->tags_array, global_tags_sort_attrs, TRUE, FALSE);
543 filtered_tags = tm_tags_extract(source_file->tags_array, ~(tm_tag_local_var_t | tm_tag_include_t));
544 ret = tm_source_file_write_tags_file(tags_file, filtered_tags);
545 g_ptr_array_free(filtered_tags, TRUE);
546 tm_source_file_free(source_file);
548 cleanup:
549 g_unlink(temp_file);
550 g_free(temp_file);
551 return ret;
554 static gboolean create_global_tags_direct(GList *source_files, const char *tags_file,
555 TMParserType lang)
557 GList *node;
558 GPtrArray *filtered_tags;
559 GPtrArray *tags = g_ptr_array_new();
560 GSList *tm_source_files = NULL;
561 gboolean ret = FALSE;
563 for (node = source_files; node; node = node->next)
565 TMSourceFile *source_file = tm_source_file_new(node->data, tm_source_file_get_lang_name(lang));
566 if (source_file)
568 guint i;
569 tm_source_files = g_slist_prepend(tm_source_files, source_file);
570 tm_source_file_parse(source_file, NULL, 0, FALSE);
571 for (i = 0; i < source_file->tags_array->len; i++)
572 g_ptr_array_add(tags, source_file->tags_array->pdata[i]);
576 filtered_tags = tm_tags_extract(tags, ~(tm_tag_local_var_t | tm_tag_include_t));
577 tm_tags_sort(filtered_tags, global_tags_sort_attrs, TRUE, FALSE);
579 if (filtered_tags->len > 0)
580 ret = tm_source_file_write_tags_file(tags_file, filtered_tags);
582 g_ptr_array_free(tags, TRUE);
583 g_ptr_array_free(filtered_tags, TRUE);
584 g_slist_free_full(tm_source_files, (GDestroyNotify)tm_source_file_free);
586 return ret;
589 /* Creates a list of global tags. Ideally, this should be created once during
590 installations so that all users can use the same file. This is because a full
591 scale global tag list can occupy several megabytes of disk space.
592 @param pre_process_cmd The pre-processing command. This is executed via system(),
593 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
594 @param sources Source files to process. Wildcards such as '/usr/include/a*.h'
595 are allowed.
596 @param tags_file The file where the tags will be stored.
597 @param lang The language to use for the tags file.
598 @return TRUE on success, FALSE on failure.
600 gboolean tm_workspace_create_global_tags(const char *pre_process_cmd, const char **sources,
601 int sources_count, const char *tags_file, TMParserType lang)
603 gboolean ret = FALSE;
604 GList *source_files = lookup_sources(sources, sources_count);
606 if (pre_process_cmd)
607 ret = create_global_tags_preprocessed(pre_process_cmd, source_files, tags_file, lang);
608 else
609 ret = create_global_tags_direct(source_files, tags_file, lang);
611 g_list_free_full(source_files, g_free);
612 return ret;
615 static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
616 const char *name, const char *scope, TMTagType type, TMParserType lang)
618 TMTag **tag;
619 guint i, num;
621 if (!src || !dst || !name || !*name)
622 return;
624 tag = tm_tags_find(src, name, FALSE, &num);
625 for (i = 0; i < num; ++i)
627 if ((type & (*tag)->type) &&
628 tm_parser_langs_compatible(lang, (*tag)->lang) &&
629 (!scope || g_strcmp0((*tag)->scope, scope) == 0))
631 g_ptr_array_add(dst, *tag);
633 tag++;
638 /* Returns all matching tags found in the workspace.
639 @param name The name of the tag to find.
640 @param scope The scope name of the tag to find, or NULL.
641 @param type The tag types to return (TMTagType). Can be a bitmask.
642 @param attrs The attributes to sort and dedup on (0 terminated integer array).
643 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
644 -1 for all
645 @return Array of matching tags.
647 GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
648 TMTagAttrType *attrs, TMParserType lang)
650 GPtrArray *tags = g_ptr_array_new();
652 fill_find_tags_array(tags, theWorkspace->tags_array, name, scope, type, lang);
653 fill_find_tags_array(tags, theWorkspace->global_tags, name, scope, type, lang);
655 if (attrs)
656 tm_tags_sort(tags, attrs, TRUE, FALSE);
658 return tags;
662 gboolean tm_workspace_is_autocomplete_tag(TMTag *tag,
663 TMSourceFile *current_file,
664 guint current_line,
665 const gchar *current_scope)
667 TMParserType lang = current_file ? current_file->lang : TM_PARSER_NONE;
669 /* ignore local variables from other files/functions or after current line */
670 gboolean valid = !(tag->type & tm_tag_local_var_t) ||
671 (current_file == tag->file &&
672 current_line >= tag->line &&
673 g_strcmp0(current_scope, tag->scope) == 0);
675 /* tag->local indicates per-file-only visibility such as static C functions */
676 gboolean valid_local = !tag->local || current_file == tag->file;
678 return valid && valid_local &&
679 !tm_tag_is_anon(tag) && tm_parser_langs_compatible(lang, tag->lang) &&
680 !(tag->type & tm_tag_include_t);
684 #if ! GLIB_CHECK_VERSION(2, 54, 0)
685 static gboolean
686 g_ptr_array_find (GPtrArray *haystack,
687 gconstpointer needle,
688 guint *index_)
690 guint i;
692 g_return_val_if_fail (haystack != NULL, FALSE);
694 for (i = 0; i < haystack->len; i++)
696 if (g_direct_equal (g_ptr_array_index (haystack, i), needle))
698 if (index_ != NULL)
699 *index_ = i;
700 return TRUE;
704 return FALSE;
706 #endif
709 typedef struct
711 TMSourceFile *file;
712 GPtrArray *header_candidates;
713 GHashTable *includes;
714 guint line;
715 const gchar *scope;
716 } CopyInfo;
718 static gboolean is_any_tag(TMTag *tag, CopyInfo *info)
720 return TRUE;
723 static gboolean is_local_tag(TMTag *tag, CopyInfo *info)
725 return tag->type & tm_tag_local_var_t;
728 static gboolean is_non_local_tag(TMTag *tag, CopyInfo *info)
730 return !is_local_tag(tag, info);
733 /* non-local tag not from current file, header, or included files */
734 static gboolean is_workspace_tag(TMTag *tag, CopyInfo *info)
736 return tag->file != info->file &&
737 (!info->header_candidates || !g_ptr_array_find(info->header_candidates, tag->file, NULL)) &&
738 !g_hash_table_lookup(info->includes, tag->file) &&
739 is_non_local_tag(tag, info);
743 static void copy_tags(GPtrArray *dst, TMTag **src, guint src_len, GHashTable *name_table,
744 gint num, gboolean (*predicate) (TMTag *, CopyInfo *), CopyInfo *info)
746 guint i;
748 g_return_if_fail(src && dst);
750 for (i = 0; i < src_len && num > 0; i++)
752 TMTag *tag = *src;
753 if (predicate(tag, info) &&
754 tm_workspace_is_autocomplete_tag(tag, info->file, info->line, info->scope) &&
755 !g_hash_table_contains(name_table, tag->name))
757 g_ptr_array_add(dst, tag);
758 g_hash_table_add(name_table, tag->name);
759 num--;
761 src++;
766 static void fill_find_tags_array_prefix(GPtrArray *dst, const char *name,
767 CopyInfo *info, guint max_num)
769 TMTag **found;
770 guint count;
771 GHashTable *name_table;
773 if (!dst || !name || !*name)
774 return;
776 name_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
778 if (info->file)
780 found = tm_tags_find(info->file->tags_array, name, TRUE, &count);
781 if (found)
783 copy_tags(dst, found, count, name_table, max_num - dst->len, is_local_tag, info);
784 if (dst->len < max_num)
785 copy_tags(dst, found, count, name_table, max_num - dst->len, is_non_local_tag, info);
788 if (dst->len < max_num && info->header_candidates)
790 guint i;
792 for (i = 0; i < info->header_candidates->len; i++)
794 TMSourceFile *hdr = info->header_candidates->pdata[i];
795 found = tm_tags_find(hdr->tags_array, name, TRUE, &count);
796 if (found)
797 copy_tags(dst, found, count, name_table, max_num - dst->len, is_non_local_tag, info);
800 if (dst->len < max_num)
802 GHashTableIter iter;
803 gpointer key;
805 g_hash_table_iter_init(&iter, info->includes);
806 while (g_hash_table_iter_next(&iter, &key, NULL))
808 TMSourceFile *include_file = key;
809 found = tm_tags_find(include_file->tags_array, name, TRUE, &count);
810 if (found)
811 copy_tags(dst, found, count, name_table, max_num - dst->len, is_non_local_tag, info);
814 if (dst->len < max_num)
816 found = tm_tags_find(theWorkspace->tags_array, name, TRUE, &count);
817 if (found)
818 copy_tags(dst, found, count, name_table, max_num - dst->len, is_workspace_tag, info);
820 if (dst->len < max_num)
822 found = tm_tags_find(theWorkspace->global_tags, name, TRUE, &count);
823 if (found)
824 copy_tags(dst, found, count, name_table, max_num - dst->len, is_any_tag, info);
827 g_hash_table_unref(name_table);
831 /* return TMSourceFile files corresponding to files included in 'source';
832 * in addition, fill header_candidates with TMSourceFiles that could be the header
833 * of 'source' based on the file name */
834 static GHashTable *get_includes(TMSourceFile *source, GPtrArray **header_candidates)
836 GHashTable *includes = g_hash_table_new(NULL, NULL);
837 GPtrArray *headers;
838 gchar *src_basename, *ptr;
839 guint i;
841 *header_candidates = NULL;
843 if (!source ||
844 (source->lang != TM_PARSER_C && source->lang != TM_PARSER_CPP))
845 return includes;
847 src_basename = g_strdup(source->short_name);
848 if ((ptr = strrchr(src_basename, '.')) != NULL)
849 *ptr = '\0';
851 headers = tm_tags_extract(source->tags_array, tm_tag_include_t);
853 for (i = 0; i < headers->len; i++)
855 TMTag *hdr_tag = headers->pdata[i];
856 gchar *hdr_name = g_path_get_basename(hdr_tag->name);
857 GPtrArray *tm_files = g_hash_table_lookup(theWorkspace->source_file_map, hdr_name);
859 if (tm_files && tm_files->len > 0)
861 guint j;
863 if (!*header_candidates)
865 gchar *hdr_basename = g_strdup(hdr_name);
866 if ((ptr = strrchr(hdr_basename, '.')) != NULL)
867 *ptr = '\0';
869 if (g_strcmp0(hdr_basename, src_basename) == 0)
870 *header_candidates = tm_files;
871 g_free(hdr_basename);
874 for (j = 0; j < tm_files->len; j++)
875 g_hash_table_add(includes, tm_files->pdata[j]);
878 g_free(hdr_name);
881 g_ptr_array_free(headers, TRUE);
882 g_free(src_basename);
883 return includes;
887 typedef struct
889 TMSourceFile *file;
890 GPtrArray *header_candidates;
891 GHashTable *includes;
892 gboolean sort_by_name;
893 } SortInfo;
896 static gint sort_found_tags(gconstpointer a, gconstpointer b, gpointer user_data)
898 SortInfo *info = user_data;
899 const TMTag *t1 = *((TMTag **) a);
900 const TMTag *t2 = *((TMTag **) b);
902 /* sort local vars first (with highest line number first),
903 * followed by tags from current file,
904 * followed by tags from header,
905 * followed by tags from other included files,
906 * followed by workspace tags,
907 * followed by global tags */
908 if (t1->type & tm_tag_local_var_t && t2->type & tm_tag_local_var_t)
909 return info->sort_by_name ? g_strcmp0(t1->name, t2->name) : t2->line - t1->line;
910 else if (t1->type & tm_tag_local_var_t)
911 return -1;
912 else if (t2->type & tm_tag_local_var_t)
913 return 1;
914 else if (t1->file == info->file && t2->file != info->file)
915 return -1;
916 else if (t2->file == info->file && t1->file != info->file)
917 return 1;
918 else if (info->header_candidates &&
919 g_ptr_array_find(info->header_candidates, t1->file, NULL) &&
920 !g_ptr_array_find(info->header_candidates, t2->file, NULL))
921 return -1;
922 else if (info->header_candidates &&
923 g_ptr_array_find(info->header_candidates, t2->file, NULL) &&
924 !g_ptr_array_find(info->header_candidates, t1->file, NULL))
925 return 1;
926 else if (g_hash_table_lookup(info->includes, t1->file) && !g_hash_table_lookup(info->includes, t2->file))
927 return -1;
928 else if (g_hash_table_lookup(info->includes, t2->file) && !g_hash_table_lookup(info->includes, t1->file))
929 return 1;
930 else if (t1->file && !t2->file)
931 return -1;
932 else if (t2->file && !t1->file)
933 return 1;
934 return g_strcmp0(t1->name, t2->name);
938 /* Returns tags with the specified prefix sorted by name, ignoring local
939 variables from other files/functions or after current line. If there are several
940 tags with the same name, only one of them appears in the resulting array.
941 @param prefix The prefix of the tag to find.
942 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
943 -1 for all.
944 @param max_num The maximum number of tags to return.
945 @return Array of matching tags sorted by their name.
947 GPtrArray *tm_workspace_find_prefix(const char *prefix,
948 TMSourceFile *current_file,
949 guint current_line,
950 const gchar *current_scope,
951 guint max_num)
953 TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
954 GPtrArray *tags = g_ptr_array_new();
955 GPtrArray *header_candidates;
956 SortInfo sort_info;
957 CopyInfo copy_info;
958 GHashTable *includes = get_includes(current_file, &header_candidates);
960 copy_info.file = current_file;
961 copy_info.header_candidates = header_candidates;
962 copy_info.includes = includes;
963 copy_info.line = current_line;
964 copy_info.scope = current_scope;
965 fill_find_tags_array_prefix(tags, prefix, &copy_info, max_num);
967 /* sort based on how "close" the tag is to current line with local
968 * variables first */
969 sort_info.file = current_file;
970 sort_info.header_candidates = header_candidates;
971 sort_info.includes = includes;
972 sort_info.sort_by_name = TRUE;
973 g_ptr_array_sort_with_data(tags, sort_found_tags, &sort_info);
975 g_hash_table_destroy(includes);
977 return tags;
981 static gboolean replace_with_char(gchar *haystack, const gchar *needle, char replacement)
983 gchar *pos = strstr(haystack, needle);
984 if (pos)
986 while (*needle)
988 *pos = replacement;
989 needle++;
990 pos++;
992 return TRUE;
994 return FALSE;
998 static gboolean replace_parens_with_char(gchar *haystack, gchar paren_begin, gchar paren_end, char replacement)
1000 gchar needle[2] = {paren_begin, '\0'};
1001 gchar *pos = strstr(haystack, needle);
1002 gint nesting = 0;
1004 if (pos)
1006 while (*pos)
1008 if (*pos == paren_begin)
1009 nesting++;
1010 else if (*pos == paren_end)
1011 nesting--;
1012 *pos = replacement;
1013 if (nesting == 0)
1014 break;
1015 pos++;
1017 return TRUE;
1019 return FALSE;
1023 static gchar *strip_type(const gchar *scoped_name, TMParserType lang, gboolean remove_scope)
1025 if (scoped_name != NULL)
1027 const gchar *sep = tm_parser_scope_separator(lang);
1028 gchar *name = g_strdup(scoped_name);
1029 gchar *scope_suffix;
1031 /* remove pointers, parens and keywords appearing in types */
1032 g_strdelimit(name, "*^&", ' ');
1033 while (replace_parens_with_char(name, '[', ']', ' ')) {}
1034 while (replace_parens_with_char(name, '<', '>', ' ')) {}
1035 while (replace_with_char(name, "const ", ' ')) {}
1036 while (replace_with_char(name, " const", ' ')) {}
1037 while (replace_with_char(name, " struct", ' ')) {}
1038 /* remove everything before final scope separator */
1039 if (remove_scope && (scope_suffix = g_strrstr(name, sep)))
1041 scope_suffix += strlen(sep);
1042 scope_suffix = g_strdup(scope_suffix);
1043 g_free(name);
1044 name = scope_suffix;
1046 g_strstrip(name);
1048 return name;
1050 return NULL;
1054 /* Gets all members of type_tag; search them inside the all array.
1055 * The namespace parameter determines whether we are performing the "namespace"
1056 * search (user has typed something like "A::" where A is a type) or "scope" search
1057 * (user has typed "a." where a is a global struct-like variable). With the
1058 * namespace search we return all direct descendants of any type while with the
1059 * scope search we return only those which can be invoked on a variable (member,
1060 * method, etc.). */
1061 static GPtrArray *
1062 find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace, guint depth)
1064 TMTagType member_types = tm_tag_max_t & ~(TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t);
1065 GPtrArray *tags;
1066 gchar *scope;
1067 guint i;
1069 if (depth == 10)
1070 return NULL; /* simple inheritence cycle avoidance */
1072 tags = g_ptr_array_new();
1074 if (namespace)
1075 member_types = tm_tag_max_t;
1077 if (type_tag->scope && *(type_tag->scope))
1078 scope = g_strconcat(type_tag->scope, tm_parser_scope_separator(type_tag->lang), type_tag->name, NULL);
1079 else
1080 scope = g_strdup(type_tag->name);
1082 for (i = 0; i < all->len; ++i)
1084 TMTag *tag = TM_TAG (all->pdata[i]);
1086 if (tag && (tag->type & member_types) &&
1087 tag->scope && tag->scope[0] != '\0' &&
1088 tm_parser_langs_compatible(tag->lang, type_tag->lang) &&
1089 strcmp(scope, tag->scope) == 0 &&
1090 (!namespace || !tm_tag_is_anon(tag)))
1092 g_ptr_array_add (tags, tag);
1096 /* add members from parent classes */
1097 if (!namespace && (type_tag->type & (tm_tag_class_t | tm_tag_struct_t)) &&
1098 type_tag->inheritance && *type_tag->inheritance)
1100 gchar *stripped = strip_type(type_tag->inheritance, type_tag->lang, FALSE);
1101 gchar **split_strv = g_strsplit(stripped, ",", -1); /* parent classes */
1102 const gchar *parent;
1104 g_free(stripped);
1106 for (i = 0; (parent = split_strv[i]) != NULL; i++)
1108 GPtrArray *parent_tags;
1110 stripped = strip_type(parent, type_tag->lang, TRUE);
1111 parent_tags = tm_workspace_find(stripped, NULL, tm_tag_class_t | tm_tag_struct_t,
1112 NULL, type_tag->lang);
1114 if (parent_tags->len > 0)
1116 TMTag *parent_tag = parent_tags->pdata[0];
1117 GPtrArray *parent_members = find_scope_members_tags(
1118 parent_tag->file ? parent_tag->file->tags_array : all, parent_tag,
1119 FALSE, depth + 1);
1121 if (parent_members)
1123 guint j;
1124 for (j = 0; j < parent_members->len; j++)
1125 g_ptr_array_add (tags, parent_members->pdata[j]);
1126 g_ptr_array_free(parent_members, TRUE);
1130 g_ptr_array_free(parent_tags, TRUE);
1131 g_free(stripped);
1134 g_strfreev(split_strv);
1137 g_free(scope);
1139 if (tags->len == 0)
1141 g_ptr_array_free(tags, TRUE);
1142 return NULL;
1145 if (depth == 0)
1147 TMTagAttrType sort_attrs[] = {tm_tag_attr_name_t, 0};
1148 tm_tags_sort(tags, sort_attrs, TRUE, FALSE);
1151 return tags;
1155 /* Gets all members of the type with the given name; search them inside tags_array */
1156 static GPtrArray *
1157 find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile *file,
1158 TMParserType lang, gboolean namespace)
1160 GPtrArray *res = NULL;
1161 gchar *type_name;
1162 guint i;
1164 g_return_val_if_fail(name && *name, NULL);
1166 type_name = g_strdup(name);
1168 /* Check if type_name is a type that can possibly contain members.
1169 * Try to resolve intermediate typedefs to get the real type name. Also
1170 * add scope information to the name if applicable.
1171 * The loop below loops only when resolving typedefs - avoid possibly infinite
1172 * loop when typedefs create a cycle by adding some limits. */
1173 for (i = 0; i < 5; i++)
1175 guint j;
1176 GPtrArray *type_tags;
1177 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
1178 TMTag *tag = NULL;
1180 if (!namespace)
1181 types &= ~tm_tag_enum_t;
1183 type_tags = g_ptr_array_new();
1184 fill_find_tags_array(type_tags, tags_array, type_name, NULL, types, lang);
1186 for (j = 0; j < type_tags->len; j++)
1188 TMTag *test_tag = TM_TAG(type_tags->pdata[j]);
1190 /* anonymous type defined in a different file than the variable -
1191 * this isn't the type we are looking for */
1192 if (tm_tag_is_anon(test_tag) && (file != test_tag->file || test_tag->file == NULL))
1193 continue;
1195 tag = test_tag;
1197 /* prefer non-typedef tags because we can be sure they contain members */
1198 if (test_tag->type != tm_tag_typedef_t)
1199 break;
1202 g_ptr_array_free(type_tags, TRUE);
1204 if (!tag) /* not a type that can contain members */
1205 break;
1207 /* intermediate typedef - resolve to the real type */
1208 if (tag->type == tm_tag_typedef_t)
1210 if (tag->var_type && tag->var_type[0] != '\0')
1212 g_free(type_name);
1213 type_name = strip_type(tag->var_type, tag->lang, TRUE);
1214 file = tag->file;
1215 continue;
1217 break;
1219 else /* real type with members */
1221 /* use the same file as the composite type if file information available */
1222 res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace, 0);
1223 break;
1227 g_free(type_name);
1229 return res;
1233 /* Checks whether a member tag is directly accessible from method */
1234 static gboolean member_accessible(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag,
1235 TMParserType lang)
1237 const gchar *sep = tm_parser_scope_separator(lang);
1238 gboolean ret = FALSE;
1239 gchar **comps;
1240 guint len;
1242 /* method scope is in the form ...::class_name::method_name */
1243 comps = g_strsplit (method_scope, sep, 0);
1244 len = g_strv_length(comps);
1245 if (len > 1)
1247 gchar *cls = comps[len - 2];
1249 if (*cls)
1251 /* find method's class members */
1252 GPtrArray *cls_tags = find_scope_members(tags, cls, NULL, lang, FALSE);
1254 if (cls_tags)
1256 guint i;
1258 /* check if one of the class members is member_tag */
1259 for (i = 0; i < cls_tags->len; i++)
1261 TMTag *t = cls_tags->pdata[i];
1263 if (t == member_tag)
1265 ret = TRUE;
1266 break;
1269 g_ptr_array_free(cls_tags, TRUE);
1274 g_strfreev(comps);
1275 return ret;
1279 /* For an array of variable/type tags, find members inside the types */
1280 static GPtrArray *
1281 find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang,
1282 gboolean member, const gchar *current_scope)
1284 GPtrArray *member_tags = NULL;
1285 guint i;
1287 /* there may be several variables/types with the same name - try each of them until
1288 * we find something */
1289 for (i = 0; i < tags->len && !member_tags; i++)
1291 TMTag *tag = TM_TAG(tags->pdata[i]);
1292 TMTagType member_types = tm_tag_member_t | tm_tag_field_t | tm_tag_method_t;
1293 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
1295 if (tag->type & types) /* type: namespace search */
1297 if (tag->type & tm_tag_typedef_t)
1298 member_tags = find_scope_members(searched_array, tag->name, tag->file, lang, TRUE);
1299 else
1300 member_tags = find_scope_members_tags(tag->file ? tag->file->tags_array : searched_array,
1301 tag, TRUE, 0);
1303 else if (tag->var_type) /* variable: scope search */
1305 /* The question now is whether we should use member tags (such as
1306 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
1307 * (which means user has typed something like foo.bar.) or if we are
1308 * inside a method where foo is a class member, we want scope completion
1309 * for foo. */
1310 if (!(tag->type & member_types) || member ||
1311 member_accessible(searched_array, current_scope, tag, lang))
1313 gchar *tag_type = strip_type(tag->var_type, tag->lang, TRUE);
1315 member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE);
1316 g_free(tag_type);
1321 return member_tags;
1325 static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array)
1327 GPtrArray *member_tags = NULL;
1328 guint i;
1330 for (i = 0; i < tags->len && !member_tags; i++)
1332 TMTag *tag = TM_TAG(tags->pdata[i]);
1334 member_tags = find_scope_members_tags(searched_array, tag, TRUE, 0);
1337 return member_tags;
1341 /* Returns all member tags of a struct/union/class if the provided name is a variable
1342 of such a type or the name of the type.
1343 @param source_file TMSourceFile of the edited source file
1344 @param name Name of the variable/type whose members are searched
1345 @param function TRUE if the name is a name of a function
1346 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1347 @param current_scope The current scope in the editor
1348 @param current_line The current line in the editor
1349 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1350 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1351 GPtrArray *
1352 tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
1353 gboolean function, gboolean member, const gchar *current_scope, guint current_line,
1354 gboolean search_namespace)
1356 TMParserType lang = source_file ? source_file->lang : TM_PARSER_NONE;
1357 GPtrArray *tags, *member_tags = NULL;
1358 TMTagType function_types = tm_tag_function_t | tm_tag_method_t |
1359 tm_tag_macro_with_arg_t | tm_tag_prototype_t;
1360 TMTagType tag_type = tm_tag_max_t &
1361 ~(function_types | tm_tag_enumerator_t | tm_tag_namespace_t | tm_tag_package_t);
1362 TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0};
1364 if (search_namespace)
1366 tags = tm_workspace_find(name, NULL, tm_tag_namespace_t, NULL, lang);
1368 member_tags = find_namespace_members_all(tags, theWorkspace->tags_array);
1369 if (!member_tags)
1370 member_tags = find_namespace_members_all(tags, theWorkspace->global_tags);
1372 g_ptr_array_free(tags, TRUE);
1375 if (!member_tags)
1377 SortInfo info;
1378 guint i;
1380 if (function)
1381 tag_type = function_types;
1383 /* tags corresponding to the variable/type name */
1384 tags = tm_workspace_find(name, NULL, tag_type, NULL, lang);
1386 /* remove invalid local tags and sort tags so "nearest" tags are first */
1387 for (i = 0; i < tags->len; i++)
1389 TMTag *tag = tags->pdata[i];
1390 if (!tm_workspace_is_autocomplete_tag(tag, source_file, current_line, current_scope))
1391 tags->pdata[i] = NULL;
1393 tm_tags_prune(tags);
1395 info.file = source_file;
1396 info.sort_by_name = FALSE;
1397 info.includes = get_includes(source_file, &info.header_candidates);
1398 g_ptr_array_sort_with_data(tags, sort_found_tags, &info);
1400 /* Start searching inside the source file, continue with workspace tags and
1401 * end with global tags. This way we find the "closest" tag to the current
1402 * file in case there are more of them. */
1403 if (source_file)
1404 member_tags = find_scope_members_all(tags, source_file->tags_array,
1405 lang, member, current_scope);
1406 if (!member_tags)
1407 member_tags = find_scope_members_all(tags, theWorkspace->tags_array, lang,
1408 member, current_scope);
1409 if (!member_tags)
1410 member_tags = find_scope_members_all(tags, theWorkspace->global_tags, lang,
1411 member, current_scope);
1413 g_ptr_array_free(tags, TRUE);
1414 g_hash_table_destroy(info.includes);
1417 if (member_tags)
1418 tm_tags_dedup(member_tags, sort_attr, FALSE);
1420 return member_tags;
1424 #ifdef TM_DEBUG
1426 /* Dumps the workspace tree - useful for debugging */
1427 void tm_workspace_dump(void)
1429 guint i;
1431 #ifdef TM_DEBUG
1432 g_message("Dumping TagManager workspace tree..");
1433 #endif
1434 for (i=0; i < theWorkspace->source_files->len; ++i)
1436 TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
1437 fprintf(stderr, "%s", source_file->file_name);
1440 #endif /* TM_DEBUG */