Move code related to various tag file formats into tm_source_file.c
[geany-mirror.git] / tagmanager / src / tm_workspace.c
blob14275a186478789cc3560c6a14e6b07cc803750f
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_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_source_file_ctags_init();
82 return TRUE;
86 /* Frees the workspace structure and all child source files. Use only when
87 exiting from the main program.
89 void tm_workspace_free(void)
91 guint i;
93 #ifdef TM_DEBUG
94 g_message("Workspace destroyed");
95 #endif
97 for (i=0; i < theWorkspace->source_files->len; ++i)
98 tm_source_file_free(theWorkspace->source_files->pdata[i]);
99 g_ptr_array_free(theWorkspace->source_files, TRUE);
100 tm_tags_array_free(theWorkspace->global_tags, TRUE);
101 g_ptr_array_free(theWorkspace->tags_array, TRUE);
102 g_ptr_array_free(theWorkspace->typename_array, TRUE);
103 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
104 g_free(theWorkspace);
105 theWorkspace = NULL;
109 /* Since TMWorkspace is a singleton, you should not create multiple
110 workspaces, but get a pointer to the workspace whenever required. The first
111 time a pointer is requested, or a source file is added to the workspace,
112 a workspace is created. Subsequent calls to the function will return the
113 created workspace.
115 const TMWorkspace *tm_get_workspace(void)
117 if (NULL == theWorkspace)
118 tm_create_workspace();
119 return theWorkspace;
123 static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_array)
125 GPtrArray *new_tags = tm_tags_merge(*big_array, small_array, workspace_tags_sort_attrs, FALSE);
126 /* tags owned by TMSourceFile - free just the pointer array */
127 g_ptr_array_free(*big_array, TRUE);
128 *big_array = new_tags;
132 static void merge_extracted_tags(GPtrArray **dest, GPtrArray *src, TMTagType tag_types)
134 GPtrArray *arr;
136 arr = tm_tags_extract(src, tag_types);
137 tm_workspace_merge_tags(dest, arr);
138 g_ptr_array_free(arr, TRUE);
142 static void update_source_file(TMSourceFile *source_file, guchar* text_buf,
143 gsize buf_size, gboolean use_buffer, gboolean update_workspace)
145 #ifdef TM_DEBUG
146 g_message("Source file updating based on source file %s", source_file->file_name);
147 #endif
149 if (update_workspace)
151 /* tm_source_file_parse() deletes the tag objects - remove the tags from
152 * workspace while they exist and can be scanned */
153 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
154 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
156 tm_source_file_parse(source_file, text_buf, buf_size, use_buffer);
157 tm_tags_sort(source_file->tags_array, file_tags_sort_attrs, FALSE, TRUE);
158 if (update_workspace)
160 #ifdef TM_DEBUG
161 g_message("Updating workspace from source file");
162 #endif
163 tm_workspace_merge_tags(&theWorkspace->tags_array, source_file->tags_array);
165 merge_extracted_tags(&(theWorkspace->typename_array), source_file->tags_array, TM_GLOBAL_TYPE_MASK);
167 #ifdef TM_DEBUG
168 else
169 g_message("Skipping workspace update because update_workspace is %s",
170 update_workspace?"TRUE":"FALSE");
172 #endif
176 /** Adds a source file to the workspace, parses it and updates the workspace tags.
177 @param source_file The source file to add to the workspace.
179 GEANY_API_SYMBOL
180 void tm_workspace_add_source_file(TMSourceFile *source_file)
182 g_return_if_fail(source_file != NULL);
184 g_ptr_array_add(theWorkspace->source_files, source_file);
185 update_source_file(source_file, NULL, 0, FALSE, TRUE);
189 void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file)
191 g_return_if_fail(source_file != NULL);
193 g_ptr_array_add(theWorkspace->source_files, source_file);
197 /* Updates the source file by reparsing the text-buffer passed as parameter.
198 Ctags will use a parsing based on buffer instead of on files.
199 You should call this function when you don't want a previous saving of the file
200 you're editing. It's useful for a "real-time" updating of the tags.
201 The tags array and the tags themselves are destroyed and re-created, hence any
202 other tag arrays pointing to these tags should be rebuilt as well. All sorting
203 information is also lost.
204 @param source_file The source file to update with a buffer.
205 @param text_buf A text buffer. The user should take care of allocate and free it after
206 the use here.
207 @param buf_size The size of text_buf.
209 void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* text_buf,
210 gsize buf_size)
212 update_source_file(source_file, text_buf, buf_size, TRUE, TRUE);
216 /** Removes a source file from the workspace if it exists. This function also removes
217 the tags belonging to this file from the workspace. To completely free the TMSourceFile
218 pointer call tm_source_file_free() on it.
219 @param source_file Pointer to the source file to be removed.
221 GEANY_API_SYMBOL
222 void tm_workspace_remove_source_file(TMSourceFile *source_file)
224 guint i;
226 g_return_if_fail(source_file != NULL);
228 for (i=0; i < theWorkspace->source_files->len; ++i)
230 if (theWorkspace->source_files->pdata[i] == source_file)
232 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
233 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
234 g_ptr_array_remove_index_fast(theWorkspace->source_files, i);
235 return;
241 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
242 want to globally refresh the workspace. This function does not call tm_source_file_update()
243 which should be called before this function on source files which need to be
244 reparsed.
246 static void tm_workspace_update(void)
248 guint i, j;
249 TMSourceFile *source_file;
251 #ifdef TM_DEBUG
252 g_message("Recreating workspace tags array");
253 #endif
255 g_ptr_array_set_size(theWorkspace->tags_array, 0);
257 #ifdef TM_DEBUG
258 g_message("Total %d objects", theWorkspace->source_files->len);
259 #endif
260 for (i=0; i < theWorkspace->source_files->len; ++i)
262 source_file = theWorkspace->source_files->pdata[i];
263 #ifdef TM_DEBUG
264 g_message("Adding tags of %s", source_file->file_name);
265 #endif
266 if (source_file->tags_array->len > 0)
268 for (j = 0; j < source_file->tags_array->len; ++j)
270 g_ptr_array_add(theWorkspace->tags_array,
271 source_file->tags_array->pdata[j]);
275 #ifdef TM_DEBUG
276 g_message("Total: %d tags", theWorkspace->tags_array->len);
277 #endif
278 tm_tags_sort(theWorkspace->tags_array, workspace_tags_sort_attrs, TRUE, FALSE);
280 g_ptr_array_free(theWorkspace->typename_array, TRUE);
281 theWorkspace->typename_array = tm_tags_extract(theWorkspace->tags_array, TM_GLOBAL_TYPE_MASK);
285 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
286 This is more efficient than calling tm_workspace_add_source_file() and
287 tm_workspace_update_source_file() separately for each of the files.
288 @param source_files @elementtype{TMSourceFile} The source files to be added to the workspace.
290 GEANY_API_SYMBOL
291 void tm_workspace_add_source_files(GPtrArray *source_files)
293 guint i;
295 g_return_if_fail(source_files != NULL);
297 for (i = 0; i < source_files->len; i++)
299 TMSourceFile *source_file = source_files->pdata[i];
301 tm_workspace_add_source_file_noupdate(source_file);
302 update_source_file(source_file, NULL, 0, FALSE, FALSE);
305 tm_workspace_update();
309 /** Removes multiple source files from the workspace and updates the workspace tag
310 arrays. This is more efficient than calling tm_workspace_remove_source_file()
311 separately for each of the files. To completely free the TMSourceFile pointers
312 call tm_source_file_free() on each of them.
313 @param source_files @elementtype{TMSourceFile} The source files to be removed from the workspace.
315 GEANY_API_SYMBOL
316 void tm_workspace_remove_source_files(GPtrArray *source_files)
318 guint i, j;
320 g_return_if_fail(source_files != NULL);
322 //TODO: sort both arrays by pointer value and remove in single pass
323 for (i = 0; i < source_files->len; i++)
325 TMSourceFile *source_file = source_files->pdata[i];
327 for (j = 0; j < theWorkspace->source_files->len; j++)
329 if (theWorkspace->source_files->pdata[j] == source_file)
331 g_ptr_array_remove_index_fast(theWorkspace->source_files, j);
332 break;
337 tm_workspace_update();
341 /* Loads the global tag list from the specified file. The global tag list should
342 have been first created using tm_workspace_create_global_tags().
343 @param tags_file The file containing global tags.
344 @return TRUE on success, FALSE on failure.
345 @see tm_workspace_create_global_tags()
347 gboolean tm_workspace_load_global_tags(const char *tags_file, TMParserType mode)
349 GPtrArray *file_tags, *new_tags;
351 file_tags = tm_source_file_read_tags_file(tags_file, mode);
352 if (!file_tags)
353 return FALSE;
355 tm_tags_sort(file_tags, global_tags_sort_attrs, TRUE, TRUE);
357 /* reorder the whole array, because tm_tags_find expects a sorted array */
358 new_tags = tm_tags_merge(theWorkspace->global_tags,
359 file_tags, global_tags_sort_attrs, TRUE);
360 g_ptr_array_free(theWorkspace->global_tags, TRUE);
361 g_ptr_array_free(file_tags, TRUE);
362 theWorkspace->global_tags = new_tags;
364 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
365 theWorkspace->global_typename_array = tm_tags_extract(new_tags, TM_GLOBAL_TYPE_MASK);
367 return TRUE;
371 static guint tm_file_inode_hash(gconstpointer key)
373 GStatBuf file_stat;
374 const char *filename = (const char*)key;
375 if (g_stat(filename, &file_stat) == 0)
377 #ifdef TM_DEBUG
378 g_message ("Hash for '%s' is '%d'\n", filename, file_stat.st_ino);
379 #endif
380 return g_direct_hash ((gpointer)(intptr_t)file_stat.st_ino);
381 } else {
382 return 0;
387 static void tm_move_entries_to_g_list(gpointer key, gpointer value, gpointer user_data)
389 GList **pp_list = (GList**)user_data;
391 if (user_data == NULL)
392 return;
394 *pp_list = g_list_prepend(*pp_list, value);
398 static void write_includes_file(FILE *fp, GList *includes_files)
400 GList *node;
402 node = includes_files;
403 while (node)
405 char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data);
406 size_t str_len = strlen(str);
408 fwrite(str, str_len, 1, fp);
409 g_free(str);
410 node = g_list_next(node);
415 static void append_to_temp_file(FILE *fp, GList *file_list)
417 GList *node;
419 node = file_list;
420 while (node)
422 const char *fname = node->data;
423 char *contents;
424 size_t length;
425 GError *err = NULL;
427 if (! g_file_get_contents(fname, &contents, &length, &err))
429 fprintf(stderr, "Unable to read file: %s\n", err->message);
430 g_error_free(err);
432 else
434 fwrite(contents, length, 1, fp);
435 fwrite("\n", 1, 1, fp); /* in case file doesn't end in newline (e.g. windows). */
436 g_free(contents);
438 node = g_list_next (node);
443 static gchar *create_temp_file(const gchar *tpl)
445 gchar *name;
446 gint fd;
448 fd = g_file_open_tmp(tpl, &name, NULL);
449 if (fd < 0)
450 name = NULL;
451 else
452 close(fd);
454 return name;
458 /* Creates a list of global tags. Ideally, this should be created once during
459 installations so that all users can use the same file. This is because a full
460 scale global tag list can occupy several megabytes of disk space.
461 @param pre_process The pre-processing command. This is executed via system(),
462 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
463 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
464 are allowed.
465 @param tags_file The file where the tags will be stored.
466 @param lang The language to use for the tags file.
467 @return TRUE on success, FALSE on failure.
469 gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes,
470 int includes_count, const char *tags_file, TMParserType lang)
472 #ifdef HAVE_GLOB_H
473 glob_t globbuf;
474 size_t idx_glob;
475 #endif
476 int idx_inc;
477 char *command;
478 guint i;
479 FILE *fp;
480 TMSourceFile *source_file;
481 GPtrArray *tags_array;
482 GHashTable *includes_files_hash;
483 GList *includes_files = NULL;
484 gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
485 gchar *temp_file2 = create_temp_file("tmp_XXXXXX.cpp");
486 gboolean ret;
488 if (NULL == temp_file || NULL == temp_file2 ||
489 NULL == (fp = g_fopen(temp_file, "w")))
491 g_free(temp_file);
492 g_free(temp_file2);
493 return FALSE;
496 includes_files_hash = g_hash_table_new_full (tm_file_inode_hash,
497 g_direct_equal,
498 NULL, g_free);
500 #ifdef HAVE_GLOB_H
501 globbuf.gl_offs = 0;
503 if (includes[0][0] == '"') /* leading \" char for glob matching */
504 for(idx_inc = 0; idx_inc < includes_count; idx_inc++)
506 size_t dirty_len = strlen(includes[idx_inc]);
507 char *clean_path = g_malloc(dirty_len - 1);
509 strncpy(clean_path, includes[idx_inc] + 1, dirty_len - 1);
510 clean_path[dirty_len - 2] = 0;
512 #ifdef TM_DEBUG
513 g_message ("[o][%s]\n", clean_path);
514 #endif
515 glob(clean_path, 0, NULL, &globbuf);
517 #ifdef TM_DEBUG
518 g_message ("matches: %d\n", globbuf.gl_pathc);
519 #endif
521 for(idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++)
523 #ifdef TM_DEBUG
524 g_message (">>> %s\n", globbuf.gl_pathv[idx_glob]);
525 #endif
526 if (!g_hash_table_lookup(includes_files_hash,
527 globbuf.gl_pathv[idx_glob]))
529 char* file_name_copy = strdup(globbuf.gl_pathv[idx_glob]);
530 g_hash_table_insert(includes_files_hash, file_name_copy,
531 file_name_copy);
532 #ifdef TM_DEBUG
533 g_message ("Added ...\n");
534 #endif
537 globfree(&globbuf);
538 g_free(clean_path);
540 else
541 #endif
542 /* no glob support or globbing not wanted */
543 for(idx_inc = 0; idx_inc < includes_count; idx_inc++)
545 if (!g_hash_table_lookup(includes_files_hash,
546 includes[idx_inc]))
548 char* file_name_copy = strdup(includes[idx_inc]);
549 g_hash_table_insert(includes_files_hash, file_name_copy,
550 file_name_copy);
554 /* Checks for duplicate file entries which would case trouble */
555 g_hash_table_foreach(includes_files_hash, tm_move_entries_to_g_list,
556 &includes_files);
558 includes_files = g_list_reverse (includes_files);
560 #ifdef TM_DEBUG
561 g_message ("writing out files to %s\n", temp_file);
562 #endif
563 if (pre_process != NULL)
564 write_includes_file(fp, includes_files);
565 else
566 append_to_temp_file(fp, includes_files);
568 g_list_free (includes_files);
569 g_hash_table_destroy(includes_files_hash);
570 includes_files_hash = NULL;
571 includes_files = NULL;
572 fclose(fp);
574 if (pre_process != NULL)
576 gint ret;
577 gchar *tmp_errfile = create_temp_file("tmp_XXXXXX");
578 gchar *errors = NULL;
579 command = g_strdup_printf("%s %s >%s 2>%s",
580 pre_process, temp_file, temp_file2, tmp_errfile);
581 #ifdef TM_DEBUG
582 g_message("Executing: %s", command);
583 #endif
584 ret = system(command);
585 g_free(command);
586 g_unlink(temp_file);
587 g_free(temp_file);
588 g_file_get_contents(tmp_errfile, &errors, NULL, NULL);
589 if (errors && *errors)
590 g_printerr("%s", errors);
591 g_free(errors);
592 g_unlink(tmp_errfile);
593 g_free(tmp_errfile);
594 if (ret == -1)
596 g_unlink(temp_file2);
597 return FALSE;
600 else
602 /* no pre-processing needed, so temp_file2 = temp_file */
603 g_unlink(temp_file2);
604 g_free(temp_file2);
605 temp_file2 = temp_file;
606 temp_file = NULL;
608 source_file = tm_source_file_new(temp_file2, tm_source_file_get_lang_name(lang));
609 update_source_file(source_file, NULL, 0, FALSE, FALSE);
610 if (NULL == source_file)
612 g_unlink(temp_file2);
613 return FALSE;
615 g_unlink(temp_file2);
616 g_free(temp_file2);
617 if (0 == source_file->tags_array->len)
619 tm_source_file_free(source_file);
620 return FALSE;
622 tags_array = tm_tags_extract(source_file->tags_array, tm_tag_max_t);
623 if ((NULL == tags_array) || (0 == tags_array->len))
625 if (tags_array)
626 g_ptr_array_free(tags_array, TRUE);
627 tm_source_file_free(source_file);
628 return FALSE;
630 if (FALSE == tm_tags_sort(tags_array, global_tags_sort_attrs, TRUE, FALSE))
632 tm_source_file_free(source_file);
633 return FALSE;
635 ret = tm_source_file_write_tags_file(tags_file, tags_array);
636 tm_source_file_free(source_file);
637 g_ptr_array_free(tags_array, TRUE);
638 return ret;
642 static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
643 const char *name, const char *scope, TMTagType type, TMParserType lang)
645 TMTag **tag;
646 guint i, num;
648 if (!src || !dst || !name || !*name)
649 return;
651 tag = tm_tags_find(src, name, FALSE, &num);
652 for (i = 0; i < num; ++i)
654 if ((type & (*tag)->type) &&
655 tm_tag_langs_compatible(lang, (*tag)->lang) &&
656 (!scope || g_strcmp0((*tag)->scope, scope) == 0))
658 g_ptr_array_add(dst, *tag);
660 tag++;
665 /* Returns all matching tags found in the workspace.
666 @param name The name of the tag to find.
667 @param scope The scope name of the tag to find, or NULL.
668 @param type The tag types to return (TMTagType). Can be a bitmask.
669 @param attrs The attributes to sort and dedup on (0 terminated integer array).
670 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
671 -1 for all
672 @return Array of matching tags.
674 GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
675 TMTagAttrType *attrs, TMParserType lang)
677 GPtrArray *tags = g_ptr_array_new();
679 fill_find_tags_array(tags, theWorkspace->tags_array, name, scope, type, lang);
680 fill_find_tags_array(tags, theWorkspace->global_tags, name, scope, type, lang);
682 if (attrs)
683 tm_tags_sort(tags, attrs, TRUE, FALSE);
685 return tags;
689 static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src,
690 const char *name, TMParserType lang, guint max_num)
692 TMTag **tag, *last = NULL;
693 guint i, count, num;
695 if (!src || !dst || !name || !*name)
696 return;
698 num = 0;
699 tag = tm_tags_find(src, name, TRUE, &count);
700 for (i = 0; i < count && num < max_num; ++i)
702 if (tm_tag_langs_compatible(lang, (*tag)->lang) &&
703 !tm_tag_is_anon(*tag) &&
704 (!last || g_strcmp0(last->name, (*tag)->name) != 0))
706 g_ptr_array_add(dst, *tag);
707 last = *tag;
708 num++;
710 tag++;
715 /* Returns tags with the specified prefix sorted by name. If there are several
716 tags with the same name, only one of them appears in the resulting array.
717 @param prefix The prefix of the tag to find.
718 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
719 -1 for all.
720 @param max_num The maximum number of tags to return.
721 @return Array of matching tags sorted by their name.
723 GPtrArray *tm_workspace_find_prefix(const char *prefix, TMParserType lang, guint max_num)
725 TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
726 GPtrArray *tags = g_ptr_array_new();
728 fill_find_tags_array_prefix(tags, theWorkspace->tags_array, prefix, lang, max_num);
729 fill_find_tags_array_prefix(tags, theWorkspace->global_tags, prefix, lang, max_num);
731 tm_tags_sort(tags, attrs, TRUE, FALSE);
732 if (tags->len > max_num)
733 tags->len = max_num;
735 return tags;
739 /* Gets all members of type_tag; search them inside the all array.
740 * The namespace parameter determines whether we are performing the "namespace"
741 * search (user has typed something like "A::" where A is a type) or "scope" search
742 * (user has typed "a." where a is a global struct-like variable). With the
743 * namespace search we return all direct descendants of any type while with the
744 * scope search we return only those which can be invoked on a variable (member,
745 * method, etc.). */
746 static GPtrArray *
747 find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace)
749 TMTagType member_types = tm_tag_max_t & ~(TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t);
750 GPtrArray *tags = g_ptr_array_new();
751 gchar *scope;
752 guint i;
754 if (namespace)
755 member_types = tm_tag_max_t;
757 if (type_tag->scope && *(type_tag->scope))
758 scope = g_strconcat(type_tag->scope, tm_tag_context_separator(type_tag->lang), type_tag->name, NULL);
759 else
760 scope = g_strdup(type_tag->name);
762 for (i = 0; i < all->len; ++i)
764 TMTag *tag = TM_TAG (all->pdata[i]);
766 if (tag && (tag->type & member_types) &&
767 tag->scope && tag->scope[0] != '\0' &&
768 tm_tag_langs_compatible(tag->lang, type_tag->lang) &&
769 strcmp(scope, tag->scope) == 0 &&
770 (!namespace || !tm_tag_is_anon(tag)))
772 g_ptr_array_add (tags, tag);
776 g_free(scope);
778 if (tags->len == 0)
780 g_ptr_array_free(tags, TRUE);
781 return NULL;
784 return tags;
788 static gchar *strip_type(const gchar *scoped_name, TMParserType lang)
790 if (scoped_name != NULL)
792 /* remove scope prefix */
793 const gchar *sep = tm_tag_context_separator(lang);
794 const gchar *base = g_strrstr(scoped_name, sep);
795 gchar *name = base ? g_strdup(base + strlen(sep)) : g_strdup(scoped_name);
797 /* remove pointers */
798 g_strdelimit(name, "*^", ' ');
799 g_strstrip(name);
801 return name;
803 return NULL;
807 /* Gets all members of the type with the given name; search them inside tags_array */
808 static GPtrArray *
809 find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile *file,
810 TMParserType lang, gboolean namespace)
812 GPtrArray *res = NULL;
813 gchar *type_name;
814 guint i;
816 g_return_val_if_fail(name && *name, NULL);
818 type_name = g_strdup(name);
820 /* Check if type_name is a type that can possibly contain members.
821 * Try to resolve intermediate typedefs to get the real type name. Also
822 * add scope information to the name if applicable.
823 * The loop below loops only when resolving typedefs - avoid possibly infinite
824 * loop when typedefs create a cycle by adding some limits. */
825 for (i = 0; i < 5; i++)
827 guint j;
828 GPtrArray *type_tags;
829 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
830 TMTag *tag = NULL;
832 if (!namespace)
833 types &= ~tm_tag_enum_t;
835 type_tags = g_ptr_array_new();
836 fill_find_tags_array(type_tags, tags_array, type_name, NULL, types, lang);
838 for (j = 0; j < type_tags->len; j++)
840 TMTag *test_tag = TM_TAG(type_tags->pdata[j]);
842 /* anonymous type defined in a different file than the variable -
843 * this isn't the type we are looking for */
844 if (tm_tag_is_anon(test_tag) && (file != test_tag->file || test_tag->file == NULL))
845 continue;
847 tag = test_tag;
849 /* prefer non-typedef tags because we can be sure they contain members */
850 if (test_tag->type != tm_tag_typedef_t)
851 break;
854 g_ptr_array_free(type_tags, TRUE);
856 if (!tag) /* not a type that can contain members */
857 break;
859 /* intermediate typedef - resolve to the real type */
860 if (tag->type == tm_tag_typedef_t)
862 if (tag->var_type && tag->var_type[0] != '\0')
864 g_free(type_name);
865 type_name = strip_type(tag->var_type, tag->lang);
866 file = tag->file;
867 continue;
869 break;
871 else /* real type with members */
873 /* use the same file as the composite type if file information available */
874 res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace);
875 break;
879 g_free(type_name);
881 return res;
885 /* Checks whether a member tag is directly accessible from method with method_scope */
886 static gboolean member_at_method_scope(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag,
887 TMParserType lang)
889 const gchar *sep = tm_tag_context_separator(lang);
890 gboolean ret = FALSE;
891 gchar **comps;
892 guint len;
894 /* method scope is in the form ...::class_name::method_name */
895 comps = g_strsplit (method_scope, sep, 0);
896 len = g_strv_length(comps);
897 if (len > 1)
899 gchar *method, *member_scope, *cls, *cls_scope;
901 /* get method/member scope */
902 method = comps[len - 1];
903 comps[len - 1] = NULL;
904 member_scope = g_strjoinv(sep, comps);
905 comps[len - 1] = method;
907 /* get class scope */
908 cls = comps[len - 2];
909 comps[len - 2] = NULL;
910 cls_scope = g_strjoinv(sep, comps);
911 comps[len - 2] = cls;
912 cls_scope = strlen(cls_scope) > 0 ? cls_scope : NULL;
914 /* check whether member inside the class */
915 if (g_strcmp0(member_tag->scope, member_scope) == 0)
917 const GPtrArray *src = member_tag->file ? member_tag->file->tags_array : tags;
918 GPtrArray *cls_tags = g_ptr_array_new();
920 /* check whether the class exists */
921 fill_find_tags_array(cls_tags, src, cls, cls_scope, TM_TYPE_WITH_MEMBERS | tm_tag_namespace_t, lang);
922 ret = cls_tags->len > 0;
923 g_ptr_array_free(cls_tags, TRUE);
926 g_free(cls_scope);
927 g_free(member_scope);
930 g_strfreev(comps);
931 return ret;
935 /* For an array of variable/type tags, find members inside the types */
936 static GPtrArray *
937 find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang,
938 gboolean member, const gchar *current_scope)
940 GPtrArray *member_tags = NULL;
941 guint i;
943 /* there may be several variables/types with the same name - try each of them until
944 * we find something */
945 for (i = 0; i < tags->len && !member_tags; i++)
947 TMTag *tag = TM_TAG(tags->pdata[i]);
948 TMTagType member_types = tm_tag_member_t | tm_tag_field_t | tm_tag_method_t;
949 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
951 if (tag->type & types) /* type: namespace search */
953 if (tag->type & tm_tag_typedef_t)
954 member_tags = find_scope_members(searched_array, tag->name, tag->file, lang, TRUE);
955 else
956 member_tags = find_scope_members_tags(tag->file ? tag->file->tags_array : searched_array,
957 tag, TRUE);
959 else if (tag->var_type) /* variable: scope search */
961 /* The question now is whether we should use member tags (such as
962 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
963 * (which means user has typed something like foo.bar.) or if we are
964 * inside a method where foo is a class member, we want scope completion
965 * for foo. */
966 if (!(tag->type & member_types) || member ||
967 member_at_method_scope(tags, current_scope, tag, lang))
969 gchar *tag_type = strip_type(tag->var_type, tag->lang);
971 member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE);
972 g_free(tag_type);
977 return member_tags;
981 static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang)
983 GPtrArray *member_tags = NULL;
984 guint i;
986 for (i = 0; i < tags->len && !member_tags; i++)
988 TMTag *tag = TM_TAG(tags->pdata[i]);
990 member_tags = find_scope_members_tags(searched_array, tag, TRUE);
993 return member_tags;
997 /* Returns all member tags of a struct/union/class if the provided name is a variable
998 of such a type or the name of the type.
999 @param source_file TMSourceFile of the edited source file
1000 @param name Name of the variable/type whose members are searched
1001 @param function TRUE if the name is a name of a function
1002 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1003 @param current_scope The current scope in the editor
1004 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1005 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1006 GPtrArray *
1007 tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
1008 gboolean function, gboolean member, const gchar *current_scope, gboolean search_namespace)
1010 TMParserType lang = source_file ? source_file->lang : TM_PARSER_NONE;
1011 GPtrArray *tags, *member_tags = NULL;
1012 TMTagType function_types = tm_tag_function_t | tm_tag_method_t |
1013 tm_tag_macro_with_arg_t | tm_tag_prototype_t;
1014 TMTagType tag_type = tm_tag_max_t &
1015 ~(function_types | tm_tag_enumerator_t | tm_tag_namespace_t | tm_tag_package_t);
1016 TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0};
1018 if (search_namespace)
1020 tags = tm_workspace_find(name, NULL, tm_tag_namespace_t, NULL, lang);
1022 member_tags = find_namespace_members_all(tags, theWorkspace->tags_array, lang);
1023 if (!member_tags)
1024 member_tags = find_namespace_members_all(tags, theWorkspace->global_tags, lang);
1026 g_ptr_array_free(tags, TRUE);
1029 if (!member_tags)
1031 if (function)
1032 tag_type = function_types;
1034 /* tags corresponding to the variable/type name */
1035 tags = tm_workspace_find(name, NULL, tag_type, NULL, lang);
1037 /* Start searching inside the source file, continue with workspace tags and
1038 * end with global tags. This way we find the "closest" tag to the current
1039 * file in case there are more of them. */
1040 if (source_file)
1041 member_tags = find_scope_members_all(tags, source_file->tags_array,
1042 lang, member, current_scope);
1043 if (!member_tags)
1044 member_tags = find_scope_members_all(tags, theWorkspace->tags_array, lang,
1045 member, current_scope);
1046 if (!member_tags)
1047 member_tags = find_scope_members_all(tags, theWorkspace->global_tags, lang,
1048 member, current_scope);
1050 g_ptr_array_free(tags, TRUE);
1053 tm_tags_dedup(member_tags, sort_attr, FALSE);
1055 return member_tags;
1059 #ifdef TM_DEBUG
1061 /* Dumps the workspace tree - useful for debugging */
1062 void tm_workspace_dump(void)
1064 guint i;
1066 #ifdef TM_DEBUG
1067 g_message("Dumping TagManager workspace tree..");
1068 #endif
1069 for (i=0; i < theWorkspace->source_files->len; ++i)
1071 TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
1072 fprintf(stderr, "%s", source_file->file_name);
1075 #endif /* TM_DEBUG */
1078 #if 0
1080 /* Returns a list of parent classes for the given class name
1081 @param name Name of the class
1082 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1083 static const GPtrArray *tm_workspace_get_parents(const gchar *name)
1085 static TMTagAttrType type[] = { tm_tag_attr_name_t, tm_tag_attr_none_t };
1086 static GPtrArray *parents = NULL;
1087 const GPtrArray *matches;
1088 guint i = 0;
1089 guint j;
1090 gchar **klasses;
1091 gchar **klass;
1092 TMTag *tag;
1094 g_return_val_if_fail(name && isalpha(*name),NULL);
1096 if (NULL == parents)
1097 parents = g_ptr_array_new();
1098 else
1099 g_ptr_array_set_size(parents, 0);
1100 matches = tm_workspace_find(name, NULL, tm_tag_class_t, type, -1);
1101 if ((NULL == matches) || (0 == matches->len))
1102 return NULL;
1103 g_ptr_array_add(parents, matches->pdata[0]);
1104 while (i < parents->len)
1106 tag = TM_TAG(parents->pdata[i]);
1107 if ((NULL != tag->inheritance) && (isalpha(tag->inheritance[0])))
1109 klasses = g_strsplit(tag->inheritance, ",", 10);
1110 for (klass = klasses; (NULL != *klass); ++ klass)
1112 for (j=0; j < parents->len; ++j)
1114 if (0 == strcmp(*klass, TM_TAG(parents->pdata[j])->name))
1115 break;
1117 if (parents->len == j)
1119 matches = tm_workspace_find(*klass, NULL, tm_tag_class_t, type, -1);
1120 if ((NULL != matches) && (0 < matches->len))
1121 g_ptr_array_add(parents, matches->pdata[0]);
1124 g_strfreev(klasses);
1126 ++ i;
1128 return parents;
1131 #endif