Remove not useless tm_source_file_ctags_init() indirection
[geany-mirror.git] / tagmanager / src / tm_workspace.c
blob87d4e62da5fe609e7b825036fd2054cd56a4eca3
1 /*
3 * Copyright (c) 2001-2002, Biswapesh Chattopadhyay
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License.
8 */
10 /**
11 * @file tm_workspace.h
12 The TMWorkspace structure is meant to be used as a singleton to store application
13 wide tag information.
15 The workspace is intended to contain a list of global tags
16 and a set of individual source files.
19 #include "general.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <ctype.h>
25 #include <sys/types.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #ifdef HAVE_GLOB_H
29 # include <glob.h>
30 #endif
31 #include <glib/gstdio.h>
33 #include "tm_workspace.h"
34 #include "tm_ctags_wrappers.h"
35 #include "tm_tag.h"
36 #include "tm_parser.h"
39 /* when changing, always keep the three sort criteria below in sync */
40 static TMTagAttrType workspace_tags_sort_attrs[] =
42 tm_tag_attr_name_t, tm_tag_attr_file_t, tm_tag_attr_line_t,
43 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
46 /* for file tags the file is always identical, don't use for sorting */
47 static TMTagAttrType file_tags_sort_attrs[] =
49 tm_tag_attr_name_t, tm_tag_attr_line_t,
50 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
53 /* global tags don't have file/line information */
54 static TMTagAttrType global_tags_sort_attrs[] =
56 tm_tag_attr_name_t,
57 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
60 static TMTagType TM_TYPE_WITH_MEMBERS =
61 tm_tag_class_t | tm_tag_struct_t | tm_tag_union_t |
62 tm_tag_enum_t | tm_tag_interface_t;
64 static TMTagType TM_GLOBAL_TYPE_MASK =
65 tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t |
66 tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t;
68 static TMWorkspace *theWorkspace = NULL;
71 static gboolean tm_create_workspace(void)
73 theWorkspace = g_new(TMWorkspace, 1);
74 theWorkspace->tags_array = g_ptr_array_new();
76 theWorkspace->global_tags = g_ptr_array_new();
77 theWorkspace->source_files = g_ptr_array_new();
78 theWorkspace->typename_array = g_ptr_array_new();
79 theWorkspace->global_typename_array = g_ptr_array_new();
81 tm_ctags_init();
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 guint tm_file_inode_hash(gconstpointer key)
374 GStatBuf file_stat;
375 const char *filename = (const char*)key;
377 if (g_stat(filename, &file_stat) == 0)
379 #ifdef TM_DEBUG
380 g_message ("Hash for '%s' is '%d'\n", filename, file_stat.st_ino);
381 #endif
382 return g_direct_hash ((gpointer)(intptr_t)file_stat.st_ino);
385 return 0;
389 static void tm_move_entries_to_g_list(gpointer key, gpointer value, gpointer user_data)
391 GList **pp_list = (GList**)user_data;
393 if (user_data == NULL)
394 return;
396 *pp_list = g_list_prepend(*pp_list, g_strdup(value));
400 static gboolean write_includes_file(const gchar *outf, GList *includes_files)
402 FILE *fp = g_fopen(outf, "w");
403 GList *node = includes_files;
405 if (!fp)
406 return FALSE;
408 while (node)
410 char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data);
411 size_t str_len = strlen(str);
413 fwrite(str, str_len, 1, fp);
414 g_free(str);
415 node = g_list_next(node);
418 fclose(fp);
420 return TRUE;
424 static gboolean append_to_temp_file(const gchar *outf, GList *file_list)
426 FILE *fp = g_fopen(outf, "w");
427 GList *node = file_list;
429 if (!fp)
430 return FALSE;
432 while (node)
434 const char *fname = node->data;
435 char *contents;
436 size_t length;
437 GError *err = NULL;
439 if (! g_file_get_contents(fname, &contents, &length, &err))
441 fprintf(stderr, "Unable to read file: %s\n", err->message);
442 g_error_free(err);
444 else
446 fwrite(contents, length, 1, fp);
447 fwrite("\n", 1, 1, fp); /* in case file doesn't end in newline (e.g. windows). */
448 g_free(contents);
450 node = g_list_next (node);
453 fclose(fp);
455 return TRUE;
459 static gchar *create_temp_file(const gchar *tpl)
461 gchar *name;
462 gint fd;
464 fd = g_file_open_tmp(tpl, &name, NULL);
465 if (fd < 0)
466 name = NULL;
467 else
468 close(fd);
470 return name;
473 static GList *lookup_includes(const gchar **includes, gint includes_count)
475 GList *includes_files = NULL;
476 GHashTable *table;
477 gint i;
478 #ifdef HAVE_GLOB_H
479 glob_t globbuf;
480 size_t idx_glob;
481 #endif
483 table = g_hash_table_new_full(tm_file_inode_hash, g_direct_equal, NULL, g_free);
485 #ifdef HAVE_GLOB_H
486 globbuf.gl_offs = 0;
488 if (includes[0][0] == '"') /* leading \" char for glob matching */
490 for (i = 0; i < includes_count; i++)
492 size_t dirty_len = strlen(includes[i]);
493 gchar *clean_path = g_malloc(dirty_len - 1);
495 strncpy(clean_path, includes[i] + 1, dirty_len - 1);
496 clean_path[dirty_len - 2] = 0;
498 #ifdef TM_DEBUG
499 g_message ("[o][%s]\n", clean_path);
500 #endif
501 glob(clean_path, 0, NULL, &globbuf);
503 #ifdef TM_DEBUG
504 g_message ("matches: %d\n", globbuf.gl_pathc);
505 #endif
507 for (idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++)
509 #ifdef TM_DEBUG
510 g_message (">>> %s\n", globbuf.gl_pathv[idx_glob]);
511 #endif
512 if (!g_hash_table_lookup(table, globbuf.gl_pathv[idx_glob]))
514 gchar *file_name_copy = strdup(globbuf.gl_pathv[idx_glob]);
516 g_hash_table_insert(table, file_name_copy, file_name_copy);
517 #ifdef TM_DEBUG
518 g_message ("Added ...\n");
519 #endif
522 globfree(&globbuf);
523 g_free(clean_path);
526 else
527 #endif /* HAVE_GLOB_H */
529 /* no glob support or globbing not wanted */
530 for (i = 0; i < includes_count; i++)
532 if (!g_hash_table_lookup(table, includes[i]))
534 gchar* file_name_copy = strdup(includes[i]);
536 g_hash_table_insert(table, file_name_copy, file_name_copy);
541 g_hash_table_foreach(table, tm_move_entries_to_g_list, &includes_files);
542 g_hash_table_destroy(table);
544 return g_list_reverse(includes_files);
547 static gchar *pre_process_file(const gchar *cmd, const gchar *inf)
549 gint ret;
550 gchar *outf = create_temp_file("tmp_XXXXXX.cpp");
551 gchar *tmp_errfile = create_temp_file("tmp_XXXXXX");
552 gchar *errors = NULL;
553 gchar *command;
555 if (!outf)
556 return NULL;
557 if (!tmp_errfile)
559 g_unlink(outf);
560 g_free(outf);
561 return NULL;
564 command = g_strdup_printf("%s %s >%s 2>%s",
565 cmd, inf, outf, tmp_errfile);
566 #ifdef TM_DEBUG
567 g_message("Executing: %s", command);
568 #endif
569 ret = system(command);
570 g_free(command);
572 g_file_get_contents(tmp_errfile, &errors, NULL, NULL);
573 if (errors && *errors)
574 g_printerr("%s", errors);
575 g_free(errors);
576 g_unlink(tmp_errfile);
577 g_free(tmp_errfile);
579 if (ret == -1)
581 g_unlink(outf);
582 g_free(outf);
583 return NULL;
586 return outf;
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 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 includes Include 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, const char **includes,
601 int includes_count, const char *tags_file, TMParserType lang)
603 gboolean ret = FALSE;
604 TMSourceFile *source_file;
605 GList *includes_files;
606 gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
608 if (!temp_file)
609 return FALSE;
611 includes_files = lookup_includes(includes, includes_count);
613 #ifdef TM_DEBUG
614 g_message ("writing out files to %s\n", temp_file);
615 #endif
616 if (pre_process)
617 ret = write_includes_file(temp_file, includes_files);
618 else
619 ret = append_to_temp_file(temp_file, includes_files);
621 g_list_free_full(includes_files, g_free);
622 if (!ret)
623 goto cleanup;
624 ret = FALSE;
626 if (pre_process)
628 gchar *temp_file2 = pre_process_file(pre_process, temp_file);
630 if (temp_file2)
632 g_unlink(temp_file);
633 g_free(temp_file);
634 temp_file = temp_file2;
636 else
637 goto cleanup;
640 source_file = tm_source_file_new(temp_file, tm_source_file_get_lang_name(lang));
641 if (!source_file)
642 goto cleanup;
643 update_source_file(source_file, NULL, 0, FALSE, FALSE);
644 if (source_file->tags_array->len == 0)
646 tm_source_file_free(source_file);
647 goto cleanup;
650 tm_tags_sort(source_file->tags_array, global_tags_sort_attrs, TRUE, FALSE);
651 ret = tm_source_file_write_tags_file(tags_file, source_file->tags_array);
652 tm_source_file_free(source_file);
654 cleanup:
655 g_unlink(temp_file);
656 g_free(temp_file);
657 return ret;
661 static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
662 const char *name, const char *scope, TMTagType type, TMParserType lang)
664 TMTag **tag;
665 guint i, num;
667 if (!src || !dst || !name || !*name)
668 return;
670 tag = tm_tags_find(src, name, FALSE, &num);
671 for (i = 0; i < num; ++i)
673 if ((type & (*tag)->type) &&
674 tm_tag_langs_compatible(lang, (*tag)->lang) &&
675 (!scope || g_strcmp0((*tag)->scope, scope) == 0))
677 g_ptr_array_add(dst, *tag);
679 tag++;
684 /* Returns all matching tags found in the workspace.
685 @param name The name of the tag to find.
686 @param scope The scope name of the tag to find, or NULL.
687 @param type The tag types to return (TMTagType). Can be a bitmask.
688 @param attrs The attributes to sort and dedup on (0 terminated integer array).
689 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
690 -1 for all
691 @return Array of matching tags.
693 GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
694 TMTagAttrType *attrs, TMParserType lang)
696 GPtrArray *tags = g_ptr_array_new();
698 fill_find_tags_array(tags, theWorkspace->tags_array, name, scope, type, lang);
699 fill_find_tags_array(tags, theWorkspace->global_tags, name, scope, type, lang);
701 if (attrs)
702 tm_tags_sort(tags, attrs, TRUE, FALSE);
704 return tags;
708 static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src,
709 const char *name, TMParserType lang, guint max_num)
711 TMTag **tag, *last = NULL;
712 guint i, count, num;
714 if (!src || !dst || !name || !*name)
715 return;
717 num = 0;
718 tag = tm_tags_find(src, name, TRUE, &count);
719 for (i = 0; i < count && num < max_num; ++i)
721 if (tm_tag_langs_compatible(lang, (*tag)->lang) &&
722 !tm_tag_is_anon(*tag) &&
723 (!last || g_strcmp0(last->name, (*tag)->name) != 0))
725 g_ptr_array_add(dst, *tag);
726 last = *tag;
727 num++;
729 tag++;
734 /* Returns tags with the specified prefix sorted by name. If there are several
735 tags with the same name, only one of them appears in the resulting array.
736 @param prefix The prefix of the tag to find.
737 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
738 -1 for all.
739 @param max_num The maximum number of tags to return.
740 @return Array of matching tags sorted by their name.
742 GPtrArray *tm_workspace_find_prefix(const char *prefix, TMParserType lang, guint max_num)
744 TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
745 GPtrArray *tags = g_ptr_array_new();
747 fill_find_tags_array_prefix(tags, theWorkspace->tags_array, prefix, lang, max_num);
748 fill_find_tags_array_prefix(tags, theWorkspace->global_tags, prefix, lang, max_num);
750 tm_tags_sort(tags, attrs, TRUE, FALSE);
751 if (tags->len > max_num)
752 tags->len = max_num;
754 return tags;
758 /* Gets all members of type_tag; search them inside the all array.
759 * The namespace parameter determines whether we are performing the "namespace"
760 * search (user has typed something like "A::" where A is a type) or "scope" search
761 * (user has typed "a." where a is a global struct-like variable). With the
762 * namespace search we return all direct descendants of any type while with the
763 * scope search we return only those which can be invoked on a variable (member,
764 * method, etc.). */
765 static GPtrArray *
766 find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace)
768 TMTagType member_types = tm_tag_max_t & ~(TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t);
769 GPtrArray *tags = g_ptr_array_new();
770 gchar *scope;
771 guint i;
773 if (namespace)
774 member_types = tm_tag_max_t;
776 if (type_tag->scope && *(type_tag->scope))
777 scope = g_strconcat(type_tag->scope, tm_tag_context_separator(type_tag->lang), type_tag->name, NULL);
778 else
779 scope = g_strdup(type_tag->name);
781 for (i = 0; i < all->len; ++i)
783 TMTag *tag = TM_TAG (all->pdata[i]);
785 if (tag && (tag->type & member_types) &&
786 tag->scope && tag->scope[0] != '\0' &&
787 tm_tag_langs_compatible(tag->lang, type_tag->lang) &&
788 strcmp(scope, tag->scope) == 0 &&
789 (!namespace || !tm_tag_is_anon(tag)))
791 g_ptr_array_add (tags, tag);
795 g_free(scope);
797 if (tags->len == 0)
799 g_ptr_array_free(tags, TRUE);
800 return NULL;
803 return tags;
807 static gchar *strip_type(const gchar *scoped_name, TMParserType lang)
809 if (scoped_name != NULL)
811 /* remove scope prefix */
812 const gchar *sep = tm_tag_context_separator(lang);
813 const gchar *base = g_strrstr(scoped_name, sep);
814 gchar *name = base ? g_strdup(base + strlen(sep)) : g_strdup(scoped_name);
816 /* remove pointers */
817 g_strdelimit(name, "*^", ' ');
818 g_strstrip(name);
820 return name;
822 return NULL;
826 /* Gets all members of the type with the given name; search them inside tags_array */
827 static GPtrArray *
828 find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile *file,
829 TMParserType lang, gboolean namespace)
831 GPtrArray *res = NULL;
832 gchar *type_name;
833 guint i;
835 g_return_val_if_fail(name && *name, NULL);
837 type_name = g_strdup(name);
839 /* Check if type_name is a type that can possibly contain members.
840 * Try to resolve intermediate typedefs to get the real type name. Also
841 * add scope information to the name if applicable.
842 * The loop below loops only when resolving typedefs - avoid possibly infinite
843 * loop when typedefs create a cycle by adding some limits. */
844 for (i = 0; i < 5; i++)
846 guint j;
847 GPtrArray *type_tags;
848 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
849 TMTag *tag = NULL;
851 if (!namespace)
852 types &= ~tm_tag_enum_t;
854 type_tags = g_ptr_array_new();
855 fill_find_tags_array(type_tags, tags_array, type_name, NULL, types, lang);
857 for (j = 0; j < type_tags->len; j++)
859 TMTag *test_tag = TM_TAG(type_tags->pdata[j]);
861 /* anonymous type defined in a different file than the variable -
862 * this isn't the type we are looking for */
863 if (tm_tag_is_anon(test_tag) && (file != test_tag->file || test_tag->file == NULL))
864 continue;
866 tag = test_tag;
868 /* prefer non-typedef tags because we can be sure they contain members */
869 if (test_tag->type != tm_tag_typedef_t)
870 break;
873 g_ptr_array_free(type_tags, TRUE);
875 if (!tag) /* not a type that can contain members */
876 break;
878 /* intermediate typedef - resolve to the real type */
879 if (tag->type == tm_tag_typedef_t)
881 if (tag->var_type && tag->var_type[0] != '\0')
883 g_free(type_name);
884 type_name = strip_type(tag->var_type, tag->lang);
885 file = tag->file;
886 continue;
888 break;
890 else /* real type with members */
892 /* use the same file as the composite type if file information available */
893 res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace);
894 break;
898 g_free(type_name);
900 return res;
904 /* Checks whether a member tag is directly accessible from method with method_scope */
905 static gboolean member_at_method_scope(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag,
906 TMParserType lang)
908 const gchar *sep = tm_tag_context_separator(lang);
909 gboolean ret = FALSE;
910 gchar **comps;
911 guint len;
913 /* method scope is in the form ...::class_name::method_name */
914 comps = g_strsplit (method_scope, sep, 0);
915 len = g_strv_length(comps);
916 if (len > 1)
918 gchar *method, *member_scope, *cls, *cls_scope;
920 /* get method/member scope */
921 method = comps[len - 1];
922 comps[len - 1] = NULL;
923 member_scope = g_strjoinv(sep, comps);
924 comps[len - 1] = method;
926 /* get class scope */
927 cls = comps[len - 2];
928 comps[len - 2] = NULL;
929 cls_scope = g_strjoinv(sep, comps);
930 comps[len - 2] = cls;
931 cls_scope = strlen(cls_scope) > 0 ? cls_scope : NULL;
933 /* check whether member inside the class */
934 if (g_strcmp0(member_tag->scope, member_scope) == 0)
936 const GPtrArray *src = member_tag->file ? member_tag->file->tags_array : tags;
937 GPtrArray *cls_tags = g_ptr_array_new();
939 /* check whether the class exists */
940 fill_find_tags_array(cls_tags, src, cls, cls_scope, TM_TYPE_WITH_MEMBERS | tm_tag_namespace_t, lang);
941 ret = cls_tags->len > 0;
942 g_ptr_array_free(cls_tags, TRUE);
945 g_free(cls_scope);
946 g_free(member_scope);
949 g_strfreev(comps);
950 return ret;
954 /* For an array of variable/type tags, find members inside the types */
955 static GPtrArray *
956 find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang,
957 gboolean member, const gchar *current_scope)
959 GPtrArray *member_tags = NULL;
960 guint i;
962 /* there may be several variables/types with the same name - try each of them until
963 * we find something */
964 for (i = 0; i < tags->len && !member_tags; i++)
966 TMTag *tag = TM_TAG(tags->pdata[i]);
967 TMTagType member_types = tm_tag_member_t | tm_tag_field_t | tm_tag_method_t;
968 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
970 if (tag->type & types) /* type: namespace search */
972 if (tag->type & tm_tag_typedef_t)
973 member_tags = find_scope_members(searched_array, tag->name, tag->file, lang, TRUE);
974 else
975 member_tags = find_scope_members_tags(tag->file ? tag->file->tags_array : searched_array,
976 tag, TRUE);
978 else if (tag->var_type) /* variable: scope search */
980 /* The question now is whether we should use member tags (such as
981 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
982 * (which means user has typed something like foo.bar.) or if we are
983 * inside a method where foo is a class member, we want scope completion
984 * for foo. */
985 if (!(tag->type & member_types) || member ||
986 member_at_method_scope(tags, current_scope, tag, lang))
988 gchar *tag_type = strip_type(tag->var_type, tag->lang);
990 member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE);
991 g_free(tag_type);
996 return member_tags;
1000 static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang)
1002 GPtrArray *member_tags = NULL;
1003 guint i;
1005 for (i = 0; i < tags->len && !member_tags; i++)
1007 TMTag *tag = TM_TAG(tags->pdata[i]);
1009 member_tags = find_scope_members_tags(searched_array, tag, TRUE);
1012 return member_tags;
1016 /* Returns all member tags of a struct/union/class if the provided name is a variable
1017 of such a type or the name of the type.
1018 @param source_file TMSourceFile of the edited source file
1019 @param name Name of the variable/type whose members are searched
1020 @param function TRUE if the name is a name of a function
1021 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1022 @param current_scope The current scope in the editor
1023 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1024 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1025 GPtrArray *
1026 tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
1027 gboolean function, gboolean member, const gchar *current_scope, gboolean search_namespace)
1029 TMParserType lang = source_file ? source_file->lang : TM_PARSER_NONE;
1030 GPtrArray *tags, *member_tags = NULL;
1031 TMTagType function_types = tm_tag_function_t | tm_tag_method_t |
1032 tm_tag_macro_with_arg_t | tm_tag_prototype_t;
1033 TMTagType tag_type = tm_tag_max_t &
1034 ~(function_types | tm_tag_enumerator_t | tm_tag_namespace_t | tm_tag_package_t);
1035 TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0};
1037 if (search_namespace)
1039 tags = tm_workspace_find(name, NULL, tm_tag_namespace_t, NULL, lang);
1041 member_tags = find_namespace_members_all(tags, theWorkspace->tags_array, lang);
1042 if (!member_tags)
1043 member_tags = find_namespace_members_all(tags, theWorkspace->global_tags, lang);
1045 g_ptr_array_free(tags, TRUE);
1048 if (!member_tags)
1050 if (function)
1051 tag_type = function_types;
1053 /* tags corresponding to the variable/type name */
1054 tags = tm_workspace_find(name, NULL, tag_type, NULL, lang);
1056 /* Start searching inside the source file, continue with workspace tags and
1057 * end with global tags. This way we find the "closest" tag to the current
1058 * file in case there are more of them. */
1059 if (source_file)
1060 member_tags = find_scope_members_all(tags, source_file->tags_array,
1061 lang, member, current_scope);
1062 if (!member_tags)
1063 member_tags = find_scope_members_all(tags, theWorkspace->tags_array, lang,
1064 member, current_scope);
1065 if (!member_tags)
1066 member_tags = find_scope_members_all(tags, theWorkspace->global_tags, lang,
1067 member, current_scope);
1069 g_ptr_array_free(tags, TRUE);
1072 tm_tags_dedup(member_tags, sort_attr, FALSE);
1074 return member_tags;
1078 #ifdef TM_DEBUG
1080 /* Dumps the workspace tree - useful for debugging */
1081 void tm_workspace_dump(void)
1083 guint i;
1085 #ifdef TM_DEBUG
1086 g_message("Dumping TagManager workspace tree..");
1087 #endif
1088 for (i=0; i < theWorkspace->source_files->len; ++i)
1090 TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
1091 fprintf(stderr, "%s", source_file->file_name);
1094 #endif /* TM_DEBUG */
1097 #if 0
1099 /* Returns a list of parent classes for the given class name
1100 @param name Name of the class
1101 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1102 static const GPtrArray *tm_workspace_get_parents(const gchar *name)
1104 static TMTagAttrType type[] = { tm_tag_attr_name_t, tm_tag_attr_none_t };
1105 static GPtrArray *parents = NULL;
1106 const GPtrArray *matches;
1107 guint i = 0;
1108 guint j;
1109 gchar **klasses;
1110 gchar **klass;
1111 TMTag *tag;
1113 g_return_val_if_fail(name && isalpha(*name),NULL);
1115 if (NULL == parents)
1116 parents = g_ptr_array_new();
1117 else
1118 g_ptr_array_set_size(parents, 0);
1119 matches = tm_workspace_find(name, NULL, tm_tag_class_t, type, -1);
1120 if ((NULL == matches) || (0 == matches->len))
1121 return NULL;
1122 g_ptr_array_add(parents, matches->pdata[0]);
1123 while (i < parents->len)
1125 tag = TM_TAG(parents->pdata[i]);
1126 if ((NULL != tag->inheritance) && (isalpha(tag->inheritance[0])))
1128 klasses = g_strsplit(tag->inheritance, ",", 10);
1129 for (klass = klasses; (NULL != *klass); ++ klass)
1131 for (j=0; j < parents->len; ++j)
1133 if (0 == strcmp(*klass, TM_TAG(parents->pdata[j])->name))
1134 break;
1136 if (parents->len == j)
1138 matches = tm_workspace_find(*klass, NULL, tm_tag_class_t, type, -1);
1139 if ((NULL != matches) && (0 < matches->len))
1140 g_ptr_array_add(parents, matches->pdata[0]);
1143 g_strfreev(klasses);
1145 ++ i;
1147 return parents;
1150 #endif