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>
30 #include <glib/gstdio.h>
32 #include "tm_workspace.h"
35 #include "tm_parser.h"
38 /* when changing, always keep the three sort criteria below in sync */
39 static TMTagAttrType workspace_tags_sort_attrs
[] =
41 tm_tag_attr_name_t
, tm_tag_attr_file_t
, tm_tag_attr_line_t
,
42 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
45 /* for file tags the file is always identical, don't use for sorting */
46 static TMTagAttrType file_tags_sort_attrs
[] =
48 tm_tag_attr_name_t
, tm_tag_attr_line_t
,
49 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
52 /* global tags don't have file/line information */
53 static TMTagAttrType global_tags_sort_attrs
[] =
56 tm_tag_attr_type_t
, tm_tag_attr_scope_t
, tm_tag_attr_arglist_t
, 0
59 static TMTagType TM_TYPE_WITH_MEMBERS
=
60 tm_tag_class_t
| tm_tag_struct_t
| tm_tag_union_t
|
61 tm_tag_enum_t
| tm_tag_interface_t
;
63 static TMTagType TM_GLOBAL_TYPE_MASK
=
64 tm_tag_class_t
| tm_tag_enum_t
| tm_tag_interface_t
|
65 tm_tag_struct_t
| tm_tag_typedef_t
| tm_tag_union_t
| tm_tag_namespace_t
;
67 static TMWorkspace
*theWorkspace
= NULL
;
70 static gboolean
tm_create_workspace(void)
72 theWorkspace
= g_new(TMWorkspace
, 1);
73 theWorkspace
->tags_array
= g_ptr_array_new();
75 theWorkspace
->global_tags
= g_ptr_array_new();
76 theWorkspace
->source_files
= g_ptr_array_new();
77 theWorkspace
->typename_array
= g_ptr_array_new();
78 theWorkspace
->global_typename_array
= g_ptr_array_new();
81 tm_parser_verify_type_mappings();
87 /* Frees the workspace structure and all child source files. Use only when
88 exiting from the main program.
90 void tm_workspace_free(void)
95 g_message("Workspace destroyed");
98 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
99 tm_source_file_free(theWorkspace
->source_files
->pdata
[i
]);
100 g_ptr_array_free(theWorkspace
->source_files
, TRUE
);
101 tm_tags_array_free(theWorkspace
->global_tags
, TRUE
);
102 g_ptr_array_free(theWorkspace
->tags_array
, TRUE
);
103 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
104 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
105 g_free(theWorkspace
);
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
116 const TMWorkspace
*tm_get_workspace(void)
118 if (NULL
== theWorkspace
)
119 tm_create_workspace();
124 static void tm_workspace_merge_tags(GPtrArray
**big_array
, GPtrArray
*small_array
)
126 GPtrArray
*new_tags
= tm_tags_merge(*big_array
, small_array
, workspace_tags_sort_attrs
, FALSE
);
127 /* tags owned by TMSourceFile - free just the pointer array */
128 g_ptr_array_free(*big_array
, TRUE
);
129 *big_array
= new_tags
;
133 static void merge_extracted_tags(GPtrArray
**dest
, GPtrArray
*src
, TMTagType tag_types
)
137 arr
= tm_tags_extract(src
, tag_types
);
138 tm_workspace_merge_tags(dest
, arr
);
139 g_ptr_array_free(arr
, TRUE
);
143 static void update_source_file(TMSourceFile
*source_file
, guchar
* text_buf
,
144 gsize buf_size
, gboolean use_buffer
, gboolean update_workspace
)
147 g_message("Source file updating based on source file %s", source_file
->file_name
);
150 if (update_workspace
)
152 /* tm_source_file_parse() deletes the tag objects - remove the tags from
153 * workspace while they exist and can be scanned */
154 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
155 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
157 tm_source_file_parse(source_file
, text_buf
, buf_size
, use_buffer
);
158 tm_tags_sort(source_file
->tags_array
, file_tags_sort_attrs
, FALSE
, TRUE
);
159 if (update_workspace
)
162 g_message("Updating workspace from source file");
164 tm_workspace_merge_tags(&theWorkspace
->tags_array
, source_file
->tags_array
);
166 merge_extracted_tags(&(theWorkspace
->typename_array
), source_file
->tags_array
, TM_GLOBAL_TYPE_MASK
);
170 g_message("Skipping workspace update because update_workspace is %s",
171 update_workspace
?"TRUE":"FALSE");
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.
181 void tm_workspace_add_source_file(TMSourceFile
*source_file
)
183 g_return_if_fail(source_file
!= NULL
);
185 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
186 update_source_file(source_file
, NULL
, 0, FALSE
, TRUE
);
190 void tm_workspace_add_source_file_noupdate(TMSourceFile
*source_file
)
192 g_return_if_fail(source_file
!= NULL
);
194 g_ptr_array_add(theWorkspace
->source_files
, source_file
);
198 /* Updates the source file by reparsing the text-buffer passed as parameter.
199 Ctags will use a parsing based on buffer instead of on files.
200 You should call this function when you don't want a previous saving of the file
201 you're editing. It's useful for a "real-time" updating of the tags.
202 The tags array and the tags themselves are destroyed and re-created, hence any
203 other tag arrays pointing to these tags should be rebuilt as well. All sorting
204 information is also lost.
205 @param source_file The source file to update with a buffer.
206 @param text_buf A text buffer. The user should take care of allocate and free it after
208 @param buf_size The size of text_buf.
210 void tm_workspace_update_source_file_buffer(TMSourceFile
*source_file
, guchar
* text_buf
,
213 update_source_file(source_file
, text_buf
, buf_size
, TRUE
, TRUE
);
217 /** Removes a source file from the workspace if it exists. This function also removes
218 the tags belonging to this file from the workspace. To completely free the TMSourceFile
219 pointer call tm_source_file_free() on it.
220 @param source_file Pointer to the source file to be removed.
223 void tm_workspace_remove_source_file(TMSourceFile
*source_file
)
227 g_return_if_fail(source_file
!= NULL
);
229 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
231 if (theWorkspace
->source_files
->pdata
[i
] == source_file
)
233 tm_tags_remove_file_tags(source_file
, theWorkspace
->tags_array
);
234 tm_tags_remove_file_tags(source_file
, theWorkspace
->typename_array
);
235 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, i
);
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
247 static void tm_workspace_update(void)
250 TMSourceFile
*source_file
;
253 g_message("Recreating workspace tags array");
256 g_ptr_array_set_size(theWorkspace
->tags_array
, 0);
259 g_message("Total %d objects", theWorkspace
->source_files
->len
);
261 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
263 source_file
= theWorkspace
->source_files
->pdata
[i
];
265 g_message("Adding tags of %s", source_file
->file_name
);
267 if (source_file
->tags_array
->len
> 0)
269 for (j
= 0; j
< source_file
->tags_array
->len
; ++j
)
271 g_ptr_array_add(theWorkspace
->tags_array
,
272 source_file
->tags_array
->pdata
[j
]);
277 g_message("Total: %d tags", theWorkspace
->tags_array
->len
);
279 tm_tags_sort(theWorkspace
->tags_array
, workspace_tags_sort_attrs
, TRUE
, FALSE
);
281 g_ptr_array_free(theWorkspace
->typename_array
, TRUE
);
282 theWorkspace
->typename_array
= tm_tags_extract(theWorkspace
->tags_array
, TM_GLOBAL_TYPE_MASK
);
286 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
287 This is more efficient than calling tm_workspace_add_source_file() and
288 tm_workspace_update_source_file() separately for each of the files.
289 @param source_files @elementtype{TMSourceFile} The source files to be added to the workspace.
292 void tm_workspace_add_source_files(GPtrArray
*source_files
)
296 g_return_if_fail(source_files
!= NULL
);
298 for (i
= 0; i
< source_files
->len
; i
++)
300 TMSourceFile
*source_file
= source_files
->pdata
[i
];
302 tm_workspace_add_source_file_noupdate(source_file
);
303 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
306 tm_workspace_update();
310 /** Removes multiple source files from the workspace and updates the workspace tag
311 arrays. This is more efficient than calling tm_workspace_remove_source_file()
312 separately for each of the files. To completely free the TMSourceFile pointers
313 call tm_source_file_free() on each of them.
314 @param source_files @elementtype{TMSourceFile} The source files to be removed from the workspace.
317 void tm_workspace_remove_source_files(GPtrArray
*source_files
)
321 g_return_if_fail(source_files
!= NULL
);
323 //TODO: sort both arrays by pointer value and remove in single pass
324 for (i
= 0; i
< source_files
->len
; i
++)
326 TMSourceFile
*source_file
= source_files
->pdata
[i
];
328 for (j
= 0; j
< theWorkspace
->source_files
->len
; j
++)
330 if (theWorkspace
->source_files
->pdata
[j
] == source_file
)
332 g_ptr_array_remove_index_fast(theWorkspace
->source_files
, j
);
338 tm_workspace_update();
342 /* Loads the global tag list from the specified file. The global tag list should
343 have been first created using tm_workspace_create_global_tags().
344 @param tags_file The file containing global tags.
345 @return TRUE on success, FALSE on failure.
346 @see tm_workspace_create_global_tags()
348 gboolean
tm_workspace_load_global_tags(const char *tags_file
, TMParserType mode
)
350 GPtrArray
*file_tags
, *new_tags
;
352 file_tags
= tm_source_file_read_tags_file(tags_file
, mode
);
356 tm_tags_sort(file_tags
, global_tags_sort_attrs
, TRUE
, TRUE
);
358 /* reorder the whole array, because tm_tags_find expects a sorted array */
359 new_tags
= tm_tags_merge(theWorkspace
->global_tags
,
360 file_tags
, global_tags_sort_attrs
, TRUE
);
361 g_ptr_array_free(theWorkspace
->global_tags
, TRUE
);
362 g_ptr_array_free(file_tags
, TRUE
);
363 theWorkspace
->global_tags
= new_tags
;
365 g_ptr_array_free(theWorkspace
->global_typename_array
, TRUE
);
366 theWorkspace
->global_typename_array
= tm_tags_extract(new_tags
, TM_GLOBAL_TYPE_MASK
);
372 static gboolean
write_includes_file(const gchar
*outf
, GList
*includes_files
)
374 FILE *fp
= g_fopen(outf
, "w");
375 GList
*node
= includes_files
;
382 char *str
= g_strdup_printf("#include \"%s\"\n", (char*)node
->data
);
383 size_t str_len
= strlen(str
);
385 fwrite(str
, str_len
, 1, fp
);
387 node
= g_list_next(node
);
390 return fclose(fp
) == 0;
394 static gboolean
combine_include_files(const gchar
*outf
, GList
*file_list
)
396 FILE *fp
= g_fopen(outf
, "w");
397 GList
*node
= file_list
;
404 const char *fname
= node
->data
;
409 if (! g_file_get_contents(fname
, &contents
, &length
, &err
))
411 fprintf(stderr
, "Unable to read file: %s\n", err
->message
);
416 fwrite(contents
, length
, 1, fp
);
417 fwrite("\n", 1, 1, fp
); /* in case file doesn't end in newline (e.g. windows). */
420 node
= g_list_next (node
);
423 return fclose(fp
) == 0;
427 static gchar
*create_temp_file(const gchar
*tpl
)
432 fd
= g_file_open_tmp(tpl
, &name
, NULL
);
441 static GList
*lookup_includes(const gchar
**includes
, gint includes_count
)
443 GList
*includes_files
= NULL
;
444 GHashTable
*table
; /* used for deduping */
451 table
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, NULL
);
456 if (includes
[0][0] == '"') /* leading \" char for glob matching */
458 for (i
= 0; i
< includes_count
; i
++)
460 size_t dirty_len
= strlen(includes
[i
]);
466 clean_path
= g_malloc(dirty_len
- 1);
468 strncpy(clean_path
, includes
[i
] + 1, dirty_len
- 1);
469 clean_path
[dirty_len
- 2] = 0;
472 g_message ("[o][%s]\n", clean_path
);
474 glob(clean_path
, 0, NULL
, &globbuf
);
477 g_message ("matches: %d\n", globbuf
.gl_pathc
);
480 for (idx_glob
= 0; idx_glob
< globbuf
.gl_pathc
; idx_glob
++)
483 g_message (">>> %s\n", globbuf
.gl_pathv
[idx_glob
]);
485 if (!g_hash_table_lookup(table
, globbuf
.gl_pathv
[idx_glob
]))
487 gchar
*file_name_copy
= g_strdup(globbuf
.gl_pathv
[idx_glob
]);
489 includes_files
= g_list_prepend(includes_files
, file_name_copy
);
490 g_hash_table_insert(table
, file_name_copy
, file_name_copy
);
492 g_message ("Added ...\n");
501 #endif /* HAVE_GLOB_H */
503 /* no glob support or globbing not wanted */
504 for (i
= 0; i
< includes_count
; i
++)
506 if (!g_hash_table_lookup(table
, includes
[i
]))
508 gchar
* file_name_copy
= g_strdup(includes
[i
]);
510 includes_files
= g_list_prepend(includes_files
, file_name_copy
);
511 g_hash_table_insert(table
, file_name_copy
, file_name_copy
);
516 g_hash_table_destroy(table
);
518 return g_list_reverse(includes_files
);
521 static gchar
*pre_process_file(const gchar
*cmd
, const gchar
*inf
)
524 gchar
*outf
= create_temp_file("tmp_XXXXXX.cpp");
526 gchar
*errors
= NULL
;
532 tmp_errfile
= create_temp_file("tmp_XXXXXX");
540 command
= g_strdup_printf("%s %s >%s 2>%s",
541 cmd
, inf
, outf
, tmp_errfile
);
543 g_message("Executing: %s", command
);
545 ret
= system(command
);
548 g_file_get_contents(tmp_errfile
, &errors
, NULL
, NULL
);
549 if (errors
&& *errors
)
550 g_printerr("%s\n", errors
);
552 g_unlink(tmp_errfile
);
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'
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.
576 gboolean
tm_workspace_create_global_tags(const char *pre_process
, const char **includes
,
577 int includes_count
, const char *tags_file
, TMParserType lang
)
579 gboolean ret
= FALSE
;
580 TMSourceFile
*source_file
;
581 GList
*includes_files
;
582 gchar
*temp_file
= create_temp_file("tmp_XXXXXX.cpp");
583 GPtrArray
*filtered_tags
;
588 includes_files
= lookup_includes(includes
, includes_count
);
591 g_message ("writing out files to %s\n", temp_file
);
594 ret
= write_includes_file(temp_file
, includes_files
);
596 ret
= combine_include_files(temp_file
, includes_files
);
598 g_list_free_full(includes_files
, g_free
);
605 gchar
*temp_file2
= pre_process_file(pre_process
, temp_file
);
611 temp_file
= temp_file2
;
617 source_file
= tm_source_file_new(temp_file
, tm_source_file_get_lang_name(lang
));
620 update_source_file(source_file
, NULL
, 0, FALSE
, FALSE
);
621 if (source_file
->tags_array
->len
== 0)
623 tm_source_file_free(source_file
);
627 tm_tags_sort(source_file
->tags_array
, global_tags_sort_attrs
, TRUE
, FALSE
);
628 filtered_tags
= tm_tags_extract(source_file
->tags_array
, ~tm_tag_local_var_t
);
629 ret
= tm_source_file_write_tags_file(tags_file
, filtered_tags
);
630 g_ptr_array_free(filtered_tags
, TRUE
);
631 tm_source_file_free(source_file
);
640 static void fill_find_tags_array(GPtrArray
*dst
, const GPtrArray
*src
,
641 const char *name
, const char *scope
, TMTagType type
, TMParserType lang
)
646 if (!src
|| !dst
|| !name
|| !*name
)
649 tag
= tm_tags_find(src
, name
, FALSE
, &num
);
650 for (i
= 0; i
< num
; ++i
)
652 if ((type
& (*tag
)->type
) &&
653 tm_parser_langs_compatible(lang
, (*tag
)->lang
) &&
654 (!scope
|| g_strcmp0((*tag
)->scope
, scope
) == 0))
656 g_ptr_array_add(dst
, *tag
);
663 /* Returns all matching tags found in the workspace.
664 @param name The name of the tag to find.
665 @param scope The scope name of the tag to find, or NULL.
666 @param type The tag types to return (TMTagType). Can be a bitmask.
667 @param attrs The attributes to sort and dedup on (0 terminated integer array).
668 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
670 @return Array of matching tags.
672 GPtrArray
*tm_workspace_find(const char *name
, const char *scope
, TMTagType type
,
673 TMTagAttrType
*attrs
, TMParserType lang
)
675 GPtrArray
*tags
= g_ptr_array_new();
677 fill_find_tags_array(tags
, theWorkspace
->tags_array
, name
, scope
, type
, lang
);
678 fill_find_tags_array(tags
, theWorkspace
->global_tags
, name
, scope
, type
, lang
);
681 tm_tags_sort(tags
, attrs
, TRUE
, FALSE
);
687 gboolean
tm_workspace_is_autocomplete_tag(TMTag
*tag
,
688 TMSourceFile
*current_file
,
690 const gchar
*current_scope
)
692 TMParserType lang
= current_file
? current_file
->lang
: TM_PARSER_NONE
;
694 /* ignore local variables from other files/functions or after current line */
695 gboolean valid
= !(tag
->type
& tm_tag_local_var_t
) ||
696 (current_file
== tag
->file
&&
697 current_line
>= tag
->line
&&
698 g_strcmp0(current_scope
, tag
->scope
) == 0);
699 return valid
&& !tm_tag_is_anon(tag
) && tm_parser_langs_compatible(lang
, tag
->lang
);
710 static gboolean
is_any_tag(TMTag
*tag
, CopyInfo
*info
)
715 static gboolean
is_local_tag(TMTag
*tag
, CopyInfo
*info
)
717 return tag
->type
& tm_tag_local_var_t
;
720 static gboolean
is_non_local_tag(TMTag
*tag
, CopyInfo
*info
)
722 return !is_local_tag(tag
, info
);
725 /* non-local tag not from current file */
726 static gboolean
is_workspace_tag(TMTag
*tag
, CopyInfo
*info
)
728 return tag
->file
!= info
->file
&&
729 is_non_local_tag(tag
, info
);
733 static guint
copy_tags(GPtrArray
*dst
, TMTag
**src
, guint src_len
, GHashTable
*name_table
,
734 gint num
, gboolean (*predicate
) (TMTag
*, CopyInfo
*), CopyInfo
*info
)
738 g_return_val_if_fail(src
&& dst
, 0);
740 for (i
= 0; i
< src_len
&& num
> 0; i
++)
743 if (predicate(tag
, info
) &&
744 tm_workspace_is_autocomplete_tag(tag
, info
->file
, info
->line
, info
->scope
) &&
745 !g_hash_table_contains(name_table
, tag
->name
))
747 g_ptr_array_add(dst
, tag
);
748 g_hash_table_add(name_table
, tag
->name
);
756 static void fill_find_tags_array_prefix(GPtrArray
*dst
, const char *name
,
757 CopyInfo
*info
, guint max_num
)
761 GHashTable
*name_table
;
763 if (!dst
|| !name
|| !*name
)
766 name_table
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, NULL
);
770 found
= tm_tags_find(info
->file
->tags_array
, name
, TRUE
, &count
);
773 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_local_tag
, info
);
774 if (dst
->len
< max_num
)
775 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_non_local_tag
, info
);
778 if (dst
->len
< max_num
)
780 found
= tm_tags_find(theWorkspace
->tags_array
, name
, TRUE
, &count
);
782 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_workspace_tag
, info
);
784 if (dst
->len
< max_num
)
786 found
= tm_tags_find(theWorkspace
->global_tags
, name
, TRUE
, &count
);
788 copy_tags(dst
, found
, count
, name_table
, max_num
- dst
->len
, is_any_tag
, info
);
791 g_hash_table_unref(name_table
);
798 gboolean sort_by_name
;
802 static gint
sort_found_tags(gconstpointer a
, gconstpointer b
, gpointer user_data
)
804 SortInfo
*info
= user_data
;
805 const TMTag
*t1
= *((TMTag
**) a
);
806 const TMTag
*t2
= *((TMTag
**) b
);
808 /* sort local vars first (with highest line number first), followed
809 * by tags from current file, followed by workspace tags, followed by
811 if (t1
->type
& tm_tag_local_var_t
&& t2
->type
& tm_tag_local_var_t
)
812 return info
->sort_by_name
? g_strcmp0(t1
->name
, t2
->name
) : t2
->line
- t1
->line
;
813 else if (t1
->type
& tm_tag_local_var_t
)
815 else if (t2
->type
& tm_tag_local_var_t
)
817 else if (t1
->file
== info
->file
&& t2
->file
!= info
->file
)
819 else if (t2
->file
== info
->file
&& t1
->file
!= info
->file
)
821 else if (t1
->file
&& !t2
->file
)
823 else if (t2
->file
&& !t1
->file
)
825 return g_strcmp0(t1
->name
, t2
->name
);
829 /* Returns tags with the specified prefix sorted by name, ignoring local
830 variables from other files/functions or after current line. If there are several
831 tags with the same name, only one of them appears in the resulting array.
832 @param prefix The prefix of the tag to find.
833 @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found,
835 @param max_num The maximum number of tags to return.
836 @return Array of matching tags sorted by their name.
838 GPtrArray
*tm_workspace_find_prefix(const char *prefix
,
839 TMSourceFile
*current_file
,
841 const gchar
*current_scope
,
844 TMTagAttrType attrs
[] = { tm_tag_attr_name_t
, 0 };
845 GPtrArray
*tags
= g_ptr_array_new();
849 copy_info
.file
= current_file
;
850 copy_info
.line
= current_line
;
851 copy_info
.scope
= current_scope
;
852 fill_find_tags_array_prefix(tags
, prefix
, ©_info
, max_num
);
854 /* sort based on how "close" the tag is to current line with local
856 sort_info
.file
= current_file
;
857 sort_info
.sort_by_name
= TRUE
;
858 g_ptr_array_sort_with_data(tags
, sort_found_tags
, &sort_info
);
864 static gboolean
replace_with_char(gchar
*haystack
, const gchar
*needle
, char replacement
)
866 gchar
*pos
= strstr(haystack
, needle
);
881 static gboolean
replace_parens_with_char(gchar
*haystack
, gchar paren_begin
, gchar paren_end
, char replacement
)
883 gchar needle
[2] = {paren_begin
, '\0'};
884 gchar
*pos
= strstr(haystack
, needle
);
891 if (*pos
== paren_begin
)
893 else if (*pos
== paren_end
)
906 static gchar
*strip_type(const gchar
*scoped_name
, TMParserType lang
, gboolean remove_scope
)
908 if (scoped_name
!= NULL
)
910 const gchar
*sep
= tm_parser_scope_separator(lang
);
911 gchar
*name
= g_strdup(scoped_name
);
914 /* remove pointers, parens and keywords appearing in types */
915 g_strdelimit(name
, "*^&", ' ');
916 while (replace_parens_with_char(name
, '[', ']', ' ')) {}
917 while (replace_parens_with_char(name
, '<', '>', ' ')) {}
918 while (replace_with_char(name
, "const ", ' ')) {}
919 while (replace_with_char(name
, " const", ' ')) {}
920 while (replace_with_char(name
, " struct", ' ')) {}
921 /* remove everything before final scope separator */
922 if (remove_scope
&& (scope_suffix
= g_strrstr(name
, sep
)))
924 scope_suffix
+= strlen(sep
);
925 scope_suffix
= g_strdup(scope_suffix
);
937 /* Gets all members of type_tag; search them inside the all array.
938 * The namespace parameter determines whether we are performing the "namespace"
939 * search (user has typed something like "A::" where A is a type) or "scope" search
940 * (user has typed "a." where a is a global struct-like variable). With the
941 * namespace search we return all direct descendants of any type while with the
942 * scope search we return only those which can be invoked on a variable (member,
945 find_scope_members_tags (const GPtrArray
*all
, TMTag
*type_tag
, gboolean
namespace, guint depth
)
947 TMTagType member_types
= tm_tag_max_t
& ~(TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
);
953 return NULL
; /* simple inheritence cycle avoidance */
955 tags
= g_ptr_array_new();
958 member_types
= tm_tag_max_t
;
960 if (type_tag
->scope
&& *(type_tag
->scope
))
961 scope
= g_strconcat(type_tag
->scope
, tm_parser_scope_separator(type_tag
->lang
), type_tag
->name
, NULL
);
963 scope
= g_strdup(type_tag
->name
);
965 for (i
= 0; i
< all
->len
; ++i
)
967 TMTag
*tag
= TM_TAG (all
->pdata
[i
]);
969 if (tag
&& (tag
->type
& member_types
) &&
970 tag
->scope
&& tag
->scope
[0] != '\0' &&
971 tm_parser_langs_compatible(tag
->lang
, type_tag
->lang
) &&
972 strcmp(scope
, tag
->scope
) == 0 &&
973 (!namespace || !tm_tag_is_anon(tag
)))
975 g_ptr_array_add (tags
, tag
);
979 /* add members from parent classes */
980 if (!namespace && (type_tag
->type
& (tm_tag_class_t
| tm_tag_struct_t
)) &&
981 type_tag
->inheritance
&& *type_tag
->inheritance
)
983 gchar
*stripped
= strip_type(type_tag
->inheritance
, type_tag
->lang
, FALSE
);
984 gchar
**split_strv
= g_strsplit(stripped
, ",", -1); /* parent classes */
989 for (i
= 0; parent
= split_strv
[i
]; i
++)
991 GPtrArray
*parent_tags
;
993 stripped
= strip_type(parent
, type_tag
->lang
, TRUE
);
994 parent_tags
= tm_workspace_find(stripped
, NULL
, tm_tag_class_t
| tm_tag_struct_t
,
995 NULL
, type_tag
->lang
);
997 if (parent_tags
->len
> 0)
999 TMTag
*parent_tag
= parent_tags
->pdata
[0];
1000 GPtrArray
*parent_members
= find_scope_members_tags(
1001 parent_tag
->file
? parent_tag
->file
->tags_array
: all
, parent_tag
,
1007 for (j
= 0; j
< parent_members
->len
; j
++)
1008 g_ptr_array_add (tags
, parent_members
->pdata
[j
]);
1009 g_ptr_array_free(parent_members
, TRUE
);
1013 g_ptr_array_free(parent_tags
, TRUE
);
1017 g_strfreev(split_strv
);
1024 g_ptr_array_free(tags
, TRUE
);
1030 TMTagAttrType sort_attrs
[] = {tm_tag_attr_name_t
, 0};
1031 tm_tags_sort(tags
, sort_attrs
, TRUE
, FALSE
);
1038 /* Gets all members of the type with the given name; search them inside tags_array */
1040 find_scope_members (const GPtrArray
*tags_array
, const gchar
*name
, TMSourceFile
*file
,
1041 TMParserType lang
, gboolean
namespace)
1043 GPtrArray
*res
= NULL
;
1047 g_return_val_if_fail(name
&& *name
, NULL
);
1049 type_name
= g_strdup(name
);
1051 /* Check if type_name is a type that can possibly contain members.
1052 * Try to resolve intermediate typedefs to get the real type name. Also
1053 * add scope information to the name if applicable.
1054 * The loop below loops only when resolving typedefs - avoid possibly infinite
1055 * loop when typedefs create a cycle by adding some limits. */
1056 for (i
= 0; i
< 5; i
++)
1059 GPtrArray
*type_tags
;
1060 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
1064 types
&= ~tm_tag_enum_t
;
1066 type_tags
= g_ptr_array_new();
1067 fill_find_tags_array(type_tags
, tags_array
, type_name
, NULL
, types
, lang
);
1069 for (j
= 0; j
< type_tags
->len
; j
++)
1071 TMTag
*test_tag
= TM_TAG(type_tags
->pdata
[j
]);
1073 /* anonymous type defined in a different file than the variable -
1074 * this isn't the type we are looking for */
1075 if (tm_tag_is_anon(test_tag
) && (file
!= test_tag
->file
|| test_tag
->file
== NULL
))
1080 /* prefer non-typedef tags because we can be sure they contain members */
1081 if (test_tag
->type
!= tm_tag_typedef_t
)
1085 g_ptr_array_free(type_tags
, TRUE
);
1087 if (!tag
) /* not a type that can contain members */
1090 /* intermediate typedef - resolve to the real type */
1091 if (tag
->type
== tm_tag_typedef_t
)
1093 if (tag
->var_type
&& tag
->var_type
[0] != '\0')
1096 type_name
= strip_type(tag
->var_type
, tag
->lang
, TRUE
);
1102 else /* real type with members */
1104 /* use the same file as the composite type if file information available */
1105 res
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: tags_array
, tag
, namespace, 0);
1116 /* Checks whether a member tag is directly accessible from method */
1117 static gboolean
member_accessible(const GPtrArray
*tags
, const gchar
*method_scope
, TMTag
*member_tag
,
1120 const gchar
*sep
= tm_parser_scope_separator(lang
);
1121 gboolean ret
= FALSE
;
1125 /* method scope is in the form ...::class_name::method_name */
1126 comps
= g_strsplit (method_scope
, sep
, 0);
1127 len
= g_strv_length(comps
);
1130 gchar
*cls
= comps
[len
- 2];
1134 /* find method's class members */
1135 GPtrArray
*cls_tags
= find_scope_members(tags
, cls
, NULL
, lang
, FALSE
);
1141 /* check if one of the class members is member_tag */
1142 for (i
= 0; i
< cls_tags
->len
; i
++)
1144 TMTag
*t
= cls_tags
->pdata
[i
];
1146 if (t
== member_tag
)
1152 g_ptr_array_free(cls_tags
, TRUE
);
1162 /* For an array of variable/type tags, find members inside the types */
1164 find_scope_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
, TMParserType lang
,
1165 gboolean member
, const gchar
*current_scope
)
1167 GPtrArray
*member_tags
= NULL
;
1170 /* there may be several variables/types with the same name - try each of them until
1171 * we find something */
1172 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
1174 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
1175 TMTagType member_types
= tm_tag_member_t
| tm_tag_field_t
| tm_tag_method_t
;
1176 TMTagType types
= TM_TYPE_WITH_MEMBERS
| tm_tag_typedef_t
;
1178 if (tag
->type
& types
) /* type: namespace search */
1180 if (tag
->type
& tm_tag_typedef_t
)
1181 member_tags
= find_scope_members(searched_array
, tag
->name
, tag
->file
, lang
, TRUE
);
1183 member_tags
= find_scope_members_tags(tag
->file
? tag
->file
->tags_array
: searched_array
,
1186 else if (tag
->var_type
) /* variable: scope search */
1188 /* The question now is whether we should use member tags (such as
1189 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
1190 * (which means user has typed something like foo.bar.) or if we are
1191 * inside a method where foo is a class member, we want scope completion
1193 if (!(tag
->type
& member_types
) || member
||
1194 member_accessible(searched_array
, current_scope
, tag
, lang
))
1196 gchar
*tag_type
= strip_type(tag
->var_type
, tag
->lang
, TRUE
);
1198 member_tags
= find_scope_members(searched_array
, tag_type
, tag
->file
, lang
, FALSE
);
1208 static GPtrArray
*find_namespace_members_all(const GPtrArray
*tags
, const GPtrArray
*searched_array
)
1210 GPtrArray
*member_tags
= NULL
;
1213 for (i
= 0; i
< tags
->len
&& !member_tags
; i
++)
1215 TMTag
*tag
= TM_TAG(tags
->pdata
[i
]);
1217 member_tags
= find_scope_members_tags(searched_array
, tag
, TRUE
, 0);
1224 /* Returns all member tags of a struct/union/class if the provided name is a variable
1225 of such a type or the name of the type.
1226 @param source_file TMSourceFile of the edited source file
1227 @param name Name of the variable/type whose members are searched
1228 @param function TRUE if the name is a name of a function
1229 @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
1230 @param current_scope The current scope in the editor
1231 @param current_line The current line in the editor
1232 @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::)
1233 @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
1235 tm_workspace_find_scope_members (TMSourceFile
*source_file
, const char *name
,
1236 gboolean function
, gboolean member
, const gchar
*current_scope
, guint current_line
,
1237 gboolean search_namespace
)
1239 TMParserType lang
= source_file
? source_file
->lang
: TM_PARSER_NONE
;
1240 GPtrArray
*tags
, *member_tags
= NULL
;
1241 TMTagType function_types
= tm_tag_function_t
| tm_tag_method_t
|
1242 tm_tag_macro_with_arg_t
| tm_tag_prototype_t
;
1243 TMTagType tag_type
= tm_tag_max_t
&
1244 ~(function_types
| tm_tag_enumerator_t
| tm_tag_namespace_t
| tm_tag_package_t
);
1245 TMTagAttrType sort_attr
[] = {tm_tag_attr_name_t
, 0};
1247 if (search_namespace
)
1249 tags
= tm_workspace_find(name
, NULL
, tm_tag_namespace_t
, NULL
, lang
);
1251 member_tags
= find_namespace_members_all(tags
, theWorkspace
->tags_array
);
1253 member_tags
= find_namespace_members_all(tags
, theWorkspace
->global_tags
);
1255 g_ptr_array_free(tags
, TRUE
);
1264 tag_type
= function_types
;
1266 /* tags corresponding to the variable/type name */
1267 tags
= tm_workspace_find(name
, NULL
, tag_type
, NULL
, lang
);
1269 /* remove invalid local tags and sort tags so "nearest" tags are first */
1270 for (i
= 0; i
< tags
->len
; i
++)
1272 TMTag
*tag
= tags
->pdata
[i
];
1273 if (!tm_workspace_is_autocomplete_tag(tag
, source_file
, current_line
, current_scope
))
1274 tags
->pdata
[i
] = NULL
;
1276 tm_tags_prune(tags
);
1278 info
.file
= source_file
;
1279 info
.sort_by_name
= FALSE
;
1280 g_ptr_array_sort_with_data(tags
, sort_found_tags
, &info
);
1282 /* Start searching inside the source file, continue with workspace tags and
1283 * end with global tags. This way we find the "closest" tag to the current
1284 * file in case there are more of them. */
1286 member_tags
= find_scope_members_all(tags
, source_file
->tags_array
,
1287 lang
, member
, current_scope
);
1289 member_tags
= find_scope_members_all(tags
, theWorkspace
->tags_array
, lang
,
1290 member
, current_scope
);
1292 member_tags
= find_scope_members_all(tags
, theWorkspace
->global_tags
, lang
,
1293 member
, current_scope
);
1295 g_ptr_array_free(tags
, TRUE
);
1299 tm_tags_dedup(member_tags
, sort_attr
, FALSE
);
1307 /* Dumps the workspace tree - useful for debugging */
1308 void tm_workspace_dump(void)
1313 g_message("Dumping TagManager workspace tree..");
1315 for (i
=0; i
< theWorkspace
->source_files
->len
; ++i
)
1317 TMSourceFile
*source_file
= theWorkspace
->source_files
->pdata
[i
];
1318 fprintf(stderr
, "%s", source_file
->file_name
);
1321 #endif /* TM_DEBUG */