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.
11 * @file tm_workspace.h
12 The TMWorkspace structure is meant to be used as a singleton to store application
15 The workspace is intended to contain a list of global tags
16 and a set of individual source files.
25 #include <sys/types.h>
31 #include <glib/gstdio.h>
33 #include "tm_workspace.h"
37 /* when changing, always keep the three sort criteria below in sync */
38 static TMTagAttrType workspace_tags_sort_attrs
[] =
40 tm_tag_attr_name_t
, tm_tag_attr_file_t
, tm_tag_attr_line_t
,
41 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
44 /* for file tags the file is always identical, don't use for sorting */
45 static TMTagAttrType file_tags_sort_attrs
[] =
47 tm_tag_attr_name_t
, tm_tag_attr_line_t
,
48 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
51 /* global tags don't have file/line information */
52 static TMTagAttrType global_tags_sort_attrs
[] =
55 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
59 static TMWorkspace
*theWorkspace
= NULL
;
62 static gboolean
tm_create_workspace(void)
64 theWorkspace
= g_new(TMWorkspace
, 1);
65 theWorkspace
->tags_array
= g_ptr_array_new();
67 theWorkspace
->global_tags
= g_ptr_array_new();
68 theWorkspace
->source_files
= g_ptr_array_new();
69 theWorkspace
->typename_array
= g_ptr_array_new();
74 /* Frees the workspace structure and all child source files. Use only when
75 exiting from the main program.
77 void tm_workspace_free(void)
82 g_message("Workspace destroyed");
85 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
86 tm_source_file_free(theWorkspace
->source_files
->pdata
[i
]);
87 g_ptr_array_free(theWorkspace
->source_files
, TRUE
);
88 tm_tags_array_free(theWorkspace
->global_tags
, TRUE
);
89 g_ptr_array_free(theWorkspace
->tags_array
, TRUE
);
90 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
96 /* Since TMWorkspace is a singleton, you should not create multiple
97 workspaces, but get a pointer to the workspace whenever required. The first
98 time a pointer is requested, or a source file is added to the workspace,
99 a workspace is created. Subsequent calls to the function will return the
102 const TMWorkspace
*tm_get_workspace(void)
104 if (NULL
== theWorkspace
)
105 tm_create_workspace();
110 static void tm_workspace_merge_tags(GPtrArray
**big_array
, GPtrArray
*small_array
)
112 GPtrArray
*new_tags
= tm_tags_merge(*big_array
, small_array
, workspace_tags_sort_attrs
, FALSE
);
113 /* tags owned by TMSourceFile - free just the pointer array */
114 g_ptr_array_free(*big_array
, TRUE
);
115 *big_array
= new_tags
;
119 static void update_source_file(TMSourceFile
*source_file
, guchar
* text_buf
,
120 gsize buf_size
, gboolean use_buffer
, gboolean update_workspace
)
123 g_message("Source file updating based on source file %s", source_file
->file_name
);
126 if (update_workspace
)
128 /* tm_source_file_parse() deletes the tag objects - remove the tags from
129 * workspace while they exist and can be scanned */
130 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
131 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
133 tm_source_file_parse(source_file
, text_buf
, buf_size
, use_buffer
);
134 tm_tags_sort(source_file
->tags_array
, file_tags_sort_attrs
, FALSE
, TRUE
);
135 if (update_workspace
)
137 GPtrArray
*sf_typedefs
;
140 g_message("Updating workspace from source file");
142 tm_workspace_merge_tags(&theWorkspace
->tags_array
, source_file
->tags_array
);
144 sf_typedefs
= tm_tags_extract(source_file
->tags_array
, TM_GLOBAL_TYPE_MASK
);
145 tm_workspace_merge_tags(&theWorkspace
->typename_array
, sf_typedefs
);
146 g_ptr_array_free(sf_typedefs
, TRUE
);
150 g_message("Skipping workspace update because update_workspace is %s",
151 update_workspace
?"TRUE":"FALSE");
157 /** Adds a source file to the workspace, parses it and updates the workspace tags.
158 @param source_file The source file to add to the workspace.
160 void tm_workspace_add_source_file(TMSourceFile
*source_file
)
162 g_return_if_fail(source_file
!= NULL
);
164 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
165 update_source_file(source_file
, NULL
, 0, FALSE
, TRUE
);
169 void tm_workspace_add_source_file_noupdate(TMSourceFile
*source_file
)
171 g_return_if_fail(source_file
!= NULL
);
173 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
177 /* Updates the source file by reparsing the text-buffer passed as parameter.
178 Ctags will use a parsing based on buffer instead of on files.
179 You should call this function when you don't want a previous saving of the file
180 you're editing. It's useful for a "real-time" updating of the tags.
181 The tags array and the tags themselves are destroyed and re-created, hence any
182 other tag arrays pointing to these tags should be rebuilt as well. All sorting
183 information is also lost. The language parameter is automatically detected
184 the first time the file is parsed if it is set to LANG_AUTO.
185 @param source_file The source file to update with a buffer.
186 @param text_buf A text buffer. The user should take care of allocate and free it after
188 @param buf_size The size of text_buf.
190 void tm_workspace_update_source_file_buffer(TMSourceFile
*source_file
, guchar
* text_buf
,
193 update_source_file(source_file
, text_buf
, buf_size
, TRUE
, TRUE
);
197 /** Removes a source file from the workspace if it exists. This function also removes
198 the tags belonging to this file from the workspace. To completely free the TMSourceFile
199 pointer call tm_source_file_free() on it.
200 @param source_file Pointer to the source file to be removed.
202 void tm_workspace_remove_source_file(TMSourceFile
*source_file
)
206 g_return_if_fail(source_file
!= NULL
);
208 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
210 if (theWorkspace
->source_files
->pdata
[i
] == source_file
)
212 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
213 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
214 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, i
);
221 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
222 want to globally refresh the workspace. This function does not call tm_source_file_update()
223 which should be called before this function on source files which need to be
226 static void tm_workspace_update(void)
229 TMSourceFile
*source_file
;
232 g_message("Recreating workspace tags array");
235 g_ptr_array_set_size(theWorkspace
->tags_array
, 0);
238 g_message("Total %d objects", theWorkspace
->source_files
->len
);
240 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
242 source_file
= theWorkspace
->source_files
->pdata
[i
];
244 g_message("Adding tags of %s", source_file
->file_name
);
246 if (source_file
->tags_array
->len
> 0)
248 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
250 g_ptr_array_add(theWorkspace
->tags_array
,
251 source_file
->tags_array
->pdata
[j
]);
256 g_message("Total: %d tags", theWorkspace
->tags_array
->len
);
258 tm_tags_sort(theWorkspace
->tags_array
, workspace_tags_sort_attrs
, TRUE
, FALSE
);
260 theWorkspace
->typename_array
= tm_tags_extract(theWorkspace
->tags_array
, TM_GLOBAL_TYPE_MASK
);
264 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
265 This is more efficient than calling tm_workspace_add_source_file() and
266 tm_workspace_update_source_file() separately for each of the files.
267 @param source_files The source files to be added to the workspace.
269 void tm_workspace_add_source_files(GPtrArray
*source_files
)
273 g_return_if_fail(source_files
!= NULL
);
275 for (i
= 0; i
< source_files
->len
; i
++)
277 TMSourceFile
*source_file
= source_files
->pdata
[i
];
279 tm_workspace_add_source_file_noupdate(source_file
);
280 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
283 tm_workspace_update();
287 /** Removes multiple source files from the workspace and updates the workspace tag
288 arrays. This is more efficient than calling tm_workspace_remove_source_file()
289 separately for each of the files. To completely free the TMSourceFile pointers
290 call tm_source_file_free() on each of them.
291 @param source_files The source files to be removed from the workspace.
293 void tm_workspace_remove_source_files(GPtrArray
*source_files
)
297 g_return_if_fail(source_files
!= NULL
);
299 //TODO: sort both arrays by pointer value and remove in single pass
300 for (i
= 0; i
< source_files
->len
; i
++)
302 TMSourceFile
*source_file
= source_files
->pdata
[i
];
304 for (j
= 0; j
< theWorkspace
->source_files
->len
; j
++)
306 if (theWorkspace
->source_files
->pdata
[j
] == source_file
)
308 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, j
);
314 tm_workspace_update();
318 /* Loads the global tag list from the specified file. The global tag list should
319 have been first created using tm_workspace_create_global_tags().
320 @param tags_file The file containing global tags.
321 @return TRUE on success, FALSE on failure.
322 @see tm_workspace_create_global_tags()
324 gboolean
tm_workspace_load_global_tags(const char *tags_file
, gint mode
)
328 GPtrArray
*file_tags
, *new_tags
;
330 TMFileFormat format
= TM_FILE_FORMAT_TAGMANAGER
;
332 if (NULL
== (fp
= g_fopen(tags_file
, "r")))
334 if ((NULL
== fgets((gchar
*) buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
337 return FALSE
; /* early out on error */
340 { /* We read the first line for the format specification. */
341 if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=pipe") != NULL
)
342 format
= TM_FILE_FORMAT_PIPE
;
343 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=tagmanager") != NULL
)
344 format
= TM_FILE_FORMAT_TAGMANAGER
;
345 else if (buf
[0] == '#' && strstr((gchar
*) buf
, "format=ctags") != NULL
)
346 format
= TM_FILE_FORMAT_CTAGS
;
347 else if (strncmp((gchar
*) buf
, "!_TAG_", 6) == 0)
348 format
= TM_FILE_FORMAT_CTAGS
;
350 { /* We didn't find a valid format specification, so we try to auto-detect the format
351 * by counting the pipe characters on the first line and asumme pipe format when
352 * we find more than one pipe on the line. */
353 guint i
, pipe_cnt
= 0, tab_cnt
= 0;
354 for (i
= 0; i
< BUFSIZ
&& buf
[i
] != '\0' && pipe_cnt
< 2; i
++)
358 else if (buf
[i
] == '\t')
362 format
= TM_FILE_FORMAT_PIPE
;
363 else if (tab_cnt
> 1)
364 format
= TM_FILE_FORMAT_CTAGS
;
366 rewind(fp
); /* reset the file pointer, to start reading again from the beginning */
369 file_tags
= g_ptr_array_new();
370 while (NULL
!= (tag
= tm_tag_new_from_file(NULL
, fp
, mode
, format
)))
371 g_ptr_array_add(file_tags
, tag
);
374 tm_tags_sort(file_tags
, global_tags_sort_attrs
, TRUE
, TRUE
);
376 /* reorder the whole array, because tm_tags_find expects a sorted array */
377 new_tags
= tm_tags_merge(theWorkspace
->global_tags
,
378 file_tags
, global_tags_sort_attrs
, TRUE
);
379 g_ptr_array_free(theWorkspace
->global_tags
, TRUE
);
380 g_ptr_array_free(file_tags
, TRUE
);
381 theWorkspace
->global_tags
= new_tags
;
387 static guint
tm_file_inode_hash(gconstpointer key
)
389 struct stat file_stat
;
390 const char *filename
= (const char*)key
;
391 if (g_stat(filename
, &file_stat
) == 0)
394 g_message ("Hash for '%s' is '%d'\n", filename
, file_stat
.st_ino
);
396 return g_direct_hash ((gpointer
)(gulong
)file_stat
.st_ino
);
403 static void tm_move_entries_to_g_list(gpointer key
, gpointer value
, gpointer user_data
)
405 GList
**pp_list
= (GList
**)user_data
;
407 if (user_data
== NULL
)
410 *pp_list
= g_list_prepend(*pp_list
, value
);
414 static void write_includes_file(FILE *fp
, GList
*includes_files
)
418 node
= includes_files
;
421 char *str
= g_strdup_printf("#include \"%s\"\n", (char*)node
->data
);
422 size_t str_len
= strlen(str
);
424 fwrite(str
, str_len
, 1, fp
);
426 node
= g_list_next(node
);
431 static void append_to_temp_file(FILE *fp
, GList
*file_list
)
438 const char *fname
= node
->data
;
443 if (! g_file_get_contents(fname
, &contents
, &length
, &err
))
445 fprintf(stderr
, "Unable to read file: %s\n", err
->message
);
450 fwrite(contents
, length
, 1, fp
);
451 fwrite("\n", 1, 1, fp
); /* in case file doesn't end in newline (e.g. windows). */
454 node
= g_list_next (node
);
459 static gchar
*create_temp_file(const gchar
*tpl
)
464 fd
= g_file_open_tmp(tpl
, &name
, NULL
);
474 /* Creates a list of global tags. Ideally, this should be created once during
475 installations so that all users can use the same file. Thsi is because a full
476 scale global tag list can occupy several megabytes of disk space.
477 @param pre_process The pre-processing command. This is executed via system(),
478 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
479 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
481 @param tags_file The file where the tags will be stored.
482 @param lang The language to use for the tags file.
483 @return TRUE on success, FALSE on failure.
485 gboolean
tm_workspace_create_global_tags(const char *pre_process
, const char **includes
,
486 int includes_count
, const char *tags_file
, int lang
)
496 TMSourceFile
*source_file
;
497 GPtrArray
*tags_array
;
498 GHashTable
*includes_files_hash
;
499 GList
*includes_files
= NULL
;
500 gchar
*temp_file
= create_temp_file("tmp_XXXXXX.cpp");
501 gchar
*temp_file2
= create_temp_file("tmp_XXXXXX.cpp");
503 if (NULL
== temp_file
|| NULL
== temp_file2
||
504 NULL
== (fp
= g_fopen(temp_file
, "w")))
511 includes_files_hash
= g_hash_table_new_full (tm_file_inode_hash
,
518 if (includes
[0][0] == '"') /* leading \" char for glob matching */
519 for(idx_inc
= 0; idx_inc
< includes_count
; idx_inc
++)
521 size_t dirty_len
= strlen(includes
[idx_inc
]);
522 char *clean_path
= g_malloc(dirty_len
- 1);
524 strncpy(clean_path
, includes
[idx_inc
] + 1, dirty_len
- 1);
525 clean_path
[dirty_len
- 2] = 0;
528 g_message ("[o][%s]\n", clean_path
);
530 glob(clean_path
, 0, NULL
, &globbuf
);
533 g_message ("matches: %d\n", globbuf
.gl_pathc
);
536 for(idx_glob
= 0; idx_glob
< globbuf
.gl_pathc
; idx_glob
++)
539 g_message (">>> %s\n", globbuf
.gl_pathv
[idx_glob
]);
541 if (!g_hash_table_lookup(includes_files_hash
,
542 globbuf
.gl_pathv
[idx_glob
]))
544 char* file_name_copy
= strdup(globbuf
.gl_pathv
[idx_glob
]);
545 g_hash_table_insert(includes_files_hash
, file_name_copy
,
548 g_message ("Added ...\n");
557 /* no glob support or globbing not wanted */
558 for(idx_inc
= 0; idx_inc
< includes_count
; idx_inc
++)
560 if (!g_hash_table_lookup(includes_files_hash
,
563 char* file_name_copy
= strdup(includes
[idx_inc
]);
564 g_hash_table_insert(includes_files_hash
, file_name_copy
,
569 /* Checks for duplicate file entries which would case trouble */
570 g_hash_table_foreach(includes_files_hash
, tm_move_entries_to_g_list
,
573 includes_files
= g_list_reverse (includes_files
);
576 g_message ("writing out files to %s\n", temp_file
);
578 if (pre_process
!= NULL
)
579 write_includes_file(fp
, includes_files
);
581 append_to_temp_file(fp
, includes_files
);
583 g_list_free (includes_files
);
584 g_hash_table_destroy(includes_files_hash
);
585 includes_files_hash
= NULL
;
586 includes_files
= NULL
;
589 if (pre_process
!= NULL
)
592 gchar
*tmp_errfile
= create_temp_file("tmp_XXXXXX");
593 gchar
*errors
= NULL
;
594 command
= g_strdup_printf("%s %s >%s 2>%s",
595 pre_process
, temp_file
, temp_file2
, tmp_errfile
);
597 g_message("Executing: %s", command
);
599 ret
= system(command
);
603 g_file_get_contents(tmp_errfile
, &errors
, NULL
, NULL
);
604 if (errors
&& *errors
)
605 g_printerr("%s", errors
);
607 g_unlink(tmp_errfile
);
611 g_unlink(temp_file2
);
617 /* no pre-processing needed, so temp_file2 = temp_file */
618 g_unlink(temp_file2
);
620 temp_file2
= temp_file
;
623 source_file
= tm_source_file_new(temp_file2
, tm_source_file_get_lang_name(lang
));
624 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
625 if (NULL
== source_file
)
627 g_unlink(temp_file2
);
630 g_unlink(temp_file2
);
632 if (0 == source_file
->tags_array
->len
)
634 tm_source_file_free(source_file
);
637 tags_array
= tm_tags_extract(source_file
->tags_array
, tm_tag_max_t
);
638 if ((NULL
== tags_array
) || (0 == tags_array
->len
))
641 g_ptr_array_free(tags_array
, TRUE
);
642 tm_source_file_free(source_file
);
645 if (FALSE
== tm_tags_sort(tags_array
, global_tags_sort_attrs
, TRUE
, FALSE
))
647 tm_source_file_free(source_file
);
650 if (NULL
== (fp
= g_fopen(tags_file
, "w")))
652 tm_source_file_free(source_file
);
655 fprintf(fp
, "# format=tagmanager\n");
656 for (i
= 0; i
< tags_array
->len
; ++i
)
658 tm_tag_write(TM_TAG(tags_array
->pdata
[i
]), fp
, tm_tag_attr_type_t
659 | tm_tag_attr_scope_t
| tm_tag_attr_arglist_t
| tm_tag_attr_vartype_t
660 | tm_tag_attr_pointer_t
);
663 tm_source_file_free(source_file
);
664 g_ptr_array_free(tags_array
, TRUE
);
669 /* Returns all matching tags found in the workspace.
670 @param name The name of the tag to find.
671 @param type The tag types to return (TMTagType). Can be a bitmask.
672 @param attrs The attributes to sort and dedup on (0 terminated integer array).
673 @param partial Whether partial match is allowed.
674 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
676 @return Array of matching tags. Do not free() it since it is a static member.
678 const GPtrArray
*tm_workspace_find(const char *name
, TMTagType type
, TMTagAttrType
*attrs
,
679 gboolean partial
, langType lang
)
681 static GPtrArray
*tags
= NULL
;
684 guint tagCount
[2]={0,0}, tagIter
;
693 g_ptr_array_set_size(tags
, 0);
695 tags
= g_ptr_array_new();
697 matches
[0] = tm_tags_find(theWorkspace
->tags_array
, name
, partial
, TRUE
,
699 matches
[1] = tm_tags_find(theWorkspace
->global_tags
, name
, partial
, TRUE
, &tagCount
[1]);
702 if (matches
[0] && *matches
[0])
704 tags_lang
= (*matches
[0])->lang
;
706 for (tagIter
=0;tagIter
<tagCount
[0];++tagIter
)
708 if ((type
& (*matches
[0])->type
) && (lang
== -1 || tags_lang
== lang
))
709 g_ptr_array_add(tags
, *matches
[0]);
712 if (0 != strncmp((*matches
[0])->name
, name
, len
))
717 if (0 != strcmp((*matches
[0])->name
, name
))
725 if (matches
[1] && *matches
[1])
727 int tags_lang_alt
= 0;
728 tags_lang
= (*matches
[1])->lang
;
729 /* tags_lang_alt is used to load C global tags only once for C and C++
730 * lang = 1 is C++, lang = 0 is C
731 * if we have lang 0, than accept also lang 1 for C++ */
732 if (tags_lang
== 0) /* C or C++ */
735 tags_lang_alt
= tags_lang
; /* otherwise just ignore it */
737 for (tagIter
=0;tagIter
<tagCount
[1];++tagIter
)
739 if ((type
& (*matches
[1])->type
) && (lang
== -1 ||
740 tags_lang
== lang
|| tags_lang_alt
== lang
))
741 g_ptr_array_add(tags
, *matches
[1]);
745 if (0 != strncmp((*matches
[1])->name
, name
, len
))
750 if (0 != strcmp((*matches
[1])->name
, name
))
758 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
763 static gboolean
match_langs(gint lang
, const TMTag
*tag
)
766 { /* workspace tag */
767 if (lang
== tag
->file
->lang
)
772 if (lang
== tag
->lang
)
779 /* scope can be NULL.
782 fill_find_tags_array (GPtrArray
*dst
, const GPtrArray
*src
,
783 const char *name
, const char *scope
, TMTagType type
, gboolean partial
,
784 gint lang
, gboolean first
)
787 guint tagIter
, count
;
789 if ((!src
) || (!dst
) || (!name
) || (!*name
))
792 match
= tm_tags_find (src
, name
, partial
, TRUE
, &count
);
793 if (count
&& match
&& *match
)
795 for (tagIter
= 0; tagIter
< count
; ++tagIter
)
797 if (! scope
|| (match
[tagIter
]->scope
&&
798 0 == strcmp(match
[tagIter
]->scope
, scope
)))
800 if (type
& match
[tagIter
]->type
)
801 if (lang
== -1 || match_langs(lang
, match
[tagIter
]))
803 g_ptr_array_add (dst
, match
[tagIter
]);
814 /* Returns all matching tags found in the workspace. Adapted from tm_workspace_find, Anjuta 2.02
815 @param name The name of the tag to find.
816 @param scope The scope name of the tag to find, or NULL.
817 @param type The tag types to return (TMTagType). Can be a bitmask.
818 @param attrs The attributes to sort and dedup on (0 terminated integer array).
819 @param partial Whether partial match is allowed.
820 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
822 @return Array of matching tags. Do not free() it since it is a static member.
825 tm_workspace_find_scoped (const char *name
, const char *scope
, TMTagType type
,
826 TMTagAttrType
*attrs
, gboolean partial
, langType lang
, gboolean global_search
)
828 static GPtrArray
*tags
= NULL
;
831 g_ptr_array_set_size (tags
, 0);
833 tags
= g_ptr_array_new ();
835 fill_find_tags_array (tags
, theWorkspace
->tags_array
,
836 name
, scope
, type
, partial
, lang
, FALSE
);
839 /* for a scoped tag, I think we always want the same language */
840 fill_find_tags_array (tags
, theWorkspace
->global_tags
,
841 name
, scope
, type
, partial
, lang
, FALSE
);
844 tm_tags_sort (tags
, attrs
, TRUE
, FALSE
);
850 find_scope_members_tags (const GPtrArray
* all
, GPtrArray
* tags
,
851 const langType langJava
, const char *name
,
852 const char *filename
, gboolean no_definitions
)
854 GPtrArray
*local
= g_ptr_array_new ();
857 size_t len
= strlen (name
);
858 for (i
= 0; (i
< all
->len
); ++i
)
860 tag
= TM_TAG (all
->pdata
[i
]);
861 if (no_definitions
&& filename
&& tag
->file
&&
862 0 != strcmp (filename
,
863 tag
->file
->short_name
))
867 if (tag
&& tag
->scope
&& tag
->scope
[0] != '\0')
869 if (0 == strncmp (name
, tag
->scope
, len
))
871 g_ptr_array_add (local
, tag
);
880 char *s_backup
= NULL
;
881 char *var_type
= NULL
;
883 for (i
= 0; (i
< local
->len
); ++i
)
885 tag
= TM_TAG (local
->pdata
[i
]);
887 if (scope
&& 0 == strcmp (name
, scope
))
889 g_ptr_array_add (tags
, tag
);
893 j
= 0; /* someone could write better code :P */
898 backup
= s_backup
[0];
900 if (0 == strcmp (name
, tag
->scope
))
903 s_backup
[0] = backup
;
908 && tag
->file
->lang
== langJava
)
910 scope
= strrchr (tag
->scope
, '.');
912 var_type
= scope
+ 1;
916 scope
= strrchr (tag
->scope
, ':');
919 var_type
= scope
+ 1;
925 s_backup
[0] = backup
;
931 backup
= s_backup
[0];
934 for (j
= 0; (j
< local
->len
); ++j
)
938 tag2
= TM_TAG (local
->pdata
[j
]);
939 if (tag2
->var_type
&&
940 0 == strcmp (var_type
, tag2
->var_type
))
946 s_backup
[0] = backup
;
956 g_ptr_array_add (tags
, tag
);
960 g_ptr_array_free (local
, TRUE
);
961 return (int) tags
->len
;
965 /* Returns all matching members tags found in given struct/union/class name.
966 @param name Name of the struct/union/class.
967 @param file_tags A GPtrArray of edited file TMTag pointers (for search speedup, can be NULL).
968 @return A GPtrArray of TMTag pointers to struct/union/class members */
970 tm_workspace_find_scope_members (const GPtrArray
* file_tags
, const char *name
,
971 gboolean search_global
, gboolean no_definitions
)
973 static GPtrArray
*tags
= NULL
;
974 GPtrArray
*local
= NULL
;
975 char *new_name
= (char *) name
;
976 char *filename
= NULL
;
977 int found
= 0, del
= 0;
978 static langType langJava
= -1;
982 /* langJava = getNamedLanguage ("Java"); */
984 g_return_val_if_fail ((theWorkspace
&& name
&& name
[0] != '\0'), NULL
);
987 tags
= g_ptr_array_new ();
991 const GPtrArray
*tags2
;
993 TMTagType types
= (tm_tag_class_t
| tm_tag_namespace_t
|
994 tm_tag_struct_t
| tm_tag_typedef_t
|
995 tm_tag_union_t
| tm_tag_enum_t
);
999 g_ptr_array_set_size (tags
, 0);
1000 got
= fill_find_tags_array (tags
, file_tags
,
1001 new_name
, NULL
, types
, FALSE
, -1, FALSE
);
1009 TMTagAttrType attrs
[] = {
1010 tm_tag_attr_name_t
, tm_tag_attr_type_t
,
1013 tags2
= tm_workspace_find (new_name
, types
, attrs
, FALSE
, -1);
1016 if ((tags2
) && (tags2
->len
== 1) && (tag
= TM_TAG (tags2
->pdata
[0])))
1018 if (tag
->type
== tm_tag_typedef_t
&& tag
->var_type
1019 && tag
->var_type
[0] != '\0')
1022 tmp_name
= tag
->var_type
;
1023 if (strcmp(tmp_name
, new_name
) == 0) {
1027 new_name
= tmp_name
;
1031 filename
= (tag
->file
?
1032 tag
->file
->short_name
: NULL
);
1033 if (tag
->scope
&& tag
->scope
[0] != '\0')
1037 tag
->file
->lang
== langJava
)
1039 new_name
= g_strdup_printf ("%s.%s",
1045 new_name
= g_strdup_printf ("%s::%s",
1058 g_ptr_array_set_size (tags
, 0);
1060 if (no_definitions
&& tag
&& tag
->file
)
1062 local
= tm_tags_extract (tag
->file
->tags_array
,
1063 (tm_tag_function_t
| tm_tag_prototype_t
|
1064 tm_tag_member_t
| tm_tag_field_t
|
1065 tm_tag_method_t
| tm_tag_enumerator_t
));
1069 local
= tm_tags_extract (theWorkspace
->tags_array
,
1070 (tm_tag_function_t
| tm_tag_prototype_t
|
1071 tm_tag_member_t
| tm_tag_field_t
|
1072 tm_tag_method_t
| tm_tag_enumerator_t
));
1076 found
= find_scope_members_tags (local
, tags
, langJava
, new_name
,
1077 filename
, no_definitions
);
1078 g_ptr_array_free (local
, TRUE
);
1080 if (!found
&& search_global
)
1082 GPtrArray
*global
= tm_tags_extract (theWorkspace
->global_tags
,
1084 tm_tag_prototype_t
|
1089 |tm_tag_struct_t
| tm_tag_typedef_t
|
1090 tm_tag_union_t
| tm_tag_enum_t
));
1093 find_scope_members_tags (global
, tags
, langJava
, new_name
,
1094 filename
, no_definitions
);
1095 g_ptr_array_free (global
, TRUE
);
1109 /* Dumps the workspace tree - useful for debugging */
1110 void tm_workspace_dump(void)
1115 g_message("Dumping TagManager workspace tree..");
1117 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
1119 TMSourceFile
*source_file
= theWorkspace
->source_files
->pdata
[i
];
1120 fprintf(stderr
, "%s", source_file
->file_name
);
1123 #endif /* TM_DEBUG */
1129 find_namespace_members_tags (const GPtrArray
* all
, GPtrArray
* tags
,
1130 const langType langJava
, const char *name
,
1131 const char *filename
)
1133 GPtrArray
*local
= g_ptr_array_new ();
1136 size_t len
= strlen (name
);
1138 g_return_val_if_fail (all
!= NULL
, 0);
1140 for (i
= 0; (i
< all
->len
); ++i
)
1142 tag
= TM_TAG (all
->pdata
[i
]);
1143 if (filename
&& tag
->file
&&
1144 0 != strcmp (filename
,
1145 tag
->file
->short_name
))
1150 if (tag
&& tag
->scope
&& tag
->scope
[0] != '\0')
1152 if (0 == strncmp (name
, tag
->scope
, len
))
1154 g_ptr_array_add (local
, tag
);
1162 for (i
= 0; (i
< local
->len
); ++i
)
1164 tag
= TM_TAG (local
->pdata
[i
]);
1167 /* if we wanna complete something like
1169 * we'll just return the tags that have "namespace1"
1170 * as their scope. So we won't return classes/members/namespaces
1171 * under, for example, namespace2, where namespace1::namespace2
1173 if (scope
&& 0 == strcmp (name
, scope
))
1175 g_ptr_array_add (tags
, tag
);
1180 g_ptr_array_free (local
, TRUE
);
1181 return (int) tags
->len
;
1184 static const GPtrArray
*
1185 tm_workspace_find_namespace_members (const GPtrArray
* file_tags
, const char *name
,
1186 gboolean search_global
)
1188 static GPtrArray
*tags
= NULL
;
1189 GPtrArray
*local
= NULL
;
1190 char *new_name
= (char *) name
;
1191 char *filename
= NULL
;
1192 int found
= 0, del
= 0;
1193 static langType langJava
= -1;
1196 g_return_val_if_fail (name
&& name
[0] != '\0', NULL
);
1199 tags
= g_ptr_array_new ();
1203 const GPtrArray
*tags2
;
1205 TMTagType types
= (tm_tag_class_t
1206 tm_tag_struct_t
| tm_tag_typedef_t
|
1207 tm_tag_union_t
| tm_tag_enum_t
);
1211 g_ptr_array_set_size (tags
, 0);
1212 got
= fill_find_tags_array (tags
, file_tags
,
1213 new_name
, NULL
, types
, FALSE
, -1, FALSE
);
1223 TMTagAttrType attrs
[] = {
1224 tm_tag_attr_name_t
, tm_tag_attr_type_t
,
1227 tags2
= tm_workspace_find (new_name
, types
, attrs
, FALSE
, -1);
1230 if ((tags2
) && (tags2
->len
== 1) && (tag
= TM_TAG (tags2
->pdata
[0])))
1232 if (tag
->type
== tm_tag_typedef_t
&& tag
->var_type
1233 && tag
->var_type
[0] != '\0')
1235 new_name
= tag
->var_type
;
1238 filename
= (tag
->file
?
1239 tag
->file
->short_name
: NULL
);
1240 if (tag
->scope
&& tag
->scope
[0] != '\0')
1244 tag
->file
->lang
== langJava
)
1246 new_name
= g_strdup_printf ("%s.%s",
1252 new_name
= g_strdup_printf ("%s::%s",
1265 g_ptr_array_set_size (tags
, 0);
1267 if (tag
&& tag
->file
)
1269 local
= tm_tags_extract (tag
->file
->tags_array
,
1270 (tm_tag_function_t
|
1271 tm_tag_field_t
| tm_tag_enumerator_t
|
1272 tm_tag_namespace_t
| tm_tag_class_t
));
1276 local
= tm_tags_extract (theWorkspace
->tags_array
,
1277 (tm_tag_function_t
| tm_tag_prototype_t
|
1279 tm_tag_field_t
| tm_tag_enumerator_t
|
1280 tm_tag_namespace_t
| tm_tag_class_t
));
1285 found
= find_namespace_members_tags (local
, tags
,
1286 langJava
, new_name
, filename
);
1287 g_ptr_array_free (local
, TRUE
);
1291 if (!found
&& search_global
)
1293 GPtrArray
*global
= tm_tags_extract (theWorkspace
->global_tags
,
1295 tm_tag_prototype_t
|
1299 tm_tag_enumerator_t
|
1300 tm_tag_namespace_t
|
1305 find_namespace_members_tags (global
, tags
, langJava
,
1306 new_name
, filename
);
1308 DEBUG_PRINT ("returning these");
1310 for (i=0; i < tags->len; i++) {
1313 cur_tag = (TMTag*)g_ptr_array_index (tags, i);
1314 tm_tag_print (cur_tag, stdout );
1317 g_ptr_array_free (global, TRUE);
1331 /* Returns a list of parent classes for the given class name
1332 @param name Name of the class
1333 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1334 static const GPtrArray
*tm_workspace_get_parents(const gchar
*name
)
1336 static TMTagAttrType type
[] = { tm_tag_attr_name_t
, tm_tag_attr_none_t
};
1337 static GPtrArray
*parents
= NULL
;
1338 const GPtrArray
*matches
;
1345 g_return_val_if_fail(name
&& isalpha(*name
),NULL
);
1347 if (NULL
== parents
)
1348 parents
= g_ptr_array_new();
1350 g_ptr_array_set_size(parents
, 0);
1351 matches
= tm_workspace_find(name
, tm_tag_class_t
, type
, FALSE
, -1);
1352 if ((NULL
== matches
) || (0 == matches
->len
))
1354 g_ptr_array_add(parents
, matches
->pdata
[0]);
1355 while (i
< parents
->len
)
1357 tag
= TM_TAG(parents
->pdata
[i
]);
1358 if ((NULL
!= tag
->inheritance
) && (isalpha(tag
->inheritance
[0])))
1360 klasses
= g_strsplit(tag
->inheritance
, ",", 10);
1361 for (klass
= klasses
; (NULL
!= *klass
); ++ klass
)
1363 for (j
=0; j
< parents
->len
; ++j
)
1365 if (0 == strcmp(*klass
, TM_TAG(parents
->pdata
[j
])->name
))
1368 if (parents
->len
== j
)
1370 matches
= tm_workspace_find(*klass
, tm_tag_class_t
, type
, FALSE
, -1);
1371 if ((NULL
!= matches
) && (0 < matches
->len
))
1372 g_ptr_array_add(parents
, matches
->pdata
[0]);
1375 g_strfreev(klasses
);