Check for fclose() success
[geany-mirror.git] / tagmanager / src / tm_workspace.c
blobdff9fa465b0ad920b448c4125e31bc09dfd064fe
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 return fclose(fp) == 0;
422 static gboolean combine_include_files(const gchar *outf, GList *file_list)
424 FILE *fp = g_fopen(outf, "w");
425 GList *node = file_list;
427 if (!fp)
428 return FALSE;
430 while (node)
432 const char *fname = node->data;
433 char *contents;
434 size_t length;
435 GError *err = NULL;
437 if (! g_file_get_contents(fname, &contents, &length, &err))
439 fprintf(stderr, "Unable to read file: %s\n", err->message);
440 g_error_free(err);
442 else
444 fwrite(contents, length, 1, fp);
445 fwrite("\n", 1, 1, fp); /* in case file doesn't end in newline (e.g. windows). */
446 g_free(contents);
448 node = g_list_next (node);
451 return fclose(fp) == 0;
455 static gchar *create_temp_file(const gchar *tpl)
457 gchar *name;
458 gint fd;
460 fd = g_file_open_tmp(tpl, &name, NULL);
461 if (fd < 0)
462 name = NULL;
463 else
464 close(fd);
466 return name;
469 static GList *lookup_includes(const gchar **includes, gint includes_count)
471 GList *includes_files = NULL;
472 GHashTable *table;
473 gint i;
474 #ifdef HAVE_GLOB_H
475 glob_t globbuf;
476 size_t idx_glob;
477 #endif
479 table = g_hash_table_new_full(tm_file_inode_hash, g_direct_equal, NULL, g_free);
481 #ifdef HAVE_GLOB_H
482 globbuf.gl_offs = 0;
484 if (includes[0][0] == '"') /* leading \" char for glob matching */
486 for (i = 0; i < includes_count; i++)
488 size_t dirty_len = strlen(includes[i]);
489 gchar *clean_path;
491 if (dirty_len < 2)
492 continue;
494 clean_path = g_malloc(dirty_len - 1);
496 strncpy(clean_path, includes[i] + 1, dirty_len - 1);
497 clean_path[dirty_len - 2] = 0;
499 #ifdef TM_DEBUG
500 g_message ("[o][%s]\n", clean_path);
501 #endif
502 glob(clean_path, 0, NULL, &globbuf);
504 #ifdef TM_DEBUG
505 g_message ("matches: %d\n", globbuf.gl_pathc);
506 #endif
508 for (idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++)
510 #ifdef TM_DEBUG
511 g_message (">>> %s\n", globbuf.gl_pathv[idx_glob]);
512 #endif
513 if (!g_hash_table_lookup(table, globbuf.gl_pathv[idx_glob]))
515 gchar *file_name_copy = g_strdup(globbuf.gl_pathv[idx_glob]);
517 g_hash_table_insert(table, file_name_copy, file_name_copy);
518 #ifdef TM_DEBUG
519 g_message ("Added ...\n");
520 #endif
523 globfree(&globbuf);
524 g_free(clean_path);
527 else
528 #endif /* HAVE_GLOB_H */
530 /* no glob support or globbing not wanted */
531 for (i = 0; i < includes_count; i++)
533 if (!g_hash_table_lookup(table, includes[i]))
535 gchar* file_name_copy = g_strdup(includes[i]);
537 g_hash_table_insert(table, file_name_copy, file_name_copy);
542 g_hash_table_foreach(table, tm_move_entries_to_g_list, &includes_files);
543 g_hash_table_destroy(table);
545 return g_list_reverse(includes_files);
548 static gchar *pre_process_file(const gchar *cmd, const gchar *inf)
550 gint ret;
551 gchar *outf = create_temp_file("tmp_XXXXXX.cpp");
552 gchar *tmp_errfile;
553 gchar *errors = NULL;
554 gchar *command;
556 if (!outf)
557 return NULL;
559 tmp_errfile = create_temp_file("tmp_XXXXXX");
560 if (!tmp_errfile)
562 g_unlink(outf);
563 g_free(outf);
564 return NULL;
567 command = g_strdup_printf("%s %s >%s 2>%s",
568 cmd, inf, outf, tmp_errfile);
569 #ifdef TM_DEBUG
570 g_message("Executing: %s", command);
571 #endif
572 ret = system(command);
573 g_free(command);
575 g_file_get_contents(tmp_errfile, &errors, NULL, NULL);
576 if (errors && *errors)
577 g_printerr("%s", errors);
578 g_free(errors);
579 g_unlink(tmp_errfile);
580 g_free(tmp_errfile);
582 if (ret == -1)
584 g_unlink(outf);
585 g_free(outf);
586 return NULL;
589 return outf;
592 /* Creates a list of global tags. Ideally, this should be created once during
593 installations so that all users can use the same file. This is because a full
594 scale global tag list can occupy several megabytes of disk space.
595 @param pre_process The pre-processing command. This is executed via system(),
596 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
597 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
598 are allowed.
599 @param tags_file The file where the tags will be stored.
600 @param lang The language to use for the tags file.
601 @return TRUE on success, FALSE on failure.
603 gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes,
604 int includes_count, const char *tags_file, TMParserType lang)
606 gboolean ret = FALSE;
607 TMSourceFile *source_file;
608 GList *includes_files;
609 gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
611 if (!temp_file)
612 return FALSE;
614 includes_files = lookup_includes(includes, includes_count);
616 #ifdef TM_DEBUG
617 g_message ("writing out files to %s\n", temp_file);
618 #endif
619 if (pre_process)
620 ret = write_includes_file(temp_file, includes_files);
621 else
622 ret = combine_include_files(temp_file, includes_files);
624 g_list_free_full(includes_files, g_free);
625 if (!ret)
626 goto cleanup;
627 ret = FALSE;
629 if (pre_process)
631 gchar *temp_file2 = pre_process_file(pre_process, temp_file);
633 if (temp_file2)
635 g_unlink(temp_file);
636 g_free(temp_file);
637 temp_file = temp_file2;
639 else
640 goto cleanup;
643 source_file = tm_source_file_new(temp_file, tm_source_file_get_lang_name(lang));
644 if (!source_file)
645 goto cleanup;
646 update_source_file(source_file, NULL, 0, FALSE, FALSE);
647 if (source_file->tags_array->len == 0)
649 tm_source_file_free(source_file);
650 goto cleanup;
653 tm_tags_sort(source_file->tags_array, global_tags_sort_attrs, TRUE, FALSE);
654 ret = tm_source_file_write_tags_file(tags_file, source_file->tags_array);
655 tm_source_file_free(source_file);
657 cleanup:
658 g_unlink(temp_file);
659 g_free(temp_file);
660 return ret;
664 static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
665 const char *name, const char *scope, TMTagType type, TMParserType lang)
667 TMTag **tag;
668 guint i, num;
670 if (!src || !dst || !name || !*name)
671 return;
673 tag = tm_tags_find(src, name, FALSE, &num);
674 for (i = 0; i < num; ++i)
676 if ((type & (*tag)->type) &&
677 tm_tag_langs_compatible(lang, (*tag)->lang) &&
678 (!scope || g_strcmp0((*tag)->scope, scope) == 0))
680 g_ptr_array_add(dst, *tag);
682 tag++;
687 /* Returns all matching tags found in the workspace.
688 @param name The name of the tag to find.
689 @param scope The scope name of the tag to find, or NULL.
690 @param type The tag types to return (TMTagType). Can be a bitmask.
691 @param attrs The attributes to sort and dedup on (0 terminated integer array).
692 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
693 -1 for all
694 @return Array of matching tags.
696 GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
697 TMTagAttrType *attrs, TMParserType lang)
699 GPtrArray *tags = g_ptr_array_new();
701 fill_find_tags_array(tags, theWorkspace->tags_array, name, scope, type, lang);
702 fill_find_tags_array(tags, theWorkspace->global_tags, name, scope, type, lang);
704 if (attrs)
705 tm_tags_sort(tags, attrs, TRUE, FALSE);
707 return tags;
711 static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src,
712 const char *name, TMParserType lang, guint max_num)
714 TMTag **tag, *last = NULL;
715 guint i, count, num;
717 if (!src || !dst || !name || !*name)
718 return;
720 num = 0;
721 tag = tm_tags_find(src, name, TRUE, &count);
722 for (i = 0; i < count && num < max_num; ++i)
724 if (tm_tag_langs_compatible(lang, (*tag)->lang) &&
725 !tm_tag_is_anon(*tag) &&
726 (!last || g_strcmp0(last->name, (*tag)->name) != 0))
728 g_ptr_array_add(dst, *tag);
729 last = *tag;
730 num++;
732 tag++;
737 /* Returns tags with the specified prefix sorted by name. If there are several
738 tags with the same name, only one of them appears in the resulting array.
739 @param prefix The prefix of the tag to find.
740 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
741 -1 for all.
742 @param max_num The maximum number of tags to return.
743 @return Array of matching tags sorted by their name.
745 GPtrArray *tm_workspace_find_prefix(const char *prefix, TMParserType lang, guint max_num)
747 TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
748 GPtrArray *tags = g_ptr_array_new();
750 fill_find_tags_array_prefix(tags, theWorkspace->tags_array, prefix, lang, max_num);
751 fill_find_tags_array_prefix(tags, theWorkspace->global_tags, prefix, lang, max_num);
753 tm_tags_sort(tags, attrs, TRUE, FALSE);
754 if (tags->len > max_num)
755 tags->len = max_num;
757 return tags;
761 /* Gets all members of type_tag; search them inside the all array.
762 * The namespace parameter determines whether we are performing the "namespace"
763 * search (user has typed something like "A::" where A is a type) or "scope" search
764 * (user has typed "a." where a is a global struct-like variable). With the
765 * namespace search we return all direct descendants of any type while with the
766 * scope search we return only those which can be invoked on a variable (member,
767 * method, etc.). */
768 static GPtrArray *
769 find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace)
771 TMTagType member_types = tm_tag_max_t & ~(TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t);
772 GPtrArray *tags = g_ptr_array_new();
773 gchar *scope;
774 guint i;
776 if (namespace)
777 member_types = tm_tag_max_t;
779 if (type_tag->scope && *(type_tag->scope))
780 scope = g_strconcat(type_tag->scope, tm_tag_context_separator(type_tag->lang), type_tag->name, NULL);
781 else
782 scope = g_strdup(type_tag->name);
784 for (i = 0; i < all->len; ++i)
786 TMTag *tag = TM_TAG (all->pdata[i]);
788 if (tag && (tag->type & member_types) &&
789 tag->scope && tag->scope[0] != '\0' &&
790 tm_tag_langs_compatible(tag->lang, type_tag->lang) &&
791 strcmp(scope, tag->scope) == 0 &&
792 (!namespace || !tm_tag_is_anon(tag)))
794 g_ptr_array_add (tags, tag);
798 g_free(scope);
800 if (tags->len == 0)
802 g_ptr_array_free(tags, TRUE);
803 return NULL;
806 return tags;
810 static gchar *strip_type(const gchar *scoped_name, TMParserType lang)
812 if (scoped_name != NULL)
814 /* remove scope prefix */
815 const gchar *sep = tm_tag_context_separator(lang);
816 const gchar *base = g_strrstr(scoped_name, sep);
817 gchar *name = base ? g_strdup(base + strlen(sep)) : g_strdup(scoped_name);
819 /* remove pointers */
820 g_strdelimit(name, "*^", ' ');
821 g_strstrip(name);
823 return name;
825 return NULL;
829 /* Gets all members of the type with the given name; search them inside tags_array */
830 static GPtrArray *
831 find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile *file,
832 TMParserType lang, gboolean namespace)
834 GPtrArray *res = NULL;
835 gchar *type_name;
836 guint i;
838 g_return_val_if_fail(name && *name, NULL);
840 type_name = g_strdup(name);
842 /* Check if type_name is a type that can possibly contain members.
843 * Try to resolve intermediate typedefs to get the real type name. Also
844 * add scope information to the name if applicable.
845 * The loop below loops only when resolving typedefs - avoid possibly infinite
846 * loop when typedefs create a cycle by adding some limits. */
847 for (i = 0; i < 5; i++)
849 guint j;
850 GPtrArray *type_tags;
851 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
852 TMTag *tag = NULL;
854 if (!namespace)
855 types &= ~tm_tag_enum_t;
857 type_tags = g_ptr_array_new();
858 fill_find_tags_array(type_tags, tags_array, type_name, NULL, types, lang);
860 for (j = 0; j < type_tags->len; j++)
862 TMTag *test_tag = TM_TAG(type_tags->pdata[j]);
864 /* anonymous type defined in a different file than the variable -
865 * this isn't the type we are looking for */
866 if (tm_tag_is_anon(test_tag) && (file != test_tag->file || test_tag->file == NULL))
867 continue;
869 tag = test_tag;
871 /* prefer non-typedef tags because we can be sure they contain members */
872 if (test_tag->type != tm_tag_typedef_t)
873 break;
876 g_ptr_array_free(type_tags, TRUE);
878 if (!tag) /* not a type that can contain members */
879 break;
881 /* intermediate typedef - resolve to the real type */
882 if (tag->type == tm_tag_typedef_t)
884 if (tag->var_type && tag->var_type[0] != '\0')
886 g_free(type_name);
887 type_name = strip_type(tag->var_type, tag->lang);
888 file = tag->file;
889 continue;
891 break;
893 else /* real type with members */
895 /* use the same file as the composite type if file information available */
896 res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace);
897 break;
901 g_free(type_name);
903 return res;
907 /* Checks whether a member tag is directly accessible from method with method_scope */
908 static gboolean member_at_method_scope(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag,
909 TMParserType lang)
911 const gchar *sep = tm_tag_context_separator(lang);
912 gboolean ret = FALSE;
913 gchar **comps;
914 guint len;
916 /* method scope is in the form ...::class_name::method_name */
917 comps = g_strsplit (method_scope, sep, 0);
918 len = g_strv_length(comps);
919 if (len > 1)
921 gchar *method, *member_scope, *cls, *cls_scope;
923 /* get method/member scope */
924 method = comps[len - 1];
925 comps[len - 1] = NULL;
926 member_scope = g_strjoinv(sep, comps);
927 comps[len - 1] = method;
929 /* get class scope */
930 cls = comps[len - 2];
931 comps[len - 2] = NULL;
932 cls_scope = g_strjoinv(sep, comps);
933 comps[len - 2] = cls;
934 cls_scope = strlen(cls_scope) > 0 ? cls_scope : NULL;
936 /* check whether member inside the class */
937 if (g_strcmp0(member_tag->scope, member_scope) == 0)
939 const GPtrArray *src = member_tag->file ? member_tag->file->tags_array : tags;
940 GPtrArray *cls_tags = g_ptr_array_new();
942 /* check whether the class exists */
943 fill_find_tags_array(cls_tags, src, cls, cls_scope, TM_TYPE_WITH_MEMBERS | tm_tag_namespace_t, lang);
944 ret = cls_tags->len > 0;
945 g_ptr_array_free(cls_tags, TRUE);
948 g_free(cls_scope);
949 g_free(member_scope);
952 g_strfreev(comps);
953 return ret;
957 /* For an array of variable/type tags, find members inside the types */
958 static GPtrArray *
959 find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang,
960 gboolean member, const gchar *current_scope)
962 GPtrArray *member_tags = NULL;
963 guint i;
965 /* there may be several variables/types with the same name - try each of them until
966 * we find something */
967 for (i = 0; i < tags->len && !member_tags; i++)
969 TMTag *tag = TM_TAG(tags->pdata[i]);
970 TMTagType member_types = tm_tag_member_t | tm_tag_field_t | tm_tag_method_t;
971 TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
973 if (tag->type & types) /* type: namespace search */
975 if (tag->type & tm_tag_typedef_t)
976 member_tags = find_scope_members(searched_array, tag->name, tag->file, lang, TRUE);
977 else
978 member_tags = find_scope_members_tags(tag->file ? tag->file->tags_array : searched_array,
979 tag, TRUE);
981 else if (tag->var_type) /* variable: scope search */
983 /* The question now is whether we should use member tags (such as
984 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
985 * (which means user has typed something like foo.bar.) or if we are
986 * inside a method where foo is a class member, we want scope completion
987 * for foo. */
988 if (!(tag->type & member_types) || member ||
989 member_at_method_scope(tags, current_scope, tag, lang))
991 gchar *tag_type = strip_type(tag->var_type, tag->lang);
993 member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE);
994 g_free(tag_type);
999 return member_tags;
1003 static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang)
1005 GPtrArray *member_tags = NULL;
1006 guint i;
1008 for (i = 0; i < tags->len && !member_tags; i++)
1010 TMTag *tag = TM_TAG(tags->pdata[i]);
1012 member_tags = find_scope_members_tags(searched_array, tag, TRUE);
1015 return member_tags;
1019 /* Returns all member tags of a struct/union/class if the provided name is a variable
1020 of such a type or the name of the type.
1021 @param source_file TMSourceFile of the edited source file
1022 @param name Name of the variable/type whose members are searched
1023 @param function TRUE if the name is a name of a function
1024 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1025 @param current_scope The current scope in the editor
1026 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1027 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1028 GPtrArray *
1029 tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
1030 gboolean function, gboolean member, const gchar *current_scope, gboolean search_namespace)
1032 TMParserType lang = source_file ? source_file->lang : TM_PARSER_NONE;
1033 GPtrArray *tags, *member_tags = NULL;
1034 TMTagType function_types = tm_tag_function_t | tm_tag_method_t |
1035 tm_tag_macro_with_arg_t | tm_tag_prototype_t;
1036 TMTagType tag_type = tm_tag_max_t &
1037 ~(function_types | tm_tag_enumerator_t | tm_tag_namespace_t | tm_tag_package_t);
1038 TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0};
1040 if (search_namespace)
1042 tags = tm_workspace_find(name, NULL, tm_tag_namespace_t, NULL, lang);
1044 member_tags = find_namespace_members_all(tags, theWorkspace->tags_array, lang);
1045 if (!member_tags)
1046 member_tags = find_namespace_members_all(tags, theWorkspace->global_tags, lang);
1048 g_ptr_array_free(tags, TRUE);
1051 if (!member_tags)
1053 if (function)
1054 tag_type = function_types;
1056 /* tags corresponding to the variable/type name */
1057 tags = tm_workspace_find(name, NULL, tag_type, NULL, lang);
1059 /* Start searching inside the source file, continue with workspace tags and
1060 * end with global tags. This way we find the "closest" tag to the current
1061 * file in case there are more of them. */
1062 if (source_file)
1063 member_tags = find_scope_members_all(tags, source_file->tags_array,
1064 lang, member, current_scope);
1065 if (!member_tags)
1066 member_tags = find_scope_members_all(tags, theWorkspace->tags_array, lang,
1067 member, current_scope);
1068 if (!member_tags)
1069 member_tags = find_scope_members_all(tags, theWorkspace->global_tags, lang,
1070 member, current_scope);
1072 g_ptr_array_free(tags, TRUE);
1075 tm_tags_dedup(member_tags, sort_attr, FALSE);
1077 return member_tags;
1081 #ifdef TM_DEBUG
1083 /* Dumps the workspace tree - useful for debugging */
1084 void tm_workspace_dump(void)
1086 guint i;
1088 #ifdef TM_DEBUG
1089 g_message("Dumping TagManager workspace tree..");
1090 #endif
1091 for (i=0; i < theWorkspace->source_files->len; ++i)
1093 TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
1094 fprintf(stderr, "%s", source_file->file_name);
1097 #endif /* TM_DEBUG */
1100 #if 0
1102 /* Returns a list of parent classes for the given class name
1103 @param name Name of the class
1104 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1105 static const GPtrArray *tm_workspace_get_parents(const gchar *name)
1107 static TMTagAttrType type[] = { tm_tag_attr_name_t, tm_tag_attr_none_t };
1108 static GPtrArray *parents = NULL;
1109 const GPtrArray *matches;
1110 guint i = 0;
1111 guint j;
1112 gchar **klasses;
1113 gchar **klass;
1114 TMTag *tag;
1116 g_return_val_if_fail(name && isalpha(*name),NULL);
1118 if (NULL == parents)
1119 parents = g_ptr_array_new();
1120 else
1121 g_ptr_array_set_size(parents, 0);
1122 matches = tm_workspace_find(name, NULL, tm_tag_class_t, type, -1);
1123 if ((NULL == matches) || (0 == matches->len))
1124 return NULL;
1125 g_ptr_array_add(parents, matches->pdata[0]);
1126 while (i < parents->len)
1128 tag = TM_TAG(parents->pdata[i]);
1129 if ((NULL != tag->inheritance) && (isalpha(tag->inheritance[0])))
1131 klasses = g_strsplit(tag->inheritance, ",", 10);
1132 for (klass = klasses; (NULL != *klass); ++ klass)
1134 for (j=0; j < parents->len; ++j)
1136 if (0 == strcmp(*klass, TM_TAG(parents->pdata[j])->name))
1137 break;
1139 if (parents->len == j)
1141 matches = tm_workspace_find(*klass, NULL, tm_tag_class_t, type, -1);
1142 if ((NULL != matches) && (0 < matches->len))
1143 g_ptr_array_add(parents, matches->pdata[0]);
1146 g_strfreev(klasses);
1148 ++ i;
1150 return parents;
1153 #endif