3 * Copyright (c) 2001-2002, Biswapesh Chattopadhyay
4 * Copyright 2005 The Geany contributors
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
12 * @file tm_workspace.h
13 The TMWorkspace structure is meant to be used as a singleton to store application
16 The workspace is intended to contain a list of global tags
17 and a set of individual source files.
24 #include <sys/types.h>
27 #include <glib/gstdio.h>
29 #include "tm_workspace.h"
32 #include "tm_parser.h"
35 /* when changing, always keep the three sort criteria below in sync */
36 static TMTagAttrType workspace_tags_sort_attrs
[] =
38 tm_tag_attr_name_t
, tm_tag_attr_file_t
, tm_tag_attr_line_t
,
39 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
42 /* for file tags the file is always identical, don't use for sorting */
43 static TMTagAttrType file_tags_sort_attrs
[] =
45 tm_tag_attr_name_t
, tm_tag_attr_line_t
,
46 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
49 /* global tags don't have file/line information */
50 static TMTagAttrType global_tags_sort_attrs
[] =
53 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
56 static TMTagType TM_TYPE_WITH_MEMBERS
=
57 tm_tag_class_t
| tm_tag_struct_t
| tm_tag_union_t
|
58 tm_tag_enum_t
| tm_tag_interface_t
;
60 static TMTagType TM_GLOBAL_TYPE_MASK
=
61 tm_tag_class_t
| tm_tag_enum_t
| tm_tag_interface_t
|
62 tm_tag_struct_t
| tm_tag_typedef_t
| tm_tag_union_t
| tm_tag_namespace_t
;
64 static TMWorkspace
*theWorkspace
= NULL
;
67 static void free_ptr_array(gpointer arr
)
69 g_ptr_array_free(arr
, TRUE
);
73 static gboolean
tm_create_workspace(void)
75 theWorkspace
= g_new(TMWorkspace
, 1);
76 theWorkspace
->tags_array
= g_ptr_array_new();
78 theWorkspace
->global_tags
= g_ptr_array_new();
79 theWorkspace
->source_files
= g_ptr_array_new();
80 theWorkspace
->typename_array
= g_ptr_array_new();
81 theWorkspace
->global_typename_array
= g_ptr_array_new();
82 theWorkspace
->source_file_map
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
86 tm_parser_verify_type_mappings();
92 /* Frees the workspace structure and all child source files. Use only when
93 exiting from the main program.
95 void tm_workspace_free(void)
100 g_message("Workspace destroyed");
103 g_hash_table_destroy(theWorkspace
->source_file_map
);
104 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
105 tm_source_file_free(theWorkspace
->source_files
->pdata
[i
]);
106 g_ptr_array_free(theWorkspace
->source_files
, TRUE
);
107 tm_tags_array_free(theWorkspace
->global_tags
, TRUE
);
108 g_ptr_array_free(theWorkspace
->tags_array
, TRUE
);
109 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
110 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
111 g_free(theWorkspace
);
116 /* Since TMWorkspace is a singleton, you should not create multiple
117 workspaces, but get a pointer to the workspace whenever required. The first
118 time a pointer is requested, or a source file is added to the workspace,
119 a workspace is created. Subsequent calls to the function will return the
122 const TMWorkspace
*tm_get_workspace(void)
124 if (NULL
== theWorkspace
)
125 tm_create_workspace();
130 static void tm_workspace_merge_tags(GPtrArray
**big_array
, GPtrArray
*small_array
)
132 GPtrArray
*new_tags
= tm_tags_merge(*big_array
, small_array
, workspace_tags_sort_attrs
, FALSE
);
133 /* tags owned by TMSourceFile - free just the pointer array */
134 g_ptr_array_free(*big_array
, TRUE
);
135 *big_array
= new_tags
;
139 static void merge_extracted_tags(GPtrArray
**dest
, GPtrArray
*src
, TMTagType tag_types
)
143 arr
= tm_tags_extract(src
, tag_types
);
144 tm_workspace_merge_tags(dest
, arr
);
145 g_ptr_array_free(arr
, TRUE
);
149 static void update_source_file(TMSourceFile
*source_file
, guchar
* text_buf
,
150 gsize buf_size
, gboolean use_buffer
, gboolean update_workspace
)
153 g_message("Source file updating based on source file %s", source_file
->file_name
);
156 if (update_workspace
)
158 /* tm_source_file_parse() deletes the tag objects - remove the tags from
159 * workspace while they exist and can be scanned */
160 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
161 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
163 tm_source_file_parse(source_file
, text_buf
, buf_size
, use_buffer
);
164 tm_tags_sort(source_file
->tags_array
, file_tags_sort_attrs
, FALSE
, TRUE
);
165 if (update_workspace
)
168 g_message("Updating workspace from source file");
170 tm_workspace_merge_tags(&theWorkspace
->tags_array
, source_file
->tags_array
);
172 merge_extracted_tags(&(theWorkspace
->typename_array
), source_file
->tags_array
, TM_GLOBAL_TYPE_MASK
);
176 g_message("Skipping workspace update because update_workspace is %s",
177 update_workspace
?"TRUE":"FALSE");
183 void tm_workspace_add_source_file_noupdate(TMSourceFile
*source_file
)
187 g_return_if_fail(source_file
!= NULL
);
189 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
191 file_arr
= g_hash_table_lookup(theWorkspace
->source_file_map
, source_file
->short_name
);
194 file_arr
= g_ptr_array_new();
195 g_hash_table_insert(theWorkspace
->source_file_map
, g_strdup(source_file
->short_name
), file_arr
);
197 g_ptr_array_add(file_arr
, source_file
);
201 /** Adds a source file to the workspace, parses it and updates the workspace tags.
202 @param source_file The source file to add to the workspace.
205 void tm_workspace_add_source_file(TMSourceFile
*source_file
)
207 g_return_if_fail(source_file
!= NULL
);
209 tm_workspace_add_source_file_noupdate(source_file
);
210 update_source_file(source_file
, NULL
, 0, FALSE
, TRUE
);
214 /* Updates the source file by reparsing the text-buffer passed as parameter.
215 Ctags will use a parsing based on buffer instead of on files.
216 You should call this function when you don't want a previous saving of the file
217 you're editing. It's useful for a "real-time" updating of the tags.
218 The tags array and the tags themselves are destroyed and re-created, hence any
219 other tag arrays pointing to these tags should be rebuilt as well. All sorting
220 information is also lost.
221 @param source_file The source file to update with a buffer.
222 @param text_buf A text buffer. The user should take care of allocate and free it after
224 @param buf_size The size of text_buf.
226 void tm_workspace_update_source_file_buffer(TMSourceFile
*source_file
, guchar
* text_buf
,
229 update_source_file(source_file
, text_buf
, buf_size
, TRUE
, TRUE
);
233 static void remove_source_file_map(TMSourceFile
*source_file
)
235 GPtrArray
*file_arr
= g_hash_table_lookup(theWorkspace
->source_file_map
, source_file
->short_name
);
238 g_ptr_array_remove_fast(file_arr
, source_file
);
242 /** Removes a source file from the workspace if it exists. This function also removes
243 the tags belonging to this file from the workspace. To completely free the TMSourceFile
244 pointer call tm_source_file_free() on it.
245 @param source_file Pointer to the source file to be removed.
248 void tm_workspace_remove_source_file(TMSourceFile
*source_file
)
252 g_return_if_fail(source_file
!= NULL
);
254 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
256 if (theWorkspace
->source_files
->pdata
[i
] == source_file
)
258 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
259 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
260 remove_source_file_map(source_file
);
261 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, i
);
268 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
269 want to globally refresh the workspace. This function does not call tm_source_file_update()
270 which should be called before this function on source files which need to be
273 static void tm_workspace_update(void)
276 TMSourceFile
*source_file
;
279 g_message("Recreating workspace tags array");
282 g_ptr_array_set_size(theWorkspace
->tags_array
, 0);
285 g_message("Total %d objects", theWorkspace
->source_files
->len
);
287 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
289 source_file
= theWorkspace
->source_files
->pdata
[i
];
291 g_message("Adding tags of %s", source_file
->file_name
);
293 if (source_file
->tags_array
->len
> 0)
295 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
297 g_ptr_array_add(theWorkspace
->tags_array
,
298 source_file
->tags_array
->pdata
[j
]);
303 g_message("Total: %d tags", theWorkspace
->tags_array
->len
);
305 tm_tags_sort(theWorkspace
->tags_array
, workspace_tags_sort_attrs
, TRUE
, FALSE
);
307 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
308 theWorkspace
->typename_array
= tm_tags_extract(theWorkspace
->tags_array
, TM_GLOBAL_TYPE_MASK
);
312 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
313 This is more efficient than calling tm_workspace_add_source_file() and
314 tm_workspace_update_source_file() separately for each of the files.
315 @param source_files @elementtype{TMSourceFile} The source files to be added to the workspace.
318 void tm_workspace_add_source_files(GPtrArray
*source_files
)
322 g_return_if_fail(source_files
!= NULL
);
324 for (i
= 0; i
< source_files
->len
; i
++)
326 TMSourceFile
*source_file
= source_files
->pdata
[i
];
328 tm_workspace_add_source_file_noupdate(source_file
);
329 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
332 tm_workspace_update();
336 /** Removes multiple source files from the workspace and updates the workspace tag
337 arrays. This is more efficient than calling tm_workspace_remove_source_file()
338 separately for each of the files. To completely free the TMSourceFile pointers
339 call tm_source_file_free() on each of them.
340 @param source_files @elementtype{TMSourceFile} The source files to be removed from the workspace.
343 void tm_workspace_remove_source_files(GPtrArray
*source_files
)
347 g_return_if_fail(source_files
!= NULL
);
349 //TODO: sort both arrays by pointer value and remove in single pass
350 for (i
= 0; i
< source_files
->len
; i
++)
352 TMSourceFile
*source_file
= source_files
->pdata
[i
];
354 for (j
= 0; j
< theWorkspace
->source_files
->len
; j
++)
356 if (theWorkspace
->source_files
->pdata
[j
] == source_file
)
358 remove_source_file_map(source_file
);
359 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, j
);
365 tm_workspace_update();
369 /* Loads the global tag list from the specified file. The global tag list should
370 have been first created using tm_workspace_create_global_tags().
371 @param tags_file The file containing global tags.
372 @return TRUE on success, FALSE on failure.
373 @see tm_workspace_create_global_tags()
375 gboolean
tm_workspace_load_global_tags(const char *tags_file
, TMParserType mode
)
377 GPtrArray
*file_tags
, *new_tags
;
379 file_tags
= tm_source_file_read_tags_file(tags_file
, mode
);
383 tm_tags_sort(file_tags
, global_tags_sort_attrs
, TRUE
, TRUE
);
385 /* reorder the whole array, because tm_tags_find expects a sorted array */
386 new_tags
= tm_tags_merge(theWorkspace
->global_tags
,
387 file_tags
, global_tags_sort_attrs
, TRUE
);
388 g_ptr_array_free(theWorkspace
->global_tags
, TRUE
);
389 g_ptr_array_free(file_tags
, TRUE
);
390 theWorkspace
->global_tags
= new_tags
;
392 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
393 theWorkspace
->global_typename_array
= tm_tags_extract(new_tags
, TM_GLOBAL_TYPE_MASK
);
399 static gboolean
write_includes_file(const gchar
*outf
, GList
*includes_files
)
401 FILE *fp
= g_fopen(outf
, "w");
402 GList
*node
= includes_files
;
409 char *str
= g_strdup_printf("#include \"%s\"\n", (char*)node
->data
);
410 size_t str_len
= strlen(str
);
412 fwrite(str
, str_len
, 1, fp
);
414 node
= g_list_next(node
);
417 return fclose(fp
) == 0;
420 static gchar
*create_temp_file(const gchar
*tpl
)
425 fd
= g_file_open_tmp(tpl
, &name
, NULL
);
434 static GList
*lookup_sources(const gchar
**sources
, gint sources_count
)
436 GList
*source_files
= NULL
;
437 GHashTable
*table
; /* used for deduping */
440 table
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, NULL
);
442 for (i
= 0; i
< sources_count
; i
++)
444 if (!g_hash_table_lookup(table
, sources
[i
]))
446 gchar
* file_name_copy
= g_strdup(sources
[i
]);
448 source_files
= g_list_prepend(source_files
, file_name_copy
);
449 g_hash_table_insert(table
, file_name_copy
, file_name_copy
);
453 g_hash_table_destroy(table
);
455 return g_list_reverse(source_files
);
458 static gchar
*pre_process_file(const gchar
*cmd
, const gchar
*inf
)
461 gchar
*outf
= create_temp_file("tmp_XXXXXX.cpp");
463 gchar
*errors
= NULL
;
469 tmp_errfile
= create_temp_file("tmp_XXXXXX");
477 command
= g_strdup_printf("%s %s >%s 2>%s",
478 cmd
, inf
, outf
, tmp_errfile
);
480 g_message("Executing: %s", command
);
482 ret
= system(command
);
485 g_file_get_contents(tmp_errfile
, &errors
, NULL
, NULL
);
486 if (errors
&& *errors
)
487 g_printerr("%s\n", errors
);
489 g_unlink(tmp_errfile
);
502 static gboolean
create_global_tags_preprocessed(const char *pre_process_cmd
,
503 GList
*source_files
, const char *tags_file
, TMParserType lang
)
505 TMSourceFile
*source_file
;
506 gboolean ret
= FALSE
;
508 gchar
*temp_file
= create_temp_file("tmp_XXXXXX.cpp");
509 GPtrArray
*filtered_tags
;
512 g_message ("writing out files to %s\n", temp_file
);
518 if (!write_includes_file(temp_file
, source_files
))
521 temp_file2
= pre_process_file(pre_process_cmd
, temp_file
);
527 temp_file
= temp_file2
;
532 source_file
= tm_source_file_new(temp_file
, tm_source_file_get_lang_name(lang
));
535 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
536 if (source_file
->tags_array
->len
== 0)
538 tm_source_file_free(source_file
);
542 tm_tags_sort(source_file
->tags_array
, global_tags_sort_attrs
, TRUE
, FALSE
);
543 filtered_tags
= tm_tags_extract(source_file
->tags_array
, ~(tm_tag_local_var_t
| tm_tag_include_t
));
544 ret
= tm_source_file_write_tags_file(tags_file
, filtered_tags
);
545 g_ptr_array_free(filtered_tags
, TRUE
);
546 tm_source_file_free(source_file
);
554 static gboolean
create_global_tags_direct(GList
*source_files
, const char *tags_file
,
558 GPtrArray
*filtered_tags
;
559 GPtrArray
*tags
= g_ptr_array_new();
560 GSList
*tm_source_files
= NULL
;
561 gboolean ret
= FALSE
;
563 for (node
= source_files
; node
; node
= node
->next
)
565 TMSourceFile
*source_file
= tm_source_file_new(node
->data
, tm_source_file_get_lang_name(lang
));
569 tm_source_files
= g_slist_prepend(tm_source_files
, source_file
);
570 tm_source_file_parse(source_file
, NULL
, 0, FALSE
);
571 for (i
= 0; i
< source_file
->tags_array
->len
; i
++)
572 g_ptr_array_add(tags
, source_file
->tags_array
->pdata
[i
]);
576 filtered_tags
= tm_tags_extract(tags
, ~(tm_tag_local_var_t
| tm_tag_include_t
));
577 tm_tags_sort(filtered_tags
, global_tags_sort_attrs
, TRUE
, FALSE
);
579 if (filtered_tags
->len
> 0)
580 ret
= tm_source_file_write_tags_file(tags_file
, filtered_tags
);
582 g_ptr_array_free(tags
, TRUE
);
583 g_ptr_array_free(filtered_tags
, TRUE
);
584 g_slist_free_full(tm_source_files
, (GDestroyNotify
)tm_source_file_free
);
589 /* Creates a list of global tags. Ideally, this should be created once during
590 installations so that all users can use the same file. This is because a full
591 scale global tag list can occupy several megabytes of disk space.
592 @param pre_process_cmd The pre-processing command. This is executed via system(),
593 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
594 @param sources Source files to process. Wildcards such as '/usr/include/a*.h'
596 @param tags_file The file where the tags will be stored.
597 @param lang The language to use for the tags file.
598 @return TRUE on success, FALSE on failure.
600 gboolean
tm_workspace_create_global_tags(const char *pre_process_cmd
, const char **sources
,
601 int sources_count
, const char *tags_file
, TMParserType lang
)
603 gboolean ret
= FALSE
;
604 GList
*source_files
= lookup_sources(sources
, sources_count
);
607 ret
= create_global_tags_preprocessed(pre_process_cmd
, source_files
, tags_file
, lang
);
609 ret
= create_global_tags_direct(source_files
, tags_file
, lang
);
611 g_list_free_full(source_files
, g_free
);
615 static void fill_find_tags_array(GPtrArray
*dst
, const GPtrArray
*src
,
616 const char *name
, const char *scope
, TMTagType type
, TMParserType lang
)
621 if (!src
|| !dst
|| !name
|| !*name
)
624 tag
= tm_tags_find(src
, name
, FALSE
, &num
);
625 for (i
= 0; i
< num
; ++i
)
627 if ((type
& (*tag
)->type
) &&
628 tm_parser_langs_compatible(lang
, (*tag
)->lang
) &&
629 (!scope
|| g_strcmp0((*tag
)->scope
, scope
) == 0))
631 g_ptr_array_add(dst
, *tag
);
638 /* Returns all matching tags found in the workspace.
639 @param name The name of the tag to find.
640 @param scope The scope name of the tag to find, or NULL.
641 @param type The tag types to return (TMTagType). Can be a bitmask.
642 @param attrs The attributes to sort and dedup on (0 terminated integer array).
643 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
645 @return Array of matching tags.
647 GPtrArray
*tm_workspace_find(const char *name
, const char *scope
, TMTagType type
,
648 TMTagAttrType
*attrs
, TMParserType lang
)
650 GPtrArray
*tags
= g_ptr_array_new();
652 fill_find_tags_array(tags
, theWorkspace
->tags_array
, name
, scope
, type
, lang
);
653 fill_find_tags_array(tags
, theWorkspace
->global_tags
, name
, scope
, type
, lang
);
656 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
662 gboolean
tm_workspace_is_autocomplete_tag(TMTag
*tag
,
663 TMSourceFile
*current_file
,
665 const gchar
*current_scope
)
667 TMParserType lang
= current_file
? current_file
->lang
: TM_PARSER_NONE
;
669 /* ignore local variables from other files/functions or after current line */
670 gboolean valid
= !(tag
->type
& tm_tag_local_var_t
) ||
671 (current_file
== tag
->file
&&
672 current_line
>= tag
->line
&&
673 g_strcmp0(current_scope
, tag
->scope
) == 0);
675 /* tag->local indicates per-file-only visibility such as static C functions */
676 gboolean valid_local
= !tag
->local
|| current_file
== tag
->file
;
678 return valid
&& valid_local
&&
679 !tm_tag_is_anon(tag
) && tm_parser_langs_compatible(lang
, tag
->lang
) &&
680 !(tag
->type
& tm_tag_include_t
);
684 #if ! GLIB_CHECK_VERSION(2, 54, 0)
686 g_ptr_array_find (GPtrArray
*haystack
,
687 gconstpointer needle
,
692 g_return_val_if_fail (haystack
!= NULL
, FALSE
);
694 for (i
= 0; i
< haystack
->len
; i
++)
696 if (g_direct_equal (g_ptr_array_index (haystack
, i
), needle
))
712 GPtrArray
*header_candidates
;
713 GHashTable
*includes
;
718 static gboolean
is_any_tag(TMTag
*tag
, CopyInfo
*info
)
723 static gboolean
is_local_tag(TMTag
*tag
, CopyInfo
*info
)
725 return tag
->type
& tm_tag_local_var_t
;
728 static gboolean
is_non_local_tag(TMTag
*tag
, CopyInfo
*info
)
730 return !is_local_tag(tag
, info
);
733 /* non-local tag not from current file, header, or included files */
734 static gboolean
is_workspace_tag(TMTag
*tag
, CopyInfo
*info
)
736 return tag
->file
!= info
->file
&&
737 (!info
->header_candidates
|| !g_ptr_array_find(info
->header_candidates
, tag
->file
, NULL
)) &&
738 !g_hash_table_lookup(info
->includes
, tag
->file
) &&
739 is_non_local_tag(tag
, info
);
743 static void copy_tags(GPtrArray
*dst
, TMTag
**src
, guint src_len
, GHashTable
*name_table
,
744 gint num
, gboolean (*predicate
) (TMTag
*, CopyInfo
*), CopyInfo
*info
)
748 g_return_if_fail(src
&& dst
);
750 for (i
= 0; i
< src_len
&& num
> 0; i
++)
753 if (predicate(tag
, info
) &&
754 tm_workspace_is_autocomplete_tag(tag
, info
->file
, info
->line
, info
->scope
) &&
755 !g_hash_table_contains(name_table
, tag
->name
))
757 g_ptr_array_add(dst
, tag
);
758 g_hash_table_add(name_table
, tag
->name
);
766 static void fill_find_tags_array_prefix(GPtrArray
*dst
, const char *name
,
767 CopyInfo
*info
, guint max_num
)
771 GHashTable
*name_table
;
773 if (!dst
|| !name
|| !*name
)
776 name_table
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, NULL
);
780 found
= tm_tags_find(info
->file
->tags_array
, name
, TRUE
, &count
);
783 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_local_tag
, info
);
784 if (dst
->len
< max_num
)
785 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_non_local_tag
, info
);
788 if (dst
->len
< max_num
&& info
->header_candidates
)
792 for (i
= 0; i
< info
->header_candidates
->len
; i
++)
794 TMSourceFile
*hdr
= info
->header_candidates
->pdata
[i
];
795 found
= tm_tags_find(hdr
->tags_array
, name
, TRUE
, &count
);
797 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_non_local_tag
, info
);
800 if (dst
->len
< max_num
)
805 g_hash_table_iter_init(&iter
, info
->includes
);
806 while (g_hash_table_iter_next(&iter
, &key
, NULL
))
808 TMSourceFile
*include_file
= key
;
809 found
= tm_tags_find(include_file
->tags_array
, name
, TRUE
, &count
);
811 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_non_local_tag
, info
);
814 if (dst
->len
< max_num
)
816 found
= tm_tags_find(theWorkspace
->tags_array
, name
, TRUE
, &count
);
818 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_workspace_tag
, info
);
820 if (dst
->len
< max_num
)
822 found
= tm_tags_find(theWorkspace
->global_tags
, name
, TRUE
, &count
);
824 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_any_tag
, info
);
827 g_hash_table_unref(name_table
);
831 /* return TMSourceFile files corresponding to files included in 'source';
832 * in addition, fill header_candidates with TMSourceFiles that could be the header
833 * of 'source' based on the file name */
834 static GHashTable
*get_includes(TMSourceFile
*source
, GPtrArray
**header_candidates
)
836 GHashTable
*includes
= g_hash_table_new(NULL
, NULL
);
838 gchar
*src_basename
, *ptr
;
841 *header_candidates
= NULL
;
844 (source
->lang
!= TM_PARSER_C
&& source
->lang
!= TM_PARSER_CPP
))
847 src_basename
= g_strdup(source
->short_name
);
848 if (ptr
= strrchr(src_basename
, '.'))
851 headers
= tm_tags_extract(source
->tags_array
, tm_tag_include_t
);
853 for (i
= 0; i
< headers
->len
; i
++)
855 TMTag
*hdr_tag
= headers
->pdata
[i
];
856 gchar
*hdr_name
= g_path_get_basename(hdr_tag
->name
);
857 GPtrArray
*tm_files
= g_hash_table_lookup(theWorkspace
->source_file_map
, hdr_name
);
859 if (tm_files
&& tm_files
->len
> 0)
863 if (!*header_candidates
)
865 gchar
*hdr_basename
= g_strdup(hdr_name
);
866 if (ptr
= strrchr(hdr_basename
, '.'))
869 if (g_strcmp0(hdr_basename
, src_basename
) == 0)
870 *header_candidates
= tm_files
;
871 g_free(hdr_basename
);
874 for (j
= 0; j
< tm_files
->len
; j
++)
875 g_hash_table_add(includes
, tm_files
->pdata
[j
]);
881 g_ptr_array_free(headers
, TRUE
);
882 g_free(src_basename
);
890 GPtrArray
*header_candidates
;
891 GHashTable
*includes
;
892 gboolean sort_by_name
;
896 static gint
sort_found_tags(gconstpointer a
, gconstpointer b
, gpointer user_data
)
898 SortInfo
*info
= user_data
;
899 const TMTag
*t1
= *((TMTag
**) a
);
900 const TMTag
*t2
= *((TMTag
**) b
);
902 /* sort local vars first (with highest line number first),
903 * followed by tags from current file,
904 * followed by tags from header,
905 * followed by tags from other included files,
906 * followed by workspace tags,
907 * followed by global tags */
908 if (t1
->type
& tm_tag_local_var_t
&& t2
->type
& tm_tag_local_var_t
)
909 return info
->sort_by_name
? g_strcmp0(t1
->name
, t2
->name
) : t2
->line
- t1
->line
;
910 else if (t1
->type
& tm_tag_local_var_t
)
912 else if (t2
->type
& tm_tag_local_var_t
)
914 else if (t1
->file
== info
->file
&& t2
->file
!= info
->file
)
916 else if (t2
->file
== info
->file
&& t1
->file
!= info
->file
)
918 else if (info
->header_candidates
&&
919 g_ptr_array_find(info
->header_candidates
, t1
->file
, NULL
) &&
920 !g_ptr_array_find(info
->header_candidates
, t2
->file
, NULL
))
922 else if (info
->header_candidates
&&
923 g_ptr_array_find(info
->header_candidates
, t2
->file
, NULL
) &&
924 !g_ptr_array_find(info
->header_candidates
, t1
->file
, NULL
))
926 else if (g_hash_table_lookup(info
->includes
, t1
->file
) && !g_hash_table_lookup(info
->includes
, t2
->file
))
928 else if (g_hash_table_lookup(info
->includes
, t2
->file
) && !g_hash_table_lookup(info
->includes
, t1
->file
))
930 else if (t1
->file
&& !t2
->file
)
932 else if (t2
->file
&& !t1
->file
)
934 return g_strcmp0(t1
->name
, t2
->name
);
938 /* Returns tags with the specified prefix sorted by name, ignoring local
939 variables from other files/functions or after current line. If there are several
940 tags with the same name, only one of them appears in the resulting array.
941 @param prefix The prefix of the tag to find.
942 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
944 @param max_num The maximum number of tags to return.
945 @return Array of matching tags sorted by their name.
947 GPtrArray
*tm_workspace_find_prefix(const char *prefix
,
948 TMSourceFile
*current_file
,
950 const gchar
*current_scope
,
953 TMTagAttrType attrs
[] = { tm_tag_attr_name_t
, 0 };
954 GPtrArray
*tags
= g_ptr_array_new();
955 GPtrArray
*header_candidates
;
958 GHashTable
*includes
= get_includes(current_file
, &header_candidates
);
960 copy_info
.file
= current_file
;
961 copy_info
.header_candidates
= header_candidates
;
962 copy_info
.includes
= includes
;
963 copy_info
.line
= current_line
;
964 copy_info
.scope
= current_scope
;
965 fill_find_tags_array_prefix(tags
, prefix
, ©_info
, max_num
);
967 /* sort based on how "close" the tag is to current line with local
969 sort_info
.file
= current_file
;
970 sort_info
.header_candidates
= header_candidates
;
971 sort_info
.includes
= includes
;
972 sort_info
.sort_by_name
= TRUE
;
973 g_ptr_array_sort_with_data(tags
, sort_found_tags
, &sort_info
);
975 g_hash_table_destroy(includes
);
981 static gboolean
replace_with_char(gchar
*haystack
, const gchar
*needle
, char replacement
)
983 gchar
*pos
= strstr(haystack
, needle
);
998 static gboolean
replace_parens_with_char(gchar
*haystack
, gchar paren_begin
, gchar paren_end
, char replacement
)
1000 gchar needle
[2] = {paren_begin
, '\0'};
1001 gchar
*pos
= strstr(haystack
, needle
);
1008 if (*pos
== paren_begin
)
1010 else if (*pos
== paren_end
)
1023 static gchar
*strip_type(const gchar
*scoped_name
, TMParserType lang
, gboolean remove_scope
)
1025 if (scoped_name
!= NULL
)
1027 const gchar
*sep
= tm_parser_scope_separator(lang
);
1028 gchar
*name
= g_strdup(scoped_name
);
1029 gchar
*scope_suffix
;
1031 /* remove pointers, parens and keywords appearing in types */
1032 g_strdelimit(name
, "*^&", ' ');
1033 while (replace_parens_with_char(name
, '[', ']', ' ')) {}
1034 while (replace_parens_with_char(name
, '<', '>', ' ')) {}
1035 while (replace_with_char(name
, "const ", ' ')) {}
1036 while (replace_with_char(name
, " const", ' ')) {}
1037 while (replace_with_char(name
, " struct", ' ')) {}
1038 /* remove everything before final scope separator */
1039 if (remove_scope
&& (scope_suffix
= g_strrstr(name
, sep
)))
1041 scope_suffix
+= strlen(sep
);
1042 scope_suffix
= g_strdup(scope_suffix
);
1044 name
= scope_suffix
;
1054 /* Gets all members of type_tag; search them inside the all array.
1055 * The namespace parameter determines whether we are performing the "namespace"
1056 * search (user has typed something like "A::" where A is a type) or "scope" search
1057 * (user has typed "a." where a is a global struct-like variable). With the
1058 * namespace search we return all direct descendants of any type while with the
1059 * scope search we return only those which can be invoked on a variable (member,
1062 find_scope_members_tags (const GPtrArray
*all
, TMTag
*type_tag
, gboolean
namespace, guint depth
)
1064 TMTagType member_types
= tm_tag_max_t
& ~(TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
);
1070 return NULL
; /* simple inheritence cycle avoidance */
1072 tags
= g_ptr_array_new();
1075 member_types
= tm_tag_max_t
;
1077 if (type_tag
->scope
&& *(type_tag
->scope
))
1078 scope
= g_strconcat(type_tag
->scope
, tm_parser_scope_separator(type_tag
->lang
), type_tag
->name
, NULL
);
1080 scope
= g_strdup(type_tag
->name
);
1082 for (i
= 0; i
< all
->len
; ++i
)
1084 TMTag
*tag
= TM_TAG (all
->pdata
[i
]);
1086 if (tag
&& (tag
->type
& member_types
) &&
1087 tag
->scope
&& tag
->scope
[0] != '\0' &&
1088 tm_parser_langs_compatible(tag
->lang
, type_tag
->lang
) &&
1089 strcmp(scope
, tag
->scope
) == 0 &&
1090 (!namespace || !tm_tag_is_anon(tag
)))
1092 g_ptr_array_add (tags
, tag
);
1096 /* add members from parent classes */
1097 if (!namespace && (type_tag
->type
& (tm_tag_class_t
| tm_tag_struct_t
)) &&
1098 type_tag
->inheritance
&& *type_tag
->inheritance
)
1100 gchar
*stripped
= strip_type(type_tag
->inheritance
, type_tag
->lang
, FALSE
);
1101 gchar
**split_strv
= g_strsplit(stripped
, ",", -1); /* parent classes */
1102 const gchar
*parent
;
1106 for (i
= 0; parent
= split_strv
[i
]; i
++)
1108 GPtrArray
*parent_tags
;
1110 stripped
= strip_type(parent
, type_tag
->lang
, TRUE
);
1111 parent_tags
= tm_workspace_find(stripped
, NULL
, tm_tag_class_t
| tm_tag_struct_t
,
1112 NULL
, type_tag
->lang
);
1114 if (parent_tags
->len
> 0)
1116 TMTag
*parent_tag
= parent_tags
->pdata
[0];
1117 GPtrArray
*parent_members
= find_scope_members_tags(
1118 parent_tag
->file
? parent_tag
->file
->tags_array
: all
, parent_tag
,
1124 for (j
= 0; j
< parent_members
->len
; j
++)
1125 g_ptr_array_add (tags
, parent_members
->pdata
[j
]);
1126 g_ptr_array_free(parent_members
, TRUE
);
1130 g_ptr_array_free(parent_tags
, TRUE
);
1134 g_strfreev(split_strv
);
1141 g_ptr_array_free(tags
, TRUE
);
1147 TMTagAttrType sort_attrs
[] = {tm_tag_attr_name_t
, 0};
1148 tm_tags_sort(tags
, sort_attrs
, TRUE
, FALSE
);
1155 /* Gets all members of the type with the given name; search them inside tags_array */
1157 find_scope_members (const GPtrArray
*tags_array
, const gchar
*name
, TMSourceFile
*file
,
1158 TMParserType lang
, gboolean
namespace)
1160 GPtrArray
*res
= NULL
;
1164 g_return_val_if_fail(name
&& *name
, NULL
);
1166 type_name
= g_strdup(name
);
1168 /* Check if type_name is a type that can possibly contain members.
1169 * Try to resolve intermediate typedefs to get the real type name. Also
1170 * add scope information to the name if applicable.
1171 * The loop below loops only when resolving typedefs - avoid possibly infinite
1172 * loop when typedefs create a cycle by adding some limits. */
1173 for (i
= 0; i
< 5; i
++)
1176 GPtrArray
*type_tags
;
1177 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
1181 types
&= ~tm_tag_enum_t
;
1183 type_tags
= g_ptr_array_new();
1184 fill_find_tags_array(type_tags
, tags_array
, type_name
, NULL
, types
, lang
);
1186 for (j
= 0; j
< type_tags
->len
; j
++)
1188 TMTag
*test_tag
= TM_TAG(type_tags
->pdata
[j
]);
1190 /* anonymous type defined in a different file than the variable -
1191 * this isn't the type we are looking for */
1192 if (tm_tag_is_anon(test_tag
) && (file
!= test_tag
->file
|| test_tag
->file
== NULL
))
1197 /* prefer non-typedef tags because we can be sure they contain members */
1198 if (test_tag
->type
!= tm_tag_typedef_t
)
1202 g_ptr_array_free(type_tags
, TRUE
);
1204 if (!tag
) /* not a type that can contain members */
1207 /* intermediate typedef - resolve to the real type */
1208 if (tag
->type
== tm_tag_typedef_t
)
1210 if (tag
->var_type
&& tag
->var_type
[0] != '\0')
1213 type_name
= strip_type(tag
->var_type
, tag
->lang
, TRUE
);
1219 else /* real type with members */
1221 /* use the same file as the composite type if file information available */
1222 res
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: tags_array
, tag
, namespace, 0);
1233 /* Checks whether a member tag is directly accessible from method */
1234 static gboolean
member_accessible(const GPtrArray
*tags
, const gchar
*method_scope
, TMTag
*member_tag
,
1237 const gchar
*sep
= tm_parser_scope_separator(lang
);
1238 gboolean ret
= FALSE
;
1242 /* method scope is in the form ...::class_name::method_name */
1243 comps
= g_strsplit (method_scope
, sep
, 0);
1244 len
= g_strv_length(comps
);
1247 gchar
*cls
= comps
[len
- 2];
1251 /* find method's class members */
1252 GPtrArray
*cls_tags
= find_scope_members(tags
, cls
, NULL
, lang
, FALSE
);
1258 /* check if one of the class members is member_tag */
1259 for (i
= 0; i
< cls_tags
->len
; i
++)
1261 TMTag
*t
= cls_tags
->pdata
[i
];
1263 if (t
== member_tag
)
1269 g_ptr_array_free(cls_tags
, TRUE
);
1279 /* For an array of variable/type tags, find members inside the types */
1281 find_scope_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, TMParserType lang
,
1282 gboolean member
, const gchar
*current_scope
)
1284 GPtrArray
*member_tags
= NULL
;
1287 /* there may be several variables/types with the same name - try each of them until
1288 * we find something */
1289 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
1291 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
1292 TMTagType member_types
= tm_tag_member_t
| tm_tag_field_t
| tm_tag_method_t
;
1293 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
1295 if (tag
->type
& types
) /* type: namespace search */
1297 if (tag
->type
& tm_tag_typedef_t
)
1298 member_tags
= find_scope_members(searched_array
, tag
->name
, tag
->file
, lang
, TRUE
);
1300 member_tags
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: searched_array
,
1303 else if (tag
->var_type
) /* variable: scope search */
1305 /* The question now is whether we should use member tags (such as
1306 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
1307 * (which means user has typed something like foo.bar.) or if we are
1308 * inside a method where foo is a class member, we want scope completion
1310 if (!(tag
->type
& member_types
) || member
||
1311 member_accessible(searched_array
, current_scope
, tag
, lang
))
1313 gchar
*tag_type
= strip_type(tag
->var_type
, tag
->lang
, TRUE
);
1315 member_tags
= find_scope_members(searched_array
, tag_type
, tag
->file
, lang
, FALSE
);
1325 static GPtrArray
*find_namespace_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
)
1327 GPtrArray
*member_tags
= NULL
;
1330 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
1332 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
1334 member_tags
= find_scope_members_tags(searched_array
, tag
, TRUE
, 0);
1341 /* Returns all member tags of a struct/union/class if the provided name is a variable
1342 of such a type or the name of the type.
1343 @param source_file TMSourceFile of the edited source file
1344 @param name Name of the variable/type whose members are searched
1345 @param function TRUE if the name is a name of a function
1346 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1347 @param current_scope The current scope in the editor
1348 @param current_line The current line in the editor
1349 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1350 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1352 tm_workspace_find_scope_members (TMSourceFile
*source_file
, const char *name
,
1353 gboolean function
, gboolean member
, const gchar
*current_scope
, guint current_line
,
1354 gboolean search_namespace
)
1356 TMParserType lang
= source_file
? source_file
->lang
: TM_PARSER_NONE
;
1357 GPtrArray
*tags
, *member_tags
= NULL
;
1358 TMTagType function_types
= tm_tag_function_t
| tm_tag_method_t
|
1359 tm_tag_macro_with_arg_t
| tm_tag_prototype_t
;
1360 TMTagType tag_type
= tm_tag_max_t
&
1361 ~(function_types
| tm_tag_enumerator_t
| tm_tag_namespace_t
| tm_tag_package_t
);
1362 TMTagAttrType sort_attr
[] = {tm_tag_attr_name_t
, 0};
1364 if (search_namespace
)
1366 tags
= tm_workspace_find(name
, NULL
, tm_tag_namespace_t
, NULL
, lang
);
1368 member_tags
= find_namespace_members_all(tags
, theWorkspace
->tags_array
);
1370 member_tags
= find_namespace_members_all(tags
, theWorkspace
->global_tags
);
1372 g_ptr_array_free(tags
, TRUE
);
1381 tag_type
= function_types
;
1383 /* tags corresponding to the variable/type name */
1384 tags
= tm_workspace_find(name
, NULL
, tag_type
, NULL
, lang
);
1386 /* remove invalid local tags and sort tags so "nearest" tags are first */
1387 for (i
= 0; i
< tags
->len
; i
++)
1389 TMTag
*tag
= tags
->pdata
[i
];
1390 if (!tm_workspace_is_autocomplete_tag(tag
, source_file
, current_line
, current_scope
))
1391 tags
->pdata
[i
] = NULL
;
1393 tm_tags_prune(tags
);
1395 info
.file
= source_file
;
1396 info
.sort_by_name
= FALSE
;
1397 info
.includes
= get_includes(source_file
, &info
.header_candidates
);
1398 g_ptr_array_sort_with_data(tags
, sort_found_tags
, &info
);
1400 /* Start searching inside the source file, continue with workspace tags and
1401 * end with global tags. This way we find the "closest" tag to the current
1402 * file in case there are more of them. */
1404 member_tags
= find_scope_members_all(tags
, source_file
->tags_array
,
1405 lang
, member
, current_scope
);
1407 member_tags
= find_scope_members_all(tags
, theWorkspace
->tags_array
, lang
,
1408 member
, current_scope
);
1410 member_tags
= find_scope_members_all(tags
, theWorkspace
->global_tags
, lang
,
1411 member
, current_scope
);
1413 g_ptr_array_free(tags
, TRUE
);
1414 g_hash_table_destroy(info
.includes
);
1418 tm_tags_dedup(member_tags
, sort_attr
, FALSE
);
1426 /* Dumps the workspace tree - useful for debugging */
1427 void tm_workspace_dump(void)
1432 g_message("Dumping TagManager workspace tree..");
1434 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
1436 TMSourceFile
*source_file
= theWorkspace
->source_files
->pdata
[i
];
1437 fprintf(stderr
, "%s", source_file
->file_name
);
1440 #endif /* TM_DEBUG */