geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

tm_workspace.c
Go to the documentation of this file.
1/*
2*
3* Copyright (c) 2001-2002, Biswapesh Chattopadhyay
4* Copyright 2005 The Geany contributors
5*
6* This source code is released for free distribution under the terms of the
7* GNU General Public License.
8*
9*/
10
11/**
12 * @file tm_workspace.h
13 The TMWorkspace structure is meant to be used as a singleton to store application
14 wide tag information.
15
16 The workspace is intended to contain a list of global tags
17 and a set of individual source files.
18*/
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <ctype.h>
24#include <sys/types.h>
25#include <string.h>
26#include <sys/stat.h>
27#ifdef HAVE_GLOB_H
28# include <glob.h>
29#endif
30#include <glib/gstdio.h>
31
32#include "tm_workspace.h"
33#include "tm_ctags.h"
34#include "tm_tag.h"
35#include "tm_parser.h"
36
37
38/* when changing, always keep the three sort criteria below in sync */
40{
43};
44
45/* for file tags the file is always identical, don't use for sorting */
47{
50};
51
52/* global tags don't have file/line information */
54{
57};
58
62
66
68
69
70static gboolean tm_create_workspace(void)
71{
72 theWorkspace = g_new(TMWorkspace, 1);
73 theWorkspace->tags_array = g_ptr_array_new();
74
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();
79
82
83 return TRUE;
84}
85
86
87/* Frees the workspace structure and all child source files. Use only when
88 exiting from the main program.
89*/
91{
92 guint i;
93
94#ifdef TM_DEBUG
95 g_message("Workspace destroyed");
96#endif
97
98 for (i=0; i < theWorkspace->source_files->len; ++i)
100 g_ptr_array_free(theWorkspace->source_files, 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);
107}
108
109
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.
115*/
117{
118 if (NULL == theWorkspace)
120 return theWorkspace;
121}
122
123
124static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_array)
125{
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;
130}
131
132
133static void merge_extracted_tags(GPtrArray **dest, GPtrArray *src, TMTagType tag_types)
134{
135 GPtrArray *arr;
136
137 arr = tm_tags_extract(src, tag_types);
138 tm_workspace_merge_tags(dest, arr);
139 g_ptr_array_free(arr, TRUE);
140}
141
142
143static void update_source_file(TMSourceFile *source_file, guchar* text_buf,
144 gsize buf_size, gboolean use_buffer, gboolean update_workspace)
145{
146#ifdef TM_DEBUG
147 g_message("Source file updating based on source file %s", source_file->file_name);
148#endif
149
150 if (update_workspace)
151 {
152 /* tm_source_file_parse() deletes the tag objects - remove the tags from
153 * workspace while they exist and can be scanned */
156 }
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)
160 {
161#ifdef TM_DEBUG
162 g_message("Updating workspace from source file");
163#endif
165
167 }
168#ifdef TM_DEBUG
169 else
170 g_message("Skipping workspace update because update_workspace is %s",
171 update_workspace?"TRUE":"FALSE");
172
173#endif
174}
175
176
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.
179*/
180GEANY_API_SYMBOL
182{
183 g_return_if_fail(source_file != NULL);
184
185 g_ptr_array_add(theWorkspace->source_files, source_file);
186 update_source_file(source_file, NULL, 0, FALSE, TRUE);
187}
188
189
191{
192 g_return_if_fail(source_file != NULL);
193
194 g_ptr_array_add(theWorkspace->source_files, source_file);
195}
196
197
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.
209*/
210void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* text_buf,
211 gsize buf_size)
212{
213 update_source_file(source_file, text_buf, buf_size, TRUE, TRUE);
214}
215
216
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.
221*/
222GEANY_API_SYMBOL
224{
225 guint i;
226
227 g_return_if_fail(source_file != NULL);
228
229 for (i=0; i < theWorkspace->source_files->len; ++i)
230 {
231 if (theWorkspace->source_files->pdata[i] == source_file)
232 {
235 g_ptr_array_remove_index_fast(theWorkspace->source_files, i);
236 return;
237 }
238 }
239}
240
241
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.
246*/
247static void tm_workspace_update(void)
248{
249 guint i, j;
250 TMSourceFile *source_file;
251
252#ifdef TM_DEBUG
253 g_message("Recreating workspace tags array");
254#endif
255
256 g_ptr_array_set_size(theWorkspace->tags_array, 0);
257
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)
262 {
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)
268 {
269 for (j = 0; j < source_file->tags_array->len; ++j)
270 {
271 g_ptr_array_add(theWorkspace->tags_array,
272 source_file->tags_array->pdata[j]);
273 }
274 }
275 }
276#ifdef TM_DEBUG
277 g_message("Total: %d tags", theWorkspace->tags_array->len);
278#endif
280
281 g_ptr_array_free(theWorkspace->typename_array, TRUE);
283}
284
285
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.
290*/
291GEANY_API_SYMBOL
292void tm_workspace_add_source_files(GPtrArray *source_files)
293{
294 guint i;
295
296 g_return_if_fail(source_files != NULL);
297
298 for (i = 0; i < source_files->len; i++)
299 {
300 TMSourceFile *source_file = source_files->pdata[i];
301
303 update_source_file(source_file, NULL, 0, FALSE, FALSE);
304 }
305
307}
308
309
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.
315*/
316GEANY_API_SYMBOL
317void tm_workspace_remove_source_files(GPtrArray *source_files)
318{
319 guint i, j;
320
321 g_return_if_fail(source_files != NULL);
322
323 //TODO: sort both arrays by pointer value and remove in single pass
324 for (i = 0; i < source_files->len; i++)
325 {
326 TMSourceFile *source_file = source_files->pdata[i];
327
328 for (j = 0; j < theWorkspace->source_files->len; j++)
329 {
330 if (theWorkspace->source_files->pdata[j] == source_file)
331 {
332 g_ptr_array_remove_index_fast(theWorkspace->source_files, j);
333 break;
334 }
335 }
336 }
337
339}
340
341
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()
347*/
348gboolean tm_workspace_load_global_tags(const char *tags_file, TMParserType mode)
349{
350 GPtrArray *file_tags, *new_tags;
351
352 file_tags = tm_source_file_read_tags_file(tags_file, mode);
353 if (!file_tags)
354 return FALSE;
355
356 tm_tags_sort(file_tags, global_tags_sort_attrs, TRUE, TRUE);
357
358 /* reorder the whole array, because tm_tags_find expects a sorted array */
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;
364
365 g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
367
368 return TRUE;
369}
370
371
372static gboolean write_includes_file(const gchar *outf, GList *includes_files)
373{
374 FILE *fp = g_fopen(outf, "w");
375 GList *node = includes_files;
376
377 if (!fp)
378 return FALSE;
379
380 while (node)
381 {
382 char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data);
383 size_t str_len = strlen(str);
384
385 fwrite(str, str_len, 1, fp);
386 g_free(str);
387 node = g_list_next(node);
388 }
389
390 return fclose(fp) == 0;
391}
392
393
394static gboolean combine_include_files(const gchar *outf, GList *file_list)
395{
396 FILE *fp = g_fopen(outf, "w");
397 GList *node = file_list;
398
399 if (!fp)
400 return FALSE;
401
402 while (node)
403 {
404 const char *fname = node->data;
405 char *contents;
406 size_t length;
407 GError *err = NULL;
408
409 if (! g_file_get_contents(fname, &contents, &length, &err))
410 {
411 fprintf(stderr, "Unable to read file: %s\n", err->message);
412 g_error_free(err);
413 }
414 else
415 {
416 fwrite(contents, length, 1, fp);
417 fwrite("\n", 1, 1, fp); /* in case file doesn't end in newline (e.g. windows). */
418 g_free(contents);
419 }
420 node = g_list_next (node);
421 }
422
423 return fclose(fp) == 0;
424}
425
426
427static gchar *create_temp_file(const gchar *tpl)
428{
429 gchar *name;
430 gint fd;
431
432 fd = g_file_open_tmp(tpl, &name, NULL);
433 if (fd < 0)
434 name = NULL;
435 else
436 close(fd);
437
438 return name;
439}
440
441static GList *lookup_includes(const gchar **includes, gint includes_count)
442{
443 GList *includes_files = NULL;
444 GHashTable *table; /* used for deduping */
445 gint i;
446#ifdef HAVE_GLOB_H
447 glob_t globbuf;
448 size_t idx_glob;
449#endif
450
451 table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
452
453#ifdef HAVE_GLOB_H
454 globbuf.gl_offs = 0;
455
456 if (includes[0][0] == '"') /* leading \" char for glob matching */
457 {
458 for (i = 0; i < includes_count; i++)
459 {
460 size_t dirty_len = strlen(includes[i]);
461 gchar *clean_path;
462
463 if (dirty_len < 2)
464 continue;
465
466 clean_path = g_malloc(dirty_len - 1);
467
468 strncpy(clean_path, includes[i] + 1, dirty_len - 1);
469 clean_path[dirty_len - 2] = 0;
470
471#ifdef TM_DEBUG
472 g_message ("[o][%s]\n", clean_path);
473#endif
474 glob(clean_path, 0, NULL, &globbuf);
475
476#ifdef TM_DEBUG
477 g_message ("matches: %d\n", globbuf.gl_pathc);
478#endif
479
480 for (idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++)
481 {
482#ifdef TM_DEBUG
483 g_message (">>> %s\n", globbuf.gl_pathv[idx_glob]);
484#endif
485 if (!g_hash_table_lookup(table, globbuf.gl_pathv[idx_glob]))
486 {
487 gchar *file_name_copy = g_strdup(globbuf.gl_pathv[idx_glob]);
488
489 includes_files = g_list_prepend(includes_files, file_name_copy);
490 g_hash_table_insert(table, file_name_copy, file_name_copy);
491#ifdef TM_DEBUG
492 g_message ("Added ...\n");
493#endif
494 }
495 }
496 globfree(&globbuf);
497 g_free(clean_path);
498 }
499 }
500 else
501#endif /* HAVE_GLOB_H */
502 {
503 /* no glob support or globbing not wanted */
504 for (i = 0; i < includes_count; i++)
505 {
506 if (!g_hash_table_lookup(table, includes[i]))
507 {
508 gchar* file_name_copy = g_strdup(includes[i]);
509
510 includes_files = g_list_prepend(includes_files, file_name_copy);
511 g_hash_table_insert(table, file_name_copy, file_name_copy);
512 }
513 }
514 }
515
516 g_hash_table_destroy(table);
517
518 return g_list_reverse(includes_files);
519}
520
521static gchar *pre_process_file(const gchar *cmd, const gchar *inf)
522{
523 gint ret;
524 gchar *outf = create_temp_file("tmp_XXXXXX.cpp");
525 gchar *tmp_errfile;
526 gchar *errors = NULL;
527 gchar *command;
528
529 if (!outf)
530 return NULL;
531
532 tmp_errfile = create_temp_file("tmp_XXXXXX");
533 if (!tmp_errfile)
534 {
535 g_unlink(outf);
536 g_free(outf);
537 return NULL;
538 }
539
540 command = g_strdup_printf("%s %s >%s 2>%s",
541 cmd, inf, outf, tmp_errfile);
542#ifdef TM_DEBUG
543 g_message("Executing: %s", command);
544#endif
545 ret = system(command);
546 g_free(command);
547
548 g_file_get_contents(tmp_errfile, &errors, NULL, NULL);
549 if (errors && *errors)
550 g_printerr("%s\n", errors);
551 g_free(errors);
552 g_unlink(tmp_errfile);
553 g_free(tmp_errfile);
554
555 if (ret == -1)
556 {
557 g_unlink(outf);
558 g_free(outf);
559 return NULL;
560 }
561
562 return outf;
563}
564
565/* Creates a list of global tags. Ideally, this should be created once during
566 installations so that all users can use the same file. This is because a full
567 scale global tag list can occupy several megabytes of disk space.
568 @param pre_process The pre-processing command. This is executed via system(),
569 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
570 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
571 are allowed.
572 @param tags_file The file where the tags will be stored.
573 @param lang The language to use for the tags file.
574 @return TRUE on success, FALSE on failure.
575*/
576gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes,
577 int includes_count, const char *tags_file, TMParserType lang)
578{
579 gboolean ret = FALSE;
580 TMSourceFile *source_file;
581 GList *includes_files;
582 gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
583
584 if (!temp_file)
585 return FALSE;
586
587 includes_files = lookup_includes(includes, includes_count);
588
589#ifdef TM_DEBUG
590 g_message ("writing out files to %s\n", temp_file);
591#endif
592 if (pre_process)
593 ret = write_includes_file(temp_file, includes_files);
594 else
595 ret = combine_include_files(temp_file, includes_files);
596
597 g_list_free_full(includes_files, g_free);
598 if (!ret)
599 goto cleanup;
600 ret = FALSE;
601
602 if (pre_process)
603 {
604 gchar *temp_file2 = pre_process_file(pre_process, temp_file);
605
606 if (temp_file2)
607 {
608 g_unlink(temp_file);
609 g_free(temp_file);
610 temp_file = temp_file2;
611 }
612 else
613 goto cleanup;
614 }
615
616 source_file = tm_source_file_new(temp_file, tm_source_file_get_lang_name(lang));
617 if (!source_file)
618 goto cleanup;
619 update_source_file(source_file, NULL, 0, FALSE, FALSE);
620 if (source_file->tags_array->len == 0)
621 {
622 tm_source_file_free(source_file);
623 goto cleanup;
624 }
625
626 tm_tags_sort(source_file->tags_array, global_tags_sort_attrs, TRUE, FALSE);
627 ret = tm_source_file_write_tags_file(tags_file, source_file->tags_array);
628 tm_source_file_free(source_file);
629
630cleanup:
631 g_unlink(temp_file);
632 g_free(temp_file);
633 return ret;
634}
635
636
637static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
638 const char *name, const char *scope, TMTagType type, TMParserType lang)
639{
640 TMTag **tag;
641 guint i, num;
642
643 if (!src || !dst || !name || !*name)
644 return;
645
646 tag = tm_tags_find(src, name, FALSE, &num);
647 for (i = 0; i < num; ++i)
648 {
649 if ((type & (*tag)->type) &&
650 tm_parser_langs_compatible(lang, (*tag)->lang) &&
651 (!scope || g_strcmp0((*tag)->scope, scope) == 0))
652 {
653 g_ptr_array_add(dst, *tag);
654 }
655 tag++;
656 }
657}
658
659
660/* Returns all matching tags found in the workspace.
661 @param name The name of the tag to find.
662 @param scope The scope name of the tag to find, or NULL.
663 @param type The tag types to return (TMTagType). Can be a bitmask.
664 @param attrs The attributes to sort and dedup on (0 terminated integer array).
665 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
666 -1 for all
667 @return Array of matching tags.
668*/
669GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
670 TMTagAttrType *attrs, TMParserType lang)
671{
672 GPtrArray *tags = g_ptr_array_new();
673
676
677 if (attrs)
678 tm_tags_sort(tags, attrs, TRUE, FALSE);
679
680 return tags;
681}
682
683
684static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src,
685 const char *name, TMParserType lang, guint max_num)
686{
687 TMTag **tag, *last = NULL;
688 guint i, count, num;
689
690 if (!src || !dst || !name || !*name)
691 return;
692
693 num = 0;
694 tag = tm_tags_find(src, name, TRUE, &count);
695 for (i = 0; i < count && num < max_num; ++i)
696 {
697 if (tm_parser_langs_compatible(lang, (*tag)->lang) &&
698 !tm_tag_is_anon(*tag) &&
699 (!last || g_strcmp0(last->name, (*tag)->name) != 0))
700 {
701 g_ptr_array_add(dst, *tag);
702 last = *tag;
703 num++;
704 }
705 tag++;
706 }
707}
708
709
710/* Returns tags with the specified prefix sorted by name. If there are several
711 tags with the same name, only one of them appears in the resulting array.
712 @param prefix The prefix of the tag to find.
713 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
714 -1 for all.
715 @param max_num The maximum number of tags to return.
716 @return Array of matching tags sorted by their name.
717*/
718GPtrArray *tm_workspace_find_prefix(const char *prefix, TMParserType lang, guint max_num)
719{
720 TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
721 GPtrArray *tags = g_ptr_array_new();
722
723 fill_find_tags_array_prefix(tags, theWorkspace->tags_array, prefix, lang, max_num);
724 fill_find_tags_array_prefix(tags, theWorkspace->global_tags, prefix, lang, max_num);
725
726 tm_tags_sort(tags, attrs, TRUE, FALSE);
727 if (tags->len > max_num)
728 tags->len = max_num;
729
730 return tags;
731}
732
733
734/* Gets all members of type_tag; search them inside the all array.
735 * The namespace parameter determines whether we are performing the "namespace"
736 * search (user has typed something like "A::" where A is a type) or "scope" search
737 * (user has typed "a." where a is a global struct-like variable). With the
738 * namespace search we return all direct descendants of any type while with the
739 * scope search we return only those which can be invoked on a variable (member,
740 * method, etc.). */
741static GPtrArray *
742find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace)
743{
745 GPtrArray *tags = g_ptr_array_new();
746 gchar *scope;
747 guint i;
748
749 if (namespace)
750 member_types = tm_tag_max_t;
751
752 if (type_tag->scope && *(type_tag->scope))
753 scope = g_strconcat(type_tag->scope, tm_parser_context_separator(type_tag->lang), type_tag->name, NULL);
754 else
755 scope = g_strdup(type_tag->name);
756
757 for (i = 0; i < all->len; ++i)
758 {
759 TMTag *tag = TM_TAG (all->pdata[i]);
760
761 if (tag && (tag->type & member_types) &&
762 tag->scope && tag->scope[0] != '\0' &&
763 tm_parser_langs_compatible(tag->lang, type_tag->lang) &&
764 strcmp(scope, tag->scope) == 0 &&
765 (!namespace || !tm_tag_is_anon(tag)))
766 {
767 g_ptr_array_add (tags, tag);
768 }
769 }
770
771 g_free(scope);
772
773 if (tags->len == 0)
774 {
775 g_ptr_array_free(tags, TRUE);
776 return NULL;
777 }
778
779 return tags;
780}
781
782
783static gchar *strip_type(const gchar *scoped_name, TMParserType lang)
784{
785 if (scoped_name != NULL)
786 {
787 /* remove scope prefix */
788 const gchar *sep = tm_parser_context_separator(lang);
789 const gchar *base = g_strrstr(scoped_name, sep);
790 gchar *name = base ? g_strdup(base + strlen(sep)) : g_strdup(scoped_name);
791
792 /* remove pointers */
793 g_strdelimit(name, "*^", ' ');
794 g_strstrip(name);
795
796 return name;
797 }
798 return NULL;
799}
800
801
802/* Gets all members of the type with the given name; search them inside tags_array */
803static GPtrArray *
804find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile *file,
805 TMParserType lang, gboolean namespace)
806{
807 GPtrArray *res = NULL;
808 gchar *type_name;
809 guint i;
810
811 g_return_val_if_fail(name && *name, NULL);
812
813 type_name = g_strdup(name);
814
815 /* Check if type_name is a type that can possibly contain members.
816 * Try to resolve intermediate typedefs to get the real type name. Also
817 * add scope information to the name if applicable.
818 * The loop below loops only when resolving typedefs - avoid possibly infinite
819 * loop when typedefs create a cycle by adding some limits. */
820 for (i = 0; i < 5; i++)
821 {
822 guint j;
823 GPtrArray *type_tags;
825 TMTag *tag = NULL;
826
827 if (!namespace)
828 types &= ~tm_tag_enum_t;
829
830 type_tags = g_ptr_array_new();
831 fill_find_tags_array(type_tags, tags_array, type_name, NULL, types, lang);
832
833 for (j = 0; j < type_tags->len; j++)
834 {
835 TMTag *test_tag = TM_TAG(type_tags->pdata[j]);
836
837 /* anonymous type defined in a different file than the variable -
838 * this isn't the type we are looking for */
839 if (tm_tag_is_anon(test_tag) && (file != test_tag->file || test_tag->file == NULL))
840 continue;
841
842 tag = test_tag;
843
844 /* prefer non-typedef tags because we can be sure they contain members */
845 if (test_tag->type != tm_tag_typedef_t)
846 break;
847 }
848
849 g_ptr_array_free(type_tags, TRUE);
850
851 if (!tag) /* not a type that can contain members */
852 break;
853
854 /* intermediate typedef - resolve to the real type */
855 if (tag->type == tm_tag_typedef_t)
856 {
857 if (tag->var_type && tag->var_type[0] != '\0')
858 {
859 g_free(type_name);
860 type_name = strip_type(tag->var_type, tag->lang);
861 file = tag->file;
862 continue;
863 }
864 break;
865 }
866 else /* real type with members */
867 {
868 /* use the same file as the composite type if file information available */
869 res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace);
870 break;
871 }
872 }
873
874 g_free(type_name);
875
876 return res;
877}
878
879
880/* Checks whether a member tag is directly accessible from method with method_scope */
881static gboolean member_at_method_scope(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag,
882 TMParserType lang)
883{
884 const gchar *sep = tm_parser_context_separator(lang);
885 gboolean ret = FALSE;
886 gchar **comps;
887 guint len;
888
889 /* method scope is in the form ...::class_name::method_name */
890 comps = g_strsplit (method_scope, sep, 0);
891 len = g_strv_length(comps);
892 if (len > 1)
893 {
894 gchar *method, *member_scope, *cls, *cls_scope;
895
896 /* get method/member scope */
897 method = comps[len - 1];
898 comps[len - 1] = NULL;
899 member_scope = g_strjoinv(sep, comps);
900 comps[len - 1] = method;
901
902 /* get class scope */
903 cls = comps[len - 2];
904 comps[len - 2] = NULL;
905 cls_scope = g_strjoinv(sep, comps);
906 comps[len - 2] = cls;
907 cls_scope = strlen(cls_scope) > 0 ? cls_scope : NULL;
908
909 /* check whether member inside the class */
910 if (g_strcmp0(member_tag->scope, member_scope) == 0)
911 {
912 const GPtrArray *src = member_tag->file ? member_tag->file->tags_array : tags;
913 GPtrArray *cls_tags = g_ptr_array_new();
914
915 /* check whether the class exists */
916 fill_find_tags_array(cls_tags, src, cls, cls_scope, TM_TYPE_WITH_MEMBERS | tm_tag_namespace_t, lang);
917 ret = cls_tags->len > 0;
918 g_ptr_array_free(cls_tags, TRUE);
919 }
920
921 g_free(cls_scope);
922 g_free(member_scope);
923 }
924
925 g_strfreev(comps);
926 return ret;
927}
928
929
930/* For an array of variable/type tags, find members inside the types */
931static GPtrArray *
932find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang,
933 gboolean member, const gchar *current_scope)
934{
935 GPtrArray *member_tags = NULL;
936 guint i;
937
938 /* there may be several variables/types with the same name - try each of them until
939 * we find something */
940 for (i = 0; i < tags->len && !member_tags; i++)
941 {
942 TMTag *tag = TM_TAG(tags->pdata[i]);
945
946 if (tag->type & types) /* type: namespace search */
947 {
948 if (tag->type & tm_tag_typedef_t)
949 member_tags = find_scope_members(searched_array, tag->name, tag->file, lang, TRUE);
950 else
951 member_tags = find_scope_members_tags(tag->file ? tag->file->tags_array : searched_array,
952 tag, TRUE);
953 }
954 else if (tag->var_type) /* variable: scope search */
955 {
956 /* The question now is whether we should use member tags (such as
957 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
958 * (which means user has typed something like foo.bar.) or if we are
959 * inside a method where foo is a class member, we want scope completion
960 * for foo. */
961 if (!(tag->type & member_types) || member ||
962 member_at_method_scope(tags, current_scope, tag, lang))
963 {
964 gchar *tag_type = strip_type(tag->var_type, tag->lang);
965
966 member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE);
967 g_free(tag_type);
968 }
969 }
970 }
971
972 return member_tags;
973}
974
975
976static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang)
977{
978 GPtrArray *member_tags = NULL;
979 guint i;
980
981 for (i = 0; i < tags->len && !member_tags; i++)
982 {
983 TMTag *tag = TM_TAG(tags->pdata[i]);
984
985 member_tags = find_scope_members_tags(searched_array, tag, TRUE);
986 }
987
988 return member_tags;
989}
990
991
992/* Returns all member tags of a struct/union/class if the provided name is a variable
993 of such a type or the name of the type.
994 @param source_file TMSourceFile of the edited source file
995 @param name Name of the variable/type whose members are searched
996 @param function TRUE if the name is a name of a function
997 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
998 @param current_scope The current scope in the editor
999 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1000 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1001GPtrArray *
1003 gboolean function, gboolean member, const gchar *current_scope, gboolean search_namespace)
1004{
1005 TMParserType lang = source_file ? source_file->lang : TM_PARSER_NONE;
1006 GPtrArray *tags, *member_tags = NULL;
1007 TMTagType function_types = tm_tag_function_t | tm_tag_method_t |
1009 TMTagType tag_type = tm_tag_max_t &
1011 TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0};
1012
1013 if (search_namespace)
1014 {
1016
1017 member_tags = find_namespace_members_all(tags, theWorkspace->tags_array, lang);
1018 if (!member_tags)
1019 member_tags = find_namespace_members_all(tags, theWorkspace->global_tags, lang);
1020
1021 g_ptr_array_free(tags, TRUE);
1022 }
1023
1024 if (!member_tags)
1025 {
1026 if (function)
1027 tag_type = function_types;
1028
1029 /* tags corresponding to the variable/type name */
1030 tags = tm_workspace_find(name, NULL, tag_type, NULL, lang);
1031
1032 /* Start searching inside the source file, continue with workspace tags and
1033 * end with global tags. This way we find the "closest" tag to the current
1034 * file in case there are more of them. */
1035 if (source_file)
1036 member_tags = find_scope_members_all(tags, source_file->tags_array,
1037 lang, member, current_scope);
1038 if (!member_tags)
1039 member_tags = find_scope_members_all(tags, theWorkspace->tags_array, lang,
1040 member, current_scope);
1041 if (!member_tags)
1042 member_tags = find_scope_members_all(tags, theWorkspace->global_tags, lang,
1043 member, current_scope);
1044
1045 g_ptr_array_free(tags, TRUE);
1046 }
1047
1048 if (member_tags)
1049 tm_tags_dedup(member_tags, sort_attr, FALSE);
1050
1051 return member_tags;
1052}
1053
1054
1055#ifdef TM_DEBUG
1056
1057/* Dumps the workspace tree - useful for debugging */
1058void tm_workspace_dump(void)
1059{
1060 guint i;
1061
1062#ifdef TM_DEBUG
1063 g_message("Dumping TagManager workspace tree..");
1064#endif
1065 for (i=0; i < theWorkspace->source_files->len; ++i)
1066 {
1067 TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
1068 fprintf(stderr, "%s", source_file->file_name);
1069 }
1070}
1071#endif /* TM_DEBUG */
1072
1073
1074#if 0
1075
1076/* Returns a list of parent classes for the given class name
1077 @param name Name of the class
1078 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1079static const GPtrArray *tm_workspace_get_parents(const gchar *name)
1080{
1082 static GPtrArray *parents = NULL;
1083 const GPtrArray *matches;
1084 guint i = 0;
1085 guint j;
1086 gchar **klasses;
1087 gchar **klass;
1088 TMTag *tag;
1089
1090 g_return_val_if_fail(name && isalpha(*name),NULL);
1091
1092 if (NULL == parents)
1093 parents = g_ptr_array_new();
1094 else
1095 g_ptr_array_set_size(parents, 0);
1096 matches = tm_workspace_find(name, NULL, tm_tag_class_t, type, -1);
1097 if ((NULL == matches) || (0 == matches->len))
1098 return NULL;
1099 g_ptr_array_add(parents, matches->pdata[0]);
1100 while (i < parents->len)
1101 {
1102 tag = TM_TAG(parents->pdata[i]);
1103 if ((NULL != tag->inheritance) && (isalpha(tag->inheritance[0])))
1104 {
1105 klasses = g_strsplit(tag->inheritance, ",", 10);
1106 for (klass = klasses; (NULL != *klass); ++ klass)
1107 {
1108 for (j=0; j < parents->len; ++j)
1109 {
1110 if (0 == strcmp(*klass, TM_TAG(parents->pdata[j])->name))
1111 break;
1112 }
1113 if (parents->len == j)
1114 {
1115 matches = tm_workspace_find(*klass, NULL, tm_tag_class_t, type, -1);
1116 if ((NULL != matches) && (0 < matches->len))
1117 g_ptr_array_add(parents, matches->pdata[0]);
1118 }
1119 }
1120 g_strfreev(klasses);
1121 }
1122 ++ i;
1123 }
1124 return parents;
1125}
1126
1127#endif
const gchar * command
Definition: build.c:2677
const gchar * name
Definition: document.c:3219
unsigned int count
static vString * scope
Definition: geany_go.c:78
#define NULL
Definition: rbtree.h:150
GtkWidget * close
Definition: sidebar.c:56
The TMSourceFile structure represents the source file and its tags in the tag manager.
TMParserType lang
GPtrArray * tags_array
Sorted tag array obtained by parsing the object.
char * file_name
Full file name (inc.
The TMTag structure represents a single tag in the tag manager.
Definition: tm_tag.h:88
char * scope
Scope of tag.
Definition: tm_tag.h:99
char * inheritance
Parent classes.
Definition: tm_tag.h:100
char * var_type
Variable type (maps to struct for typedefs)
Definition: tm_tag.h:101
TMSourceFile * file
These are tag attributes.
Definition: tm_tag.h:94
TMTagType type
Tag Type.
Definition: tm_tag.h:90
char * name
Name of tag.
Definition: tm_tag.h:89
TMParserType lang
Definition: tm_tag.h:104
The Tag Manager Workspace.
Definition: tm_workspace.h:28
GPtrArray * global_typename_array
Definition: tm_workspace.h:34
GPtrArray * typename_array
Definition: tm_workspace.h:33
GPtrArray * global_tags
Global tags loaded at startup.
Definition: tm_workspace.h:29
GPtrArray * source_files
An array of TMSourceFile pointers.
Definition: tm_workspace.h:30
GPtrArray * tags_array
Sorted tags from all source files (just pointers to source file tags, the tag objects are owned by th...
Definition: tm_workspace.h:31
void tm_ctags_init(void)
Definition: tm_ctags.c:197
const gchar * tm_parser_context_separator(TMParserType lang)
Definition: tm_parser.c:764
gboolean tm_parser_langs_compatible(TMParserType lang, TMParserType other)
Definition: tm_parser.c:838
void tm_parser_verify_type_mappings(void)
Definition: tm_parser.c:704
TMTagType
Types of tags.
Definition: tm_parser.h:23
@ tm_tag_function_t
Function definition.
Definition: tm_parser.h:29
@ tm_tag_package_t
Package (Java only)
Definition: tm_parser.h:34
@ tm_tag_interface_t
Interface (Java only)
Definition: tm_parser.h:30
@ tm_tag_field_t
Field (Java only)
Definition: tm_parser.h:28
@ tm_tag_class_t
Class declaration.
Definition: tm_parser.h:25
@ tm_tag_enumerator_t
Enumerator value.
Definition: tm_parser.h:27
@ tm_tag_max_t
Maximum value of TMTagType.
Definition: tm_parser.h:45
@ tm_tag_member_t
Member variable of class/struct.
Definition: tm_parser.h:31
@ tm_tag_method_t
Class method (Java only)
Definition: tm_parser.h:32
@ tm_tag_union_t
Union.
Definition: tm_parser.h:38
@ tm_tag_struct_t
Struct declaration.
Definition: tm_parser.h:36
@ tm_tag_macro_with_arg_t
Parameterized macro.
Definition: tm_parser.h:42
@ tm_tag_namespace_t
Namespace declaration.
Definition: tm_parser.h:33
@ tm_tag_prototype_t
Function prototype.
Definition: tm_parser.h:35
@ tm_tag_typedef_t
Typedef.
Definition: tm_parser.h:37
@ tm_tag_enum_t
Enum declaration.
Definition: tm_parser.h:26
gint TMParserType
Definition: tm_parser.h:52
void tm_source_file_free(TMSourceFile *source_file)
Decrements the reference count of source_file.
GPtrArray * tm_source_file_read_tags_file(const gchar *tags_file, TMParserType mode)
TMSourceFile * tm_source_file_new(const char *file_name, const char *name)
Initializes a TMSourceFile structure and returns a pointer to it.
gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_array)
gboolean tm_source_file_parse(TMSourceFile *source_file, guchar *text_buf, gsize buf_size, gboolean use_buffer)
const gchar * tm_source_file_get_lang_name(TMParserType lang)
GPtrArray * tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
Definition: tm_tag.c:482
GPtrArray * tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types)
Definition: tm_tag.c:505
void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
Definition: tm_tag.c:529
void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
Definition: tm_tag.c:311
TMTag ** tm_tags_find(const GPtrArray *tags_array, const char *name, gboolean partial, guint *tagCount)
Definition: tm_tag.c:603
gboolean tm_tag_is_anon(const TMTag *tag)
Definition: tm_tag.c:668
void tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean dedup, gboolean unref_duplicates)
Definition: tm_tag.c:297
void tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
Definition: tm_tag.c:267
#define TM_TAG(tag)
Use the TM_TAG() macro to cast a pointer to (TMTag *)
Definition: tm_tag.h:40
TMTagAttrType
Tag Attributes.
Definition: tm_tag.h:51
@ tm_tag_attr_none_t
Undefined.
Definition: tm_tag.h:52
@ tm_tag_attr_name_t
Tag Name.
Definition: tm_tag.h:53
@ tm_tag_attr_file_t
File in which tag exists.
Definition: tm_tag.h:55
@ tm_tag_attr_scope_t
Scope of the tag.
Definition: tm_tag.h:58
@ tm_tag_attr_arglist_t
Argument list.
Definition: tm_tag.h:60
@ tm_tag_attr_line_t
Line number of tag.
Definition: tm_tag.h:56
@ tm_tag_attr_type_t
Tag Type.
Definition: tm_tag.h:54
static TMWorkspace * theWorkspace
Definition: tm_workspace.c:67
GPtrArray * tm_workspace_find(const char *name, const char *scope, TMTagType type, TMTagAttrType *attrs, TMParserType lang)
Definition: tm_workspace.c:669
void tm_workspace_remove_source_files(GPtrArray *source_files)
Removes multiple source files from the workspace and updates the workspace tag arrays.
Definition: tm_workspace.c:317
static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src, const char *name, const char *scope, TMTagType type, TMParserType lang)
Definition: tm_workspace.c:637
static TMTagAttrType file_tags_sort_attrs[]
Definition: tm_workspace.c:46
GPtrArray * tm_workspace_find_prefix(const char *prefix, TMParserType lang, guint max_num)
Definition: tm_workspace.c:718
static TMTagAttrType workspace_tags_sort_attrs[]
Definition: tm_workspace.c:39
static GList * lookup_includes(const gchar **includes, gint includes_count)
Definition: tm_workspace.c:441
void tm_workspace_add_source_files(GPtrArray *source_files)
Adds multiple source files to the workspace and updates the workspace tag arrays.
Definition: tm_workspace.c:292
static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_array)
Definition: tm_workspace.c:124
static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src, const char *name, TMParserType lang, guint max_num)
Definition: tm_workspace.c:684
static gboolean write_includes_file(const gchar *outf, GList *includes_files)
Definition: tm_workspace.c:372
static GPtrArray * find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang)
Definition: tm_workspace.c:976
static GPtrArray * find_scope_members(const GPtrArray *tags_array, const gchar *name, TMSourceFile *file, TMParserType lang, gboolean namespace)
Definition: tm_workspace.c:804
static TMTagType TM_GLOBAL_TYPE_MASK
Definition: tm_workspace.c:63
static TMTagAttrType global_tags_sort_attrs[]
Definition: tm_workspace.c:53
static void merge_extracted_tags(GPtrArray **dest, GPtrArray *src, TMTagType tag_types)
Definition: tm_workspace.c:133
static GPtrArray * find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang, gboolean member, const gchar *current_scope)
Definition: tm_workspace.c:932
void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file)
Definition: tm_workspace.c:190
static TMTagType TM_TYPE_WITH_MEMBERS
Definition: tm_workspace.c:59
static void tm_workspace_update(void)
Definition: tm_workspace.c:247
static gboolean combine_include_files(const gchar *outf, GList *file_list)
Definition: tm_workspace.c:394
static gboolean member_at_method_scope(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag, TMParserType lang)
Definition: tm_workspace.c:881
void tm_workspace_remove_source_file(TMSourceFile *source_file)
Removes a source file from the workspace if it exists.
Definition: tm_workspace.c:223
static void update_source_file(TMSourceFile *source_file, guchar *text_buf, gsize buf_size, gboolean use_buffer, gboolean update_workspace)
Definition: tm_workspace.c:143
void tm_workspace_add_source_file(TMSourceFile *source_file)
Adds a source file to the workspace, parses it and updates the workspace tags.
Definition: tm_workspace.c:181
GPtrArray * tm_workspace_find_scope_members(TMSourceFile *source_file, const char *name, gboolean function, gboolean member, const gchar *current_scope, gboolean search_namespace)
gboolean tm_workspace_load_global_tags(const char *tags_file, TMParserType mode)
Definition: tm_workspace.c:348
static GPtrArray * find_scope_members_tags(const GPtrArray *all, TMTag *type_tag, gboolean namespace)
Definition: tm_workspace.c:742
static gboolean tm_create_workspace(void)
Definition: tm_workspace.c:70
static gchar * strip_type(const gchar *scoped_name, TMParserType lang)
Definition: tm_workspace.c:783
static gchar * pre_process_file(const gchar *cmd, const gchar *inf)
Definition: tm_workspace.c:521
gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes, int includes_count, const char *tags_file, TMParserType lang)
Definition: tm_workspace.c:576
void tm_workspace_free(void)
Definition: tm_workspace.c:90
const TMWorkspace * tm_get_workspace(void)
Definition: tm_workspace.c:116
static gchar * create_temp_file(const gchar *tpl)
Definition: tm_workspace.c:427
void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar *text_buf, gsize buf_size)
Definition: tm_workspace.c:210
The TMWorkspace structure is meant to be used as a singleton to store application wide tag informatio...