Remove unnecessary "lang" parameter of various functions
[geany-mirror.git] / src / tagmanager / tm_workspace.c
blob3511e8ea5be7e1ecda28db54dd13fedc03da1cf3
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 #ifdef HAVE_GLOB_H
28 # include <glob.h>
29 #endif
30 #include <glib/gstdio.h>
32 #include "tm_workspace.h"
33 #include "tm_ctags.h"
34 #include "tm_tag.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[] =
55 tm_tag_attr_name_t,
56 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
59 static TMTagType TM_TYPE_WITH_MEMBERS =
60 tm_tag_class_t | tm_tag_struct_t | tm_tag_union_t |
61 tm_tag_enum_t | tm_tag_interface_t;
63 static TMTagType TM_GLOBAL_TYPE_MASK =
64 tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t |
65 tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t;
67 static TMWorkspace *theWorkspace = NULL;
70 static gboolean tm_create_workspace(void)
72 theWorkspace = g_new(TMWorkspace, 1);
73 theWorkspace->tags_array = g_ptr_array_new();
75 theWorkspace->global_tags = g_ptr_array_new();
76 theWorkspace->source_files = g_ptr_array_new();
77 theWorkspace->typename_array = g_ptr_array_new();
78 theWorkspace->global_typename_array = g_ptr_array_new();
80 tm_ctags_init();
81 tm_parser_verify_type_mappings();
83 return TRUE;
87 /* Frees the workspace structure and all child source files. Use only when
88 exiting from the main program.
90 void tm_workspace_free(void)
92 guint i;
94 #ifdef TM_DEBUG
95 g_message("Workspace destroyed");
96 #endif
98 for (i=0; i < theWorkspace->source_files->len; ++i)
99 tm_source_file_free(theWorkspace->source_files->pdata[i]);
100 g_ptr_array_free(theWorkspace->source_files, TRUE);
101 tm_tags_array_free(theWorkspace->global_tags, TRUE);
102 g_ptr_array_free(theWorkspace->tags_array, TRUE);
103 g_ptr_array_free(theWorkspace->typename_array, TRUE);
104 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
105 g_free(theWorkspace);
106 theWorkspace = NULL;
110 /* Since TMWorkspace is a singleton, you should not create multiple
111 workspaces, but get a pointer to the workspace whenever required. The first
112 time a pointer is requested, or a source file is added to the workspace,
113 a workspace is created. Subsequent calls to the function will return the
114 created workspace.
116 const TMWorkspace *tm_get_workspace(void)
118 if (NULL == theWorkspace)
119 tm_create_workspace();
120 return theWorkspace;
124 static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_array)
126 GPtrArray *new_tags = tm_tags_merge(*big_array, small_array, workspace_tags_sort_attrs, FALSE);
127 /* tags owned by TMSourceFile - free just the pointer array */
128 g_ptr_array_free(*big_array, TRUE);
129 *big_array = new_tags;
133 static void merge_extracted_tags(GPtrArray **dest, GPtrArray *src, TMTagType tag_types)
135 GPtrArray *arr;
137 arr = tm_tags_extract(src, tag_types);
138 tm_workspace_merge_tags(dest, arr);
139 g_ptr_array_free(arr, TRUE);
143 static void update_source_file(TMSourceFile *source_file, guchar* text_buf,
144 gsize buf_size, gboolean use_buffer, gboolean update_workspace)
146 #ifdef TM_DEBUG
147 g_message("Source file updating based on source file %s", source_file->file_name);
148 #endif
150 if (update_workspace)
152 /* tm_source_file_parse() deletes the tag objects - remove the tags from
153 * workspace while they exist and can be scanned */
154 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
155 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
157 tm_source_file_parse(source_file, text_buf, buf_size, use_buffer);
158 tm_tags_sort(source_file->tags_array, file_tags_sort_attrs, FALSE, TRUE);
159 if (update_workspace)
161 #ifdef TM_DEBUG
162 g_message("Updating workspace from source file");
163 #endif
164 tm_workspace_merge_tags(&theWorkspace->tags_array, source_file->tags_array);
166 merge_extracted_tags(&(theWorkspace->typename_array), source_file->tags_array, TM_GLOBAL_TYPE_MASK);
168 #ifdef TM_DEBUG
169 else
170 g_message("Skipping workspace update because update_workspace is %s",
171 update_workspace?"TRUE":"FALSE");
173 #endif
177 /** Adds a source file to the workspace, parses it and updates the workspace tags.
178 @param source_file The source file to add to the workspace.
180 GEANY_API_SYMBOL
181 void tm_workspace_add_source_file(TMSourceFile *source_file)
183 g_return_if_fail(source_file != NULL);
185 g_ptr_array_add(theWorkspace->source_files, source_file);
186 update_source_file(source_file, NULL, 0, FALSE, TRUE);
190 void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file)
192 g_return_if_fail(source_file != NULL);
194 g_ptr_array_add(theWorkspace->source_files, source_file);
198 /* Updates the source file by reparsing the text-buffer passed as parameter.
199 Ctags will use a parsing based on buffer instead of on files.
200 You should call this function when you don't want a previous saving of the file
201 you're editing. It's useful for a "real-time" updating of the tags.
202 The tags array and the tags themselves are destroyed and re-created, hence any
203 other tag arrays pointing to these tags should be rebuilt as well. All sorting
204 information is also lost.
205 @param source_file The source file to update with a buffer.
206 @param text_buf A text buffer. The user should take care of allocate and free it after
207 the use here.
208 @param buf_size The size of text_buf.
210 void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* text_buf,
211 gsize buf_size)
213 update_source_file(source_file, text_buf, buf_size, TRUE, TRUE);
217 /** Removes a source file from the workspace if it exists. This function also removes
218 the tags belonging to this file from the workspace. To completely free the TMSourceFile
219 pointer call tm_source_file_free() on it.
220 @param source_file Pointer to the source file to be removed.
222 GEANY_API_SYMBOL
223 void tm_workspace_remove_source_file(TMSourceFile *source_file)
225 guint i;
227 g_return_if_fail(source_file != NULL);
229 for (i=0; i < theWorkspace->source_files->len; ++i)
231 if (theWorkspace->source_files->pdata[i] == source_file)
233 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
234 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
235 g_ptr_array_remove_index_fast(theWorkspace->source_files, i);
236 return;
242 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
243 want to globally refresh the workspace. This function does not call tm_source_file_update()
244 which should be called before this function on source files which need to be
245 reparsed.
247 static void tm_workspace_update(void)
249 guint i, j;
250 TMSourceFile *source_file;
252 #ifdef TM_DEBUG
253 g_message("Recreating workspace tags array");
254 #endif
256 g_ptr_array_set_size(theWorkspace->tags_array, 0);
258 #ifdef TM_DEBUG
259 g_message("Total %d objects", theWorkspace->source_files->len);
260 #endif
261 for (i=0; i < theWorkspace->source_files->len; ++i)
263 source_file = theWorkspace->source_files->pdata[i];
264 #ifdef TM_DEBUG
265 g_message("Adding tags of %s", source_file->file_name);
266 #endif
267 if (source_file->tags_array->len > 0)
269 for (j = 0; j < source_file->tags_array->len; ++j)
271 g_ptr_array_add(theWorkspace->tags_array,
272 source_file->tags_array->pdata[j]);
276 #ifdef TM_DEBUG
277 g_message("Total: %d tags", theWorkspace->tags_array->len);
278 #endif
279 tm_tags_sort(theWorkspace->tags_array, workspace_tags_sort_attrs, TRUE, FALSE);
281 g_ptr_array_free(theWorkspace->typename_array, TRUE);
282 theWorkspace->typename_array = tm_tags_extract(theWorkspace->tags_array, TM_GLOBAL_TYPE_MASK);
286 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
287 This is more efficient than calling tm_workspace_add_source_file() and
288 tm_workspace_update_source_file() separately for each of the files.
289 @param source_files @elementtype{TMSourceFile} The source files to be added to the workspace.
291 GEANY_API_SYMBOL
292 void tm_workspace_add_source_files(GPtrArray *source_files)
294 guint i;
296 g_return_if_fail(source_files != NULL);
298 for (i = 0; i < source_files->len; i++)
300 TMSourceFile *source_file = source_files->pdata[i];
302 tm_workspace_add_source_file_noupdate(source_file);
303 update_source_file(source_file, NULL, 0, FALSE, FALSE);
306 tm_workspace_update();
310 /** Removes multiple source files from the workspace and updates the workspace tag
311 arrays. This is more efficient than calling tm_workspace_remove_source_file()
312 separately for each of the files. To completely free the TMSourceFile pointers
313 call tm_source_file_free() on each of them.
314 @param source_files @elementtype{TMSourceFile} The source files to be removed from the workspace.
316 GEANY_API_SYMBOL
317 void tm_workspace_remove_source_files(GPtrArray *source_files)
319 guint i, j;
321 g_return_if_fail(source_files != NULL);
323 //TODO: sort both arrays by pointer value and remove in single pass
324 for (i = 0; i < source_files->len; i++)
326 TMSourceFile *source_file = source_files->pdata[i];
328 for (j = 0; j < theWorkspace->source_files->len; j++)
330 if (theWorkspace->source_files->pdata[j] == source_file)
332 g_ptr_array_remove_index_fast(theWorkspace->source_files, j);
333 break;
338 tm_workspace_update();
342 /* Loads the global tag list from the specified file. The global tag list should
343 have been first created using tm_workspace_create_global_tags().
344 @param tags_file The file containing global tags.
345 @return TRUE on success, FALSE on failure.
346 @see tm_workspace_create_global_tags()
348 gboolean tm_workspace_load_global_tags(const char *tags_file, TMParserType mode)
350 GPtrArray *file_tags, *new_tags;
352 file_tags = tm_source_file_read_tags_file(tags_file, mode);
353 if (!file_tags)
354 return FALSE;
356 tm_tags_sort(file_tags, global_tags_sort_attrs, TRUE, TRUE);
358 /* reorder the whole array, because tm_tags_find expects a sorted array */
359 new_tags = tm_tags_merge(theWorkspace->global_tags,
360 file_tags, global_tags_sort_attrs, TRUE);
361 g_ptr_array_free(theWorkspace->global_tags, TRUE);
362 g_ptr_array_free(file_tags, TRUE);
363 theWorkspace->global_tags = new_tags;
365 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
366 theWorkspace->global_typename_array = tm_tags_extract(new_tags, TM_GLOBAL_TYPE_MASK);
368 return TRUE;
372 static gboolean write_includes_file(const gchar *outf, GList *includes_files)
374 FILE *fp = g_fopen(outf, "w");
375 GList *node = includes_files;
377 if (!fp)
378 return FALSE;
380 while (node)
382 char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data);
383 size_t str_len = strlen(str);
385 fwrite(str, str_len, 1, fp);
386 g_free(str);
387 node = g_list_next(node);
390 return fclose(fp) == 0;
394 static gboolean combine_include_files(const gchar *outf, GList *file_list)
396 FILE *fp = g_fopen(outf, "w");
397 GList *node = file_list;
399 if (!fp)
400 return FALSE;
402 while (node)
404 const char *fname = node->data;
405 char *contents;
406 size_t length;
407 GError *err = NULL;
409 if (! g_file_get_contents(fname, &contents, &length, &err))
411 fprintf(stderr, "Unable to read file: %s\n", err->message);
412 g_error_free(err);
414 else
416 fwrite(contents, length, 1, fp);
417 fwrite("\n", 1, 1, fp); /* in case file doesn't end in newline (e.g. windows). */
418 g_free(contents);
420 node = g_list_next (node);
423 return fclose(fp) == 0;
427 static gchar *create_temp_file(const gchar *tpl)
429 gchar *name;
430 gint fd;
432 fd = g_file_open_tmp(tpl, &name, NULL);
433 if (fd < 0)
434 name = NULL;
435 else
436 close(fd);
438 return name;
441 static GList *lookup_includes(const gchar **includes, gint includes_count)
443 GList *includes_files = NULL;
444 GHashTable *table; /* used for deduping */
445 gint i;
446 #ifdef HAVE_GLOB_H
447 glob_t globbuf;
448 size_t idx_glob;
449 #endif
451 table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
453 #ifdef HAVE_GLOB_H
454 globbuf.gl_offs = 0;
456 if (includes[0][0] == '"') /* leading \" char for glob matching */
458 for (i = 0; i < includes_count; i++)
460 size_t dirty_len = strlen(includes[i]);
461 gchar *clean_path;
463 if (dirty_len < 2)
464 continue;
466 clean_path = g_malloc(dirty_len - 1);
468 strncpy(clean_path, includes[i] + 1, dirty_len - 1);
469 clean_path[dirty_len - 2] = 0;
471 #ifdef TM_DEBUG
472 g_message ("[o][%s]\n", clean_path);
473 #endif
474 glob(clean_path, 0, NULL, &globbuf);
476 #ifdef TM_DEBUG
477 g_message ("matches: %d\n", globbuf.gl_pathc);
478 #endif
480 for (idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++)
482 #ifdef TM_DEBUG
483 g_message (">>> %s\n", globbuf.gl_pathv[idx_glob]);
484 #endif
485 if (!g_hash_table_lookup(table, globbuf.gl_pathv[idx_glob]))
487 gchar *file_name_copy = g_strdup(globbuf.gl_pathv[idx_glob]);
489 includes_files = g_list_prepend(includes_files, file_name_copy);
490 g_hash_table_insert(table, file_name_copy, file_name_copy);
491 #ifdef TM_DEBUG
492 g_message ("Added ...\n");
493 #endif
496 globfree(&globbuf);
497 g_free(clean_path);
500 else
501 #endif /* HAVE_GLOB_H */
503 /* no glob support or globbing not wanted */
504 for (i = 0; i < includes_count; i++)
506 if (!g_hash_table_lookup(table, includes[i]))
508 gchar* file_name_copy = g_strdup(includes[i]);
510 includes_files = g_list_prepend(includes_files, file_name_copy);
511 g_hash_table_insert(table, file_name_copy, file_name_copy);
516 g_hash_table_destroy(table);
518 return g_list_reverse(includes_files);
521 static gchar *pre_process_file(const gchar *cmd, const gchar *inf)
523 gint ret;
524 gchar *outf = create_temp_file("tmp_XXXXXX.cpp");
525 gchar *tmp_errfile;
526 gchar *errors = NULL;
527 gchar *command;
529 if (!outf)
530 return NULL;
532 tmp_errfile = create_temp_file("tmp_XXXXXX");
533 if (!tmp_errfile)
535 g_unlink(outf);
536 g_free(outf);
537 return NULL;
540 command = g_strdup_printf("%s %s >%s 2>%s",
541 cmd, inf, outf, tmp_errfile);
542 #ifdef TM_DEBUG
543 g_message("Executing: %s", command);
544 #endif
545 ret = system(command);
546 g_free(command);
548 g_file_get_contents(tmp_errfile, &errors, NULL, NULL);
549 if (errors && *errors)
550 g_printerr("%s\n", errors);
551 g_free(errors);
552 g_unlink(tmp_errfile);
553 g_free(tmp_errfile);
555 if (ret == -1)
557 g_unlink(outf);
558 g_free(outf);
559 return NULL;
562 return outf;
565 /* Creates a list of global tags. Ideally, this should be created once during
566 installations so that all users can use the same file. This is because a full
567 scale global tag list can occupy several megabytes of disk space.
568 @param pre_process The pre-processing command. This is executed via system(),
569 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
570 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
571 are allowed.
572 @param tags_file The file where the tags will be stored.
573 @param lang The language to use for the tags file.
574 @return TRUE on success, FALSE on failure.
576 gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes,
577 int includes_count, const char *tags_file, TMParserType lang)
579 gboolean ret = FALSE;
580 TMSourceFile *source_file;
581 GList *includes_files;
582 gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
583 GPtrArray *filtered_tags;
585 if (!temp_file)
586 return FALSE;
588 includes_files = lookup_includes(includes, includes_count);
590 #ifdef TM_DEBUG
591 g_message ("writing out files to %s\n", temp_file);
592 #endif
593 if (pre_process)
594 ret = write_includes_file(temp_file, includes_files);
595 else
596 ret = combine_include_files(temp_file, includes_files);
598 g_list_free_full(includes_files, g_free);
599 if (!ret)
600 goto cleanup;
601 ret = FALSE;
603 if (pre_process)
605 gchar *temp_file2 = pre_process_file(pre_process, temp_file);
607 if (temp_file2)
609 g_unlink(temp_file);
610 g_free(temp_file);
611 temp_file = temp_file2;
613 else
614 goto cleanup;
617 source_file = tm_source_file_new(temp_file, tm_source_file_get_lang_name(lang));
618 if (!source_file)
619 goto cleanup;
620 update_source_file(source_file, NULL, 0, FALSE, FALSE);
621 if (source_file->tags_array->len == 0)
623 tm_source_file_free(source_file);
624 goto cleanup;
627 tm_tags_sort(source_file->tags_array, global_tags_sort_attrs, TRUE, FALSE);
628 filtered_tags = tm_tags_extract(source_file->tags_array, ~tm_tag_local_var_t);
629 ret = tm_source_file_write_tags_file(tags_file, filtered_tags);
630 g_ptr_array_free(filtered_tags, TRUE);
631 tm_source_file_free(source_file);
633 cleanup:
634 g_unlink(temp_file);
635 g_free(temp_file);
636 return ret;
640 static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
641 const char *name, const char *scope, TMTagType type, TMParserType lang)
643 TMTag **tag;
644 guint i, num;
646 if (!src || !dst || !name || !*name)
647 return;
649 tag = tm_tags_find(src, name, FALSE, &num);
650 for (i = 0; i < num; ++i)
652 if ((type & (*tag)->type) &&
653 tm_parser_langs_compatible(lang, (*tag)->lang) &&
654 (!scope || g_strcmp0((*tag)->scope, scope) == 0))
656 g_ptr_array_add(dst, *tag);
658 tag++;
663 /* Returns all matching tags found in the workspace.
664 @param name The name of the tag to find.
665 @param scope The scope name of the tag to find, or NULL.
666 @param type The tag types to return (TMTagType). Can be a bitmask.
667 @param attrs The attributes to sort and dedup on (0 terminated integer array).
668 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
669 -1 for all
670 @return Array of matching tags.
672 GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
673 TMTagAttrType *attrs, TMParserType lang)
675 GPtrArray *tags = g_ptr_array_new();
677 fill_find_tags_array(tags, theWorkspace->tags_array, name, scope, type, lang);
678 fill_find_tags_array(tags, theWorkspace->global_tags, name, scope, type, lang);
680 if (attrs)
681 tm_tags_sort(tags, attrs, TRUE, FALSE);
683 return tags;
687 gboolean tm_workspace_is_autocomplete_tag(TMTag *tag,
688 TMSourceFile *current_file,
689 guint current_line,
690 const gchar *current_scope)
692 TMParserType lang = current_file ? current_file->lang : TM_PARSER_NONE;
694 /* ignore local variables from other files/functions or after current line */
695 gboolean valid = !(tag->type & tm_tag_local_var_t) ||
696 (current_file == tag->file &&
697 current_line >= tag->line &&
698 g_strcmp0(current_scope, tag->scope) == 0);
699 return valid && !tm_tag_is_anon(tag) && tm_parser_langs_compatible(lang, tag->lang);
703 typedef struct
705 TMSourceFile *file;
706 guint line;
707 const gchar *scope;
708 } CopyInfo;
710 static gboolean is_any_tag(TMTag *tag, CopyInfo *info)
712 return TRUE;
715 static gboolean is_local_tag(TMTag *tag, CopyInfo *info)
717 return tag->type & tm_tag_local_var_t;
720 static gboolean is_non_local_tag(TMTag *tag, CopyInfo *info)
722 return !is_local_tag(tag, info);
725 /* non-local tag not from current file */
726 static gboolean is_workspace_tag(TMTag *tag, CopyInfo *info)
728 return tag->file != info->file &&
729 is_non_local_tag(tag, info);
733 static guint copy_tags(GPtrArray *dst, TMTag **src, guint src_len, GHashTable *name_table,
734 gint num, gboolean (*predicate) (TMTag *, CopyInfo *), CopyInfo *info)
736 guint i;
738 g_return_val_if_fail(src && dst, 0);
740 for (i = 0; i < src_len && num > 0; i++)
742 TMTag *tag = *src;
743 if (predicate(tag, info) &&
744 tm_workspace_is_autocomplete_tag(tag, info->file, info->line, info->scope) &&
745 !g_hash_table_contains(name_table, tag->name))
747 g_ptr_array_add(dst, tag);
748 g_hash_table_add(name_table, tag->name);
749 num--;
751 src++;
756 static void fill_find_tags_array_prefix(GPtrArray *dst, const char *name,
757 CopyInfo *info, guint max_num)
759 TMTag **found;
760 guint count;
761 GHashTable *name_table;
763 if (!dst || !name || !*name)
764 return;
766 name_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
768 if (info->file)
770 found = tm_tags_find(info->file->tags_array, name, TRUE, &count);
771 if (found)
773 copy_tags(dst, found, count, name_table, max_num - dst->len, is_local_tag, info);
774 if (dst->len < max_num)
775 copy_tags(dst, found, count, name_table, max_num - dst->len, is_non_local_tag, info);
778 if (dst->len < max_num)
780 found = tm_tags_find(theWorkspace->tags_array, name, TRUE, &count);
781 if (found)
782 copy_tags(dst, found, count, name_table, max_num - dst->len, is_workspace_tag, info);
784 if (dst->len < max_num)
786 found = tm_tags_find(theWorkspace->global_tags, name, TRUE, &count);
787 if (found)
788 copy_tags(dst, found, count, name_table, max_num - dst->len, is_any_tag, info);
791 g_hash_table_unref(name_table);
795 typedef struct
797 TMSourceFile *file;
798 gboolean sort_by_name;
799 } SortInfo;
802 static gint sort_found_tags(gconstpointer a, gconstpointer b, gpointer user_data)
804 SortInfo *info = user_data;
805 const TMTag *t1 = *((TMTag **) a);
806 const TMTag *t2 = *((TMTag **) b);
808 /* sort local vars first (with highest line number first), followed
809 * by tags from current file, followed by workspace tags, followed by
810 * global tags */
811 if (t1->type & tm_tag_local_var_t && t2->type & tm_tag_local_var_t)
812 return info->sort_by_name ? g_strcmp0(t1->name, t2->name) : t2->line - t1->line;
813 else if (t1->type & tm_tag_local_var_t)
814 return -1;
815 else if (t2->type & tm_tag_local_var_t)
816 return 1;
817 else if (t1->file == info->file && t2->file != info->file)
818 return -1;
819 else if (t2->file == info->file && t1->file != info->file)
820 return 1;
821 else if (t1->file && !t2->file)
822 return -1;
823 else if (t2->file && !t1->file)
824 return 1;
825 return g_strcmp0(t1->name, t2->name);
829 /* Returns tags with the specified prefix sorted by name, ignoring local
830 variables from other files/functions or after current line. If there are several
831 tags with the same name, only one of them appears in the resulting array.
832 @param prefix The prefix of the tag to find.
833 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
834 -1 for all.
835 @param max_num The maximum number of tags to return.
836 @return Array of matching tags sorted by their name.
838 GPtrArray *tm_workspace_find_prefix(const char *prefix,
839 TMSourceFile *current_file,
840 guint current_line,
841 const gchar *current_scope,
842 guint max_num)
844 TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
845 GPtrArray *tags = g_ptr_array_new();
846 SortInfo sort_info;
847 CopyInfo copy_info;
849 copy_info.file = current_file;
850 copy_info.line = current_line;
851 copy_info.scope = current_scope;
852 fill_find_tags_array_prefix(tags, prefix, &copy_info, max_num);
854 /* sort based on how "close" the tag is to current line with local
855 * variables first */
856 sort_info.file = current_file;
857 sort_info.sort_by_name = TRUE;
858 g_ptr_array_sort_with_data(tags, sort_found_tags, &sort_info);
860 return tags;
864 static gboolean replace_with_char(gchar *haystack, const gchar *needle, char replacement)
866 gchar *pos = strstr(haystack, needle);
867 if (pos)
869 while (*needle)
871 *pos = replacement;
872 needle++;
873 pos++;
875 return TRUE;
877 return FALSE;
881 static gboolean replace_parens_with_char(gchar *haystack, gchar paren_begin, gchar paren_end, char replacement)
883 gchar needle[2] = {paren_begin, '\0'};
884 gchar *pos = strstr(haystack, needle);
885 gint nesting = 0;
887 if (pos)
889 while (*pos)
891 if (*pos == paren_begin)
892 nesting++;
893 else if (*pos == paren_end)
894 nesting--;
895 *pos = replacement;
896 if (nesting == 0)
897 break;
898 pos++;
900 return TRUE;
902 return FALSE;
906 static gchar *strip_type(const gchar *scoped_name, TMParserType lang, gboolean remove_scope)
908 if (scoped_name != NULL)
910 const gchar *sep = tm_parser_scope_separator(lang);
911 gchar *name = g_strdup(scoped_name);
912 gchar *scope_suffix;
914 /* remove pointers, parens and keywords appearing in types */
915 g_strdelimit(name, "*^&", ' ');
916 while (replace_parens_with_char(name, '[', ']', ' ')) {}
917 while (replace_parens_with_char(name, '<', '>', ' ')) {}
918 while (replace_with_char(name, "const ", ' ')) {}
919 while (replace_with_char(name, " const", ' ')) {}
920 while (replace_with_char(name, " struct", ' ')) {}
921 /* remove everything before final scope separator */
922 if (remove_scope && (scope_suffix = g_strrstr(name, sep)))
924 scope_suffix += strlen(sep);
925 scope_suffix = g_strdup(scope_suffix);
926 g_free(name);
927 name = scope_suffix;
929 g_strstrip(name);
931 return name;
933 return NULL;
937 /* Gets all members of type_tag; search them inside the all array.
938 * The namespace parameter determines whether we are performing the "namespace"
939 * search (user has typed something like "A::" where A is a type) or "scope" search
940 * (user has typed "a." where a is a global struct-like variable). With the
941 * namespace search we return all direct descendants of any type while with the
942 * scope search we return only those which can be invoked on a variable (member,
943 * method, etc.). */
944 static GPtrArray *
945 find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace, guint depth)
947 TMTagType member_types = tm_tag_max_t & ~(TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t);
948 GPtrArray *tags;
949 gchar *scope;
950 guint i;
952 if (depth == 10)
953 return NULL; /* simple inheritence cycle avoidance */
955 tags = g_ptr_array_new();
957 if (namespace)
958 member_types = tm_tag_max_t;
960 if (type_tag->scope && *(type_tag->scope))
961 scope = g_strconcat(type_tag->scope, tm_parser_scope_separator(type_tag->lang), type_tag->name, NULL);
962 else
963 scope = g_strdup(type_tag->name);
965 for (i = 0; i < all->len; ++i)
967 TMTag *tag = TM_TAG (all->pdata[i]);
969 if (tag && (tag->type & member_types) &&
970 tag->scope && tag->scope[0] != '\0' &&
971 tm_parser_langs_compatible(tag->lang, type_tag->lang) &&
972 strcmp(scope, tag->scope) == 0 &&
973 (!namespace || !tm_tag_is_anon(tag)))
975 g_ptr_array_add (tags, tag);
979 /* add members from parent classes */
980 if (!namespace && (type_tag->type & (tm_tag_class_t | tm_tag_struct_t)) &&
981 type_tag->inheritance && *type_tag->inheritance)
983 gchar *stripped = strip_type(type_tag->inheritance, type_tag->lang, FALSE);
984 gchar **split_strv = g_strsplit(stripped, ",", -1); /* parent classes */
985 const gchar *parent;
987 g_free(stripped);
989 for (i = 0; parent = split_strv[i]; i++)
991 GPtrArray *parent_tags;
993 stripped = strip_type(parent, type_tag->lang, TRUE);
994 parent_tags = tm_workspace_find(stripped, NULL, tm_tag_class_t | tm_tag_struct_t,
995 NULL, type_tag->lang);
997 if (parent_tags->len > 0)
999 TMTag *parent_tag = parent_tags->pdata[0];
1000 GPtrArray *parent_members = find_scope_members_tags(
1001 parent_tag->file ? parent_tag->file->tags_array : all, parent_tag,
1002 FALSE, depth + 1);
1004 if (parent_members)
1006 guint j;
1007 for (j = 0; j < parent_members->len; j++)
1008 g_ptr_array_add (tags, parent_members->pdata[j]);
1009 g_ptr_array_free(parent_members, TRUE);
1013 g_ptr_array_free(parent_tags, TRUE);
1014 g_free(stripped);
1017 g_strfreev(split_strv);
1020 g_free(scope);
1022 if (tags->len == 0)
1024 g_ptr_array_free(tags, TRUE);
1025 return NULL;
1028 if (depth == 0)
1030 TMTagAttrType sort_attrs[] = {tm_tag_attr_name_t, 0};
1031 tm_tags_sort(tags, sort_attrs, TRUE, FALSE);
1034 return tags;
1038 /* Gets all members of the type with the given name; search them inside tags_array */
1039 static GPtrArray *
1040 find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile *file,
1041 TMParserType lang, gboolean namespace)
1043 GPtrArray *res = NULL;
1044 gchar *type_name;
1045 guint i;
1047 g_return_val_if_fail(name && *name, NULL);
1049 type_name = g_strdup(name);
1051 /* Check if type_name is a type that can possibly contain members.
1052 * Try to resolve intermediate typedefs to get the real type name. Also
1053 * add scope information to the name if applicable.
1054 * The loop below loops only when resolving typedefs - avoid possibly infinite
1055 * loop when typedefs create a cycle by adding some limits. */
1056 for (i = 0; i < 5; i++)
1058 guint j;
1059 GPtrArray *type_tags;
1060 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
1061 TMTag *tag = NULL;
1063 if (!namespace)
1064 types &= ~tm_tag_enum_t;
1066 type_tags = g_ptr_array_new();
1067 fill_find_tags_array(type_tags, tags_array, type_name, NULL, types, lang);
1069 for (j = 0; j < type_tags->len; j++)
1071 TMTag *test_tag = TM_TAG(type_tags->pdata[j]);
1073 /* anonymous type defined in a different file than the variable -
1074 * this isn't the type we are looking for */
1075 if (tm_tag_is_anon(test_tag) && (file != test_tag->file || test_tag->file == NULL))
1076 continue;
1078 tag = test_tag;
1080 /* prefer non-typedef tags because we can be sure they contain members */
1081 if (test_tag->type != tm_tag_typedef_t)
1082 break;
1085 g_ptr_array_free(type_tags, TRUE);
1087 if (!tag) /* not a type that can contain members */
1088 break;
1090 /* intermediate typedef - resolve to the real type */
1091 if (tag->type == tm_tag_typedef_t)
1093 if (tag->var_type && tag->var_type[0] != '\0')
1095 g_free(type_name);
1096 type_name = strip_type(tag->var_type, tag->lang, TRUE);
1097 file = tag->file;
1098 continue;
1100 break;
1102 else /* real type with members */
1104 /* use the same file as the composite type if file information available */
1105 res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace, 0);
1106 break;
1110 g_free(type_name);
1112 return res;
1116 /* Checks whether a member tag is directly accessible from method */
1117 static gboolean member_accessible(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag,
1118 TMParserType lang)
1120 const gchar *sep = tm_parser_scope_separator(lang);
1121 gboolean ret = FALSE;
1122 gchar **comps;
1123 guint len;
1125 /* method scope is in the form ...::class_name::method_name */
1126 comps = g_strsplit (method_scope, sep, 0);
1127 len = g_strv_length(comps);
1128 if (len > 1)
1130 gchar *cls = comps[len - 2];
1132 if (*cls)
1134 /* find method's class members */
1135 GPtrArray *cls_tags = find_scope_members(tags, cls, NULL, lang, FALSE);
1137 if (cls_tags)
1139 guint i;
1141 /* check if one of the class members is member_tag */
1142 for (i = 0; i < cls_tags->len; i++)
1144 TMTag *t = cls_tags->pdata[i];
1146 if (t == member_tag)
1148 ret = TRUE;
1149 break;
1152 g_ptr_array_free(cls_tags, TRUE);
1157 g_strfreev(comps);
1158 return ret;
1162 /* For an array of variable/type tags, find members inside the types */
1163 static GPtrArray *
1164 find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang,
1165 gboolean member, const gchar *current_scope)
1167 GPtrArray *member_tags = NULL;
1168 guint i;
1170 /* there may be several variables/types with the same name - try each of them until
1171 * we find something */
1172 for (i = 0; i < tags->len && !member_tags; i++)
1174 TMTag *tag = TM_TAG(tags->pdata[i]);
1175 TMTagType member_types = tm_tag_member_t | tm_tag_field_t | tm_tag_method_t;
1176 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
1178 if (tag->type & types) /* type: namespace search */
1180 if (tag->type & tm_tag_typedef_t)
1181 member_tags = find_scope_members(searched_array, tag->name, tag->file, lang, TRUE);
1182 else
1183 member_tags = find_scope_members_tags(tag->file ? tag->file->tags_array : searched_array,
1184 tag, TRUE, 0);
1186 else if (tag->var_type) /* variable: scope search */
1188 /* The question now is whether we should use member tags (such as
1189 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
1190 * (which means user has typed something like foo.bar.) or if we are
1191 * inside a method where foo is a class member, we want scope completion
1192 * for foo. */
1193 if (!(tag->type & member_types) || member ||
1194 member_accessible(searched_array, current_scope, tag, lang))
1196 gchar *tag_type = strip_type(tag->var_type, tag->lang, TRUE);
1198 member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE);
1199 g_free(tag_type);
1204 return member_tags;
1208 static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array)
1210 GPtrArray *member_tags = NULL;
1211 guint i;
1213 for (i = 0; i < tags->len && !member_tags; i++)
1215 TMTag *tag = TM_TAG(tags->pdata[i]);
1217 member_tags = find_scope_members_tags(searched_array, tag, TRUE, 0);
1220 return member_tags;
1224 /* Returns all member tags of a struct/union/class if the provided name is a variable
1225 of such a type or the name of the type.
1226 @param source_file TMSourceFile of the edited source file
1227 @param name Name of the variable/type whose members are searched
1228 @param function TRUE if the name is a name of a function
1229 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1230 @param current_scope The current scope in the editor
1231 @param current_line The current line in the editor
1232 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1233 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1234 GPtrArray *
1235 tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
1236 gboolean function, gboolean member, const gchar *current_scope, guint current_line,
1237 gboolean search_namespace)
1239 TMParserType lang = source_file ? source_file->lang : TM_PARSER_NONE;
1240 GPtrArray *tags, *member_tags = NULL;
1241 TMTagType function_types = tm_tag_function_t | tm_tag_method_t |
1242 tm_tag_macro_with_arg_t | tm_tag_prototype_t;
1243 TMTagType tag_type = tm_tag_max_t &
1244 ~(function_types | tm_tag_enumerator_t | tm_tag_namespace_t | tm_tag_package_t);
1245 TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0};
1247 if (search_namespace)
1249 tags = tm_workspace_find(name, NULL, tm_tag_namespace_t, NULL, lang);
1251 member_tags = find_namespace_members_all(tags, theWorkspace->tags_array);
1252 if (!member_tags)
1253 member_tags = find_namespace_members_all(tags, theWorkspace->global_tags);
1255 g_ptr_array_free(tags, TRUE);
1258 if (!member_tags)
1260 SortInfo info;
1261 guint i;
1263 if (function)
1264 tag_type = function_types;
1266 /* tags corresponding to the variable/type name */
1267 tags = tm_workspace_find(name, NULL, tag_type, NULL, lang);
1269 /* remove invalid local tags and sort tags so "nearest" tags are first */
1270 for (i = 0; i < tags->len; i++)
1272 TMTag *tag = tags->pdata[i];
1273 if (!tm_workspace_is_autocomplete_tag(tag, source_file, current_line, current_scope))
1274 tags->pdata[i] = NULL;
1276 tm_tags_prune(tags);
1278 info.file = source_file;
1279 info.sort_by_name = FALSE;
1280 g_ptr_array_sort_with_data(tags, sort_found_tags, &info);
1282 /* Start searching inside the source file, continue with workspace tags and
1283 * end with global tags. This way we find the "closest" tag to the current
1284 * file in case there are more of them. */
1285 if (source_file)
1286 member_tags = find_scope_members_all(tags, source_file->tags_array,
1287 lang, member, current_scope);
1288 if (!member_tags)
1289 member_tags = find_scope_members_all(tags, theWorkspace->tags_array, lang,
1290 member, current_scope);
1291 if (!member_tags)
1292 member_tags = find_scope_members_all(tags, theWorkspace->global_tags, lang,
1293 member, current_scope);
1295 g_ptr_array_free(tags, TRUE);
1298 if (member_tags)
1299 tm_tags_dedup(member_tags, sort_attr, FALSE);
1301 return member_tags;
1305 #ifdef TM_DEBUG
1307 /* Dumps the workspace tree - useful for debugging */
1308 void tm_workspace_dump(void)
1310 guint i;
1312 #ifdef TM_DEBUG
1313 g_message("Dumping TagManager workspace tree..");
1314 #endif
1315 for (i=0; i < theWorkspace->source_files->len; ++i)
1317 TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
1318 fprintf(stderr, "%s", source_file->file_name);
1321 #endif /* TM_DEBUG */