Branch: refs/heads/master Author: Colomban Wendling ban@herbesfolles.org Committer: Colomban Wendling ban@herbesfolles.org Date: Sat, 08 Nov 2014 18:37:20 UTC Commit: 8c77accfd090c794e8eb1366772f417596052eb1 https://github.com/geany/geany/commit/8c77accfd090c794e8eb1366772f417596052e...
Log Message: ----------- Merge PR#356 from 'b4n/techee/tm'
Huge TagManager improvements
Modified Paths: -------------- doc/Doxyfile.in doc/Makefile.am plugins/geanyfunctions.h src/dialogs.c src/document.c src/document.h src/editor.c src/highlighting.c src/main.c src/plugindata.h src/plugins.c src/sidebar.c src/symbols.c src/symbols.h tagmanager/ctags/read.c tagmanager/ctags/read.h tagmanager/src/Makefile.am tagmanager/src/makefile.win32 tagmanager/src/tm_file_entry.c tagmanager/src/tm_file_entry.h tagmanager/src/tm_source_file.c tagmanager/src/tm_source_file.h tagmanager/src/tm_tag.c tagmanager/src/tm_tag.h tagmanager/src/tm_tagmanager.h tagmanager/src/tm_work_object.c tagmanager/src/tm_work_object.h tagmanager/src/tm_workspace.c tagmanager/src/tm_workspace.h tests/ctags/debian_432872.f90.tags tests/ctags/keyword_explicit.cs.tags tests/ctags/keyword_implicit.cs.tags tests/ctags/keyword_names.f90.tags tests/ctags/keyword_struct.cs.tags wscript
Modified: doc/Doxyfile.in 2 lines changed, 0 insertions(+), 2 deletions(-) =================================================================== @@ -778,8 +778,6 @@ INPUT = @top_srcdir@/src/ \ @top_srcdir@/plugins/geanyfunctions.h \ @top_srcdir@/tagmanager/src/tm_source_file.c \ @top_srcdir@/tagmanager/src/tm_source_file.h \ - @top_srcdir@/tagmanager/src/tm_work_object.c \ - @top_srcdir@/tagmanager/src/tm_work_object.h \ @top_srcdir@/tagmanager/src/tm_workspace.c \ @top_srcdir@/tagmanager/src/tm_workspace.h
Modified: doc/Makefile.am 1 lines changed, 0 insertions(+), 1 deletions(-) =================================================================== @@ -98,7 +98,6 @@ doxygen_sources = \ $(top_srcdir)/plugins/geanyplugin.h \ $(top_srcdir)/plugins/geanyfunctions.h \ $(top_srcdir)/tagmanager/src/tm_source_file.[ch] \ - $(top_srcdir)/tagmanager/src/tm_work_object.[ch] \ $(top_srcdir)/tagmanager/src/tm_workspace.[ch]
Doxyfile.stamp: Doxyfile $(doxygen_sources)
Modified: plugins/geanyfunctions.h 18 lines changed, 10 insertions(+), 8 deletions(-) =================================================================== @@ -346,14 +346,16 @@ geany_functions->p_tm->tm_get_real_path #define tm_source_file_new \ geany_functions->p_tm->tm_source_file_new -#define tm_workspace_add_object \ - geany_functions->p_tm->tm_workspace_add_object -#define tm_source_file_update \ - geany_functions->p_tm->tm_source_file_update -#define tm_work_object_free \ - geany_functions->p_tm->tm_work_object_free -#define tm_workspace_remove_object \ - geany_functions->p_tm->tm_workspace_remove_object +#define tm_source_file_free \ + geany_functions->p_tm->tm_source_file_free +#define tm_workspace_add_source_file \ + geany_functions->p_tm->tm_workspace_add_source_file +#define tm_workspace_remove_source_file \ + geany_functions->p_tm->tm_workspace_remove_source_file +#define tm_workspace_add_source_files \ + geany_functions->p_tm->tm_workspace_add_source_files +#define tm_workspace_remove_source_files \ + geany_functions->p_tm->tm_workspace_remove_source_files #define search_show_find_in_files_dialog \ geany_functions->p_search->search_show_find_in_files_dialog #define highlighting_get_style \
Modified: src/dialogs.c 10 lines changed, 7 insertions(+), 3 deletions(-) =================================================================== @@ -501,9 +501,13 @@ static gboolean handle_save_as(const gchar *utf8_filename, gboolean rename_file) { document_rename_file(doc, utf8_filename); } - /* create a new tm_source_file object otherwise tagmanager won't work correctly */ - tm_workspace_remove_object(doc->tm_file, TRUE, TRUE); - doc->tm_file = NULL; + if (doc->tm_file) + { + /* create a new tm_source_file object otherwise tagmanager won't work correctly */ + tm_workspace_remove_source_file(doc->tm_file); + tm_source_file_free(doc->tm_file); + doc->tm_file = NULL; + } } success = document_save_file_as(doc, utf8_filename);
Modified: src/document.c 40 lines changed, 16 insertions(+), 24 deletions(-) =================================================================== @@ -140,7 +140,7 @@ static GtkWidget* document_show_message(GeanyDocument *doc, GtkMessageType msgty * string returned by @c tm_get_real_path(). * * @return The matching document, or @c NULL. - * @note This is only really useful when passing a @c TMWorkObject::file_name. + * @note This is only really useful when passing a @c TMSourceFile::file_name. * @see GeanyDocument::real_path. * @see document_find_by_filename(). * @@ -714,7 +714,11 @@ static gboolean remove_page(guint page_num) g_free(doc->priv->saved_encoding.encoding); g_free(doc->file_name); g_free(doc->real_path); - tm_workspace_remove_object(doc->tm_file, TRUE, !main_status.quitting); + if (doc->tm_file) + { + tm_workspace_remove_source_file(doc->tm_file); + tm_source_file_free(doc->tm_file); + }
if (doc->priv->tag_tree) gtk_widget_destroy(doc->priv->tag_tree); @@ -2483,17 +2487,14 @@ void document_update_tags(GeanyDocument *doc)
/* lookup the name rather than using filetype name to support custom filetypes */ name = tm_source_file_get_lang_name(doc->file_type->lang); - doc->tm_file = tm_source_file_new(locale_filename, FALSE, name); + doc->tm_file = tm_source_file_new(locale_filename, name); g_free(locale_filename);
- if (doc->tm_file && !tm_workspace_add_object(doc->tm_file)) - { - tm_work_object_free(doc->tm_file); - doc->tm_file = NULL; - } + if (doc->tm_file) + tm_workspace_add_source_file_noupdate(doc->tm_file); }
- /* early out if there's no work object and we couldn't create one */ + /* early out if there's no tm source file and we couldn't create one */ if (doc->tm_file == NULL) { /* We must call sidebar_update_tag_list() before returning, @@ -2503,20 +2504,11 @@ void document_update_tags(GeanyDocument *doc) return; }
- len = sci_get_length(doc->editor->sci); - /* tm_source_file_buffer_update() below don't support 0-length data, - * so just empty the tags array and leave */ - if (len < 1) - { - tm_tags_array_free(doc->tm_file->tags_array, FALSE); - sidebar_update_tag_list(doc, FALSE); - return; - } - /* Parse Scintilla's buffer directly using TagManager * Note: this buffer *MUST NOT* be modified */ + len = sci_get_length(doc->editor->sci); buffer_ptr = (guchar *) scintilla_send_message(doc->editor->sci, SCI_GETCHARACTERPOINTER, 0, 0); - tm_source_file_buffer_update(doc->tm_file, buffer_ptr, len, TRUE); + tm_workspace_update_source_file_buffer(doc->tm_file, buffer_ptr, len);
sidebar_update_tag_list(doc, TRUE); document_highlight_tags(doc); @@ -2555,13 +2547,12 @@ void document_highlight_tags(GeanyDocument *doc) default: return; /* early out if type keywords are not supported */ } - if (!app->tm_workspace->work_object.tags_array) + if (!app->tm_workspace->tags_array) return;
/* get any type keywords and tell scintilla about them * this will cause the type keywords to be colourized in scintilla */ - keywords_str = symbols_find_tags_as_string(app->tm_workspace->work_object.tags_array, - TM_GLOBAL_TYPE_MASK, doc->file_type->lang); + keywords_str = symbols_find_typenames_as_string(doc->file_type->lang, FALSE); if (keywords_str) { keywords = g_string_free(keywords_str, FALSE); @@ -2617,7 +2608,8 @@ static void document_load_config(GeanyDocument *doc, GeanyFiletype *type, /* delete tm file object to force creation of a new one */ if (doc->tm_file != NULL) { - tm_workspace_remove_object(doc->tm_file, TRUE, TRUE); + tm_workspace_remove_source_file(doc->tm_file); + tm_source_file_free(doc->tm_file); doc->tm_file = NULL; } /* load tags files before highlighting (some lexers highlight global typenames) */
Modified: src/document.h 4 lines changed, 2 insertions(+), 2 deletions(-) =================================================================== @@ -93,8 +93,8 @@ typedef struct GeanyDocument /** The filetype for this document, it's only a reference to one of the elements of the global * filetypes array. */ GeanyFiletype *file_type; - /** TMWorkObject object for this document, or @c NULL. */ - TMWorkObject *tm_file; + /** TMSourceFile object for this document, or @c NULL. */ + TMSourceFile *tm_file; /** Whether this document is read-only. */ gboolean readonly; /** Whether this document has been changed since it was last saved. */
Modified: src/editor.c 32 lines changed, 16 insertions(+), 16 deletions(-) =================================================================== @@ -639,7 +639,7 @@ static void show_tags_list(GeanyEditor *editor, const GPtrArray *tags, gsize roo g_string_append(words, tag->name);
/* for now, tag types don't all follow C, so just look at arglist */ - if (!EMPTY(tag->atts.entry.arglist)) + if (!EMPTY(tag->arglist)) g_string_append(words, "?2"); else g_string_append(words, "?1"); @@ -732,10 +732,10 @@ static void autocomplete_scope(GeanyEditor *editor) return;
tag = g_ptr_array_index(tags, 0); - name = tag->atts.entry.var_type; + name = tag->var_type; if (name) { - TMWorkObject *obj = editor->document->tm_file; + TMSourceFile *obj = editor->document->tm_file;
tags = tm_workspace_find_scope_members(obj ? obj->tags_array : NULL, name, TRUE, FALSE); @@ -1784,43 +1784,43 @@ static gint find_start_bracket(ScintillaObject *sci, gint pos)
static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id) { - if (! tag->atts.entry.arglist) + if (! tag->arglist) return FALSE;
if (ft_id != GEANY_FILETYPES_PASCAL) { /* usual calltips: "retval tagname (arglist)" */ - if (tag->atts.entry.var_type) + if (tag->var_type) { guint i;
- g_string_append(str, tag->atts.entry.var_type); - for (i = 0; i < tag->atts.entry.pointerOrder; i++) + g_string_append(str, tag->var_type); + for (i = 0; i < tag->pointerOrder; i++) { g_string_append_c(str, '*'); } g_string_append_c(str, ' '); } - if (tag->atts.entry.scope) + if (tag->scope) { const gchar *cosep = symbols_get_context_separator(ft_id);
- g_string_append(str, tag->atts.entry.scope); + g_string_append(str, tag->scope); g_string_append(str, cosep); } g_string_append(str, tag->name); g_string_append_c(str, ' '); - g_string_append(str, tag->atts.entry.arglist); + g_string_append(str, tag->arglist); } else { /* special case Pascal calltips: "tagname (arglist) : retval" */ g_string_append(str, tag->name); g_string_append_c(str, ' '); - g_string_append(str, tag->atts.entry.arglist); + g_string_append(str, tag->arglist);
- if (!EMPTY(tag->atts.entry.var_type)) + if (!EMPTY(tag->var_type)) { g_string_append(str, " : "); - g_string_append(str, tag->atts.entry.var_type); + g_string_append(str, tag->var_type); } }
@@ -1831,7 +1831,7 @@ static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id static gchar *find_calltip(const gchar *word, GeanyFiletype *ft) { const GPtrArray *tags; - const gint arg_types = tm_tag_function_t | tm_tag_prototype_t | + const TMTagType arg_types = tm_tag_function_t | tm_tag_prototype_t | tm_tag_method_t | tm_tag_macro_with_arg_t; TMTagAttrType *attrs = NULL; TMTag *tag; @@ -1862,7 +1862,7 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft) { tag = TM_TAG(tags->pdata[i]);
- if (! tag->atts.entry.arglist) + if (! tag->arglist) tags->pdata[i] = NULL; } tm_tags_prune((GPtrArray *) tags); @@ -1873,7 +1873,7 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft) TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0};
- tm_tags_sort((GPtrArray *) tags, sort_attr, TRUE); + tm_tags_sort((GPtrArray *) tags, sort_attr, TRUE, FALSE); }
/* if the current word has changed since last time, start with the first tag match */
Modified: src/highlighting.c 19 lines changed, 1 insertions(+), 18 deletions(-) =================================================================== @@ -433,23 +433,6 @@ void highlighting_free_styles(void) }
-static GString *get_global_typenames(gint lang) -{ - GString *s = NULL; - - if (app->tm_workspace) - { - GPtrArray *tags_array = app->tm_workspace->global_tags; - - if (tags_array) - { - s = symbols_find_tags_as_string(tags_array, TM_GLOBAL_TYPE_MASK, lang); - } - } - return s; -} - - static gchar* get_keyfile_whitespace_chars(GKeyFile *config, GKeyFile *configh) { @@ -823,7 +806,7 @@ static void merge_type_keywords(ScintillaObject *sci, guint ft_id, guint keyword const gchar *user_words = style_sets[ft_id].keywords[keyword_idx]; GString *s;
- s = get_global_typenames(filetypes[ft_id]->lang); + s = symbols_find_typenames_as_string(filetypes[ft_id]->lang, TRUE); if (G_UNLIKELY(s == NULL)) s = g_string_sized_new(200); else
Modified: src/main.c 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -1289,7 +1289,7 @@ static void do_main_quit(void) filetypes_free_types(); log_finalize();
- tm_workspace_free(TM_WORK_OBJECT(app->tm_workspace)); + tm_workspace_free(); g_free(app->configdir); g_free(app->datadir); g_free(app->docdir);
Modified: src/plugindata.h 16 lines changed, 8 insertions(+), 8 deletions(-) =================================================================== @@ -58,7 +58,7 @@ G_BEGIN_DECLS * @warning You should not test for values below 200 as previously * @c GEANY_API_VERSION was defined as an enum value, not a macro. */ -#define GEANY_API_VERSION 220 +#define GEANY_API_VERSION 221
/* hack to have a different ABI when built with GTK3 because loading GTK2-linked plugins * with GTK3-linked Geany leads to crash */ @@ -72,7 +72,7 @@ G_BEGIN_DECLS * Changing this forces all plugins to be recompiled before Geany can load them. */ /* This should usually stay the same if fields are only appended, assuming only pointers to * structs and not structs themselves are declared by plugins. */ -#define GEANY_ABI_VERSION (69 << GEANY_ABI_SHIFT) +#define GEANY_ABI_VERSION (70 << GEANY_ABI_SHIFT)
/** Defines a function to check the plugin is safe to load. @@ -599,12 +599,12 @@ SearchFuncs; typedef struct TagManagerFuncs { gchar* (*tm_get_real_path) (const gchar *file_name); - TMWorkObject* (*tm_source_file_new) (const char *file_name, gboolean update, const char *name); - gboolean (*tm_workspace_add_object) (TMWorkObject *work_object); - gboolean (*tm_source_file_update) (TMWorkObject *source_file, gboolean force, - gboolean recurse, gboolean update_parent); - void (*tm_work_object_free) (gpointer work_object); - gboolean (*tm_workspace_remove_object) (TMWorkObject *w, gboolean do_free, gboolean update); + TMSourceFile* (*tm_source_file_new) (const char *file_name, const char *name); + void (*tm_source_file_free) (TMSourceFile *source_file); + void (*tm_workspace_add_source_file) (TMSourceFile *source_file); + void (*tm_workspace_remove_source_file) (TMSourceFile *source_file); + void (*tm_workspace_add_source_files) (GPtrArray *source_files); + void (*tm_workspace_remove_source_files) (GPtrArray *source_files); } TagManagerFuncs;
Modified: src/plugins.c 9 lines changed, 5 insertions(+), 4 deletions(-) =================================================================== @@ -290,10 +290,11 @@ static KeybindingFuncs keybindings_funcs = { static TagManagerFuncs tagmanager_funcs = { &tm_get_real_path, &tm_source_file_new, - &tm_workspace_add_object, - &tm_source_file_update, - &tm_work_object_free, - &tm_workspace_remove_object + &tm_source_file_free, + &tm_workspace_add_source_file, + &tm_workspace_remove_source_file, + &tm_workspace_add_source_files, + &tm_workspace_remove_source_files };
static SearchFuncs search_funcs = {
Modified: src/sidebar.c 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -904,7 +904,7 @@ static gboolean taglist_go_to_selection(GtkTreeSelection *selection, guint keyva if (! tag) return FALSE;
- line = tag->atts.entry.line; + line = tag->line; if (line > 0) { GeanyDocument *doc = document_get_current();
Modified: src/symbols.c 129 lines changed, 60 insertions(+), 69 deletions(-) =================================================================== @@ -64,11 +64,6 @@ #include <stdlib.h>
-const guint TM_GLOBAL_TYPE_MASK = - tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t | - tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t; - - static gchar **html_entities = NULL;
typedef struct @@ -248,7 +243,7 @@ static void html_tags_loaded(void) }
-GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gint lang) +GString *symbols_find_typenames_as_string(gint lang, gboolean global) { guint j; TMTag *tag; @@ -256,9 +251,10 @@ GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gin GPtrArray *typedefs; gint tag_lang;
- g_return_val_if_fail(tags_array != NULL, NULL); - - typedefs = tm_tags_extract(tags_array, tag_types); + if (global) + typedefs = tm_tags_extract(app->tm_workspace->global_tags, TM_GLOBAL_TYPE_MASK); + else + typedefs = app->tm_workspace->typename_array;
if ((typedefs) && (typedefs->len > 0)) { @@ -266,9 +262,7 @@ GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gin for (j = 0; j < typedefs->len; ++j) { tag = TM_TAG(typedefs->pdata[j]); - /* tag->atts.file.lang contains (for some reason) the line of the tag if - * tag->atts.entry.file is not NULL */ - tag_lang = (tag->atts.entry.file) ? tag->atts.entry.file->lang : tag->atts.file.lang; + tag_lang = tag->lang;
/* the check for tag_lang == lang is necessary to avoid wrong type colouring of * e.g. PHP classes in C++ files @@ -282,7 +276,7 @@ GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gin } } } - if (typedefs) + if (typedefs && global) g_ptr_array_free(typedefs, TRUE); return s; } @@ -333,25 +327,25 @@ GString *symbols_get_macro_list(gint lang) gint tag_lang; TMTag *tag;
- if (app->tm_workspace->work_objects == NULL) + if (app->tm_workspace->source_files == NULL) return NULL;
ftags = g_ptr_array_sized_new(50); words = g_string_sized_new(200);
- for (j = 0; j < app->tm_workspace->work_objects->len; j++) + for (j = 0; j < app->tm_workspace->source_files->len; j++) { GPtrArray *tags;
- tags = tm_tags_extract(TM_WORK_OBJECT(app->tm_workspace->work_objects->pdata[j])->tags_array, + tags = tm_tags_extract(TM_SOURCE_FILE(app->tm_workspace->source_files->pdata[j])->tags_array, tm_tag_enum_t | tm_tag_variable_t | tm_tag_macro_t | tm_tag_macro_with_arg_t); if (NULL != tags) { for (i = 0; ((i < tags->len) && (i < editor_prefs.autocompletion_max_entries)); ++i) { tag = TM_TAG(tags->pdata[i]); - tag_lang = (tag->atts.entry.file) ? - tag->atts.entry.file->lang : tag->atts.file.lang; + tag_lang = (tag->file) ? + tag->file->lang : tag->lang;
if (tag_lang == lang) g_ptr_array_add(ftags, (gpointer) tags->pdata[i]); @@ -367,7 +361,7 @@ GString *symbols_get_macro_list(gint lang) return NULL; }
- tm_tags_sort(ftags, NULL, FALSE); + tm_tags_sort(ftags, NULL, FALSE, FALSE); for (j = 0; j < ftags->len; j++) { if (j > 0) @@ -395,24 +389,21 @@ symbols_find_tm_tag(const GPtrArray *tags, const gchar *tag_name) }
-static TMTag *find_work_object_tag(const TMWorkObject *workobj, +static TMTag *find_source_file_tag(GPtrArray *tags_array, const gchar *tag_name, guint type) { GPtrArray *tags; TMTag *tmtag;
- if (G_LIKELY(workobj != NULL)) + tags = tm_tags_extract(tags_array, type); + if (tags != NULL) { - tags = tm_tags_extract(workobj->tags_array, type); - if (tags != NULL) - { - tmtag = symbols_find_tm_tag(tags, tag_name); + tmtag = symbols_find_tm_tag(tags, tag_name);
- g_ptr_array_free(tags, TRUE); + g_ptr_array_free(tags, TRUE);
- if (tmtag != NULL) - return tmtag; - } + if (tmtag != NULL) + return tmtag; } return NULL; /* not found */ } @@ -421,19 +412,19 @@ static TMTag *find_work_object_tag(const TMWorkObject *workobj, static TMTag *find_workspace_tag(const gchar *tag_name, guint type) { guint j; - const GPtrArray *work_objects = NULL; + const GPtrArray *source_files = NULL;
if (app->tm_workspace != NULL) - work_objects = app->tm_workspace->work_objects; + source_files = app->tm_workspace->source_files;
- if (work_objects != NULL) + if (source_files != NULL) { - for (j = 0; j < work_objects->len; j++) + for (j = 0; j < source_files->len; j++) { - TMWorkObject *workobj = TM_WORK_OBJECT(work_objects->pdata[j]); + TMSourceFile *srcfile = source_files->pdata[j]; TMTag *tmtag;
- tmtag = find_work_object_tag(workobj, tag_name, type); + tmtag = find_source_file_tag(srcfile->tags_array, tag_name, type); if (tmtag != NULL) return tmtag; } @@ -468,7 +459,7 @@ static gint compare_symbol(const TMTag *tag_a, const TMTag *tag_b) ret = strcmp(tag_a->name, tag_b->name); if (ret == 0) { - return tag_a->atts.entry.line - tag_b->atts.entry.line; + return tag_a->line - tag_b->line; } return ret; } @@ -484,21 +475,21 @@ static gint compare_symbol_lines(gconstpointer a, gconstpointer b) if (a == NULL || b == NULL) return 0;
- ret = tag_a->atts.entry.line - tag_b->atts.entry.line; + ret = tag_a->line - tag_b->line; if (ret == 0) { - if (tag_a->atts.entry.scope == NULL) - return -(tag_a->atts.entry.scope != tag_b->atts.entry.scope); - if (tag_b->atts.entry.scope == NULL) - return tag_a->atts.entry.scope != tag_b->atts.entry.scope; + if (tag_a->scope == NULL) + return -(tag_a->scope != tag_b->scope); + if (tag_b->scope == NULL) + return tag_a->scope != tag_b->scope; else - return strcmp(tag_a->atts.entry.scope, tag_b->atts.entry.scope); + return strcmp(tag_a->scope, tag_b->scope); } return ret; }
-static GList *get_tag_list(GeanyDocument *doc, guint tag_types) +static GList *get_tag_list(GeanyDocument *doc, TMTagType tag_types) { GList *tag_names = NULL; TMTag *tag; @@ -1037,7 +1028,7 @@ static void hide_empty_rows(GtkTreeStore *store) static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboolean found_parent) { gchar *utf8_name; - const gchar *scope = tag->atts.entry.scope; + const gchar *scope = tag->scope; static GString *buffer = NULL; /* buffer will be small so we can keep it for reuse */ gboolean doc_is_utf8 = FALSE;
@@ -1078,7 +1069,7 @@ static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboole if (! doc_is_utf8) g_free(utf8_name);
- g_string_append_printf(buffer, " [%lu]", tag->atts.entry.line); + g_string_append_printf(buffer, " [%lu]", tag->line);
return buffer->str; } @@ -1108,7 +1099,7 @@ static gchar *get_symbol_tooltip(GeanyDocument *doc, const TMTag *tag) /* find the last word in "foo::bar::blah", e.g. "blah" */ static const gchar *get_parent_name(const TMTag *tag, filetype_id ft_id) { - const gchar *scope = tag->atts.entry.scope; + const gchar *scope = tag->scope; const gchar *separator = symbols_get_context_separator(ft_id); const gchar *str, *ptr;
@@ -1226,9 +1217,9 @@ static gboolean tag_equal(gconstpointer v1, gconstpointer v2) const TMTag *t2 = v2;
return (t1->type == t2->type && strcmp(t1->name, t2->name) == 0 && - utils_str_equal(t1->atts.entry.scope, t2->atts.entry.scope) && + utils_str_equal(t1->scope, t2->scope) && /* include arglist in match to support e.g. C++ overloading */ - utils_str_equal(t1->atts.entry.arglist, t2->atts.entry.arglist)); + utils_str_equal(t1->arglist, t2->arglist)); }
@@ -1242,15 +1233,15 @@ static guint tag_hash(gconstpointer v) h = (h << 5) + h + tag->type; for (p = tag->name; *p != '\0'; p++) h = (h << 5) + h + *p; - if (tag->atts.entry.scope) + if (tag->scope) { - for (p = tag->atts.entry.scope; *p != '\0'; p++) + for (p = tag->scope; *p != '\0'; p++) h = (h << 5) + h + *p; } /* for e.g. C++ overloading */ - if (tag->atts.entry.arglist) + if (tag->arglist) { - for (p = tag->atts.entry.arglist; *p != '\0'; p++) + for (p = tag->arglist; *p != '\0'; p++) h = (h << 5) + h + *p; }
@@ -1348,7 +1339,7 @@ static GList *tags_table_lookup(GHashTable *table, TMTag *tag) glong delta; data = node->data;
-#define TAG_DELTA(a, b) ABS((glong) TM_TAG(a)->atts.entry.line - (glong) TM_TAG(b)->atts.entry.line) +#define TAG_DELTA(a, b) ABS((glong) TM_TAG(a)->line - (glong) TM_TAG(b)->line)
delta = TAG_DELTA(((GList *) node->data)->data, tag); for (node = node->next; node; node = node->next) @@ -1542,7 +1533,7 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags) gtk_tree_model_get(GTK_TREE_MODEL(store), node->data, SYMBOLS_COLUMN_TAG, &parent_tag, -1);
- d = tag->atts.entry.line - parent_tag->atts.entry.line; + d = tag->line - parent_tag->line; if (! parent_search || (d >= 0 && d < delta)) { delta = d; @@ -1616,7 +1607,7 @@ static gboolean tag_has_missing_parent(const TMTag *tag, GtkTreeStore *store, GtkTreeIter *iter) { /* if the tag has a parent tag, it should be at depth >= 2 */ - return !EMPTY(tag->atts.entry.scope) && + return !EMPTY(tag->scope) && gtk_tree_store_iter_depth(store, iter) == 1; }
@@ -1666,7 +1657,7 @@ static gint tree_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, if (tag_a && tag_b) if (!sort_by_name || (utils_str_equal(tag_a->name, tag_b->name) && - utils_str_equal(tag_a->atts.entry.scope, tag_b->atts.entry.scope))) + utils_str_equal(tag_a->scope, tag_b->scope))) cmp = compare_symbol_lines(tag_a, tag_b); } } @@ -1931,8 +1922,8 @@ static void load_user_tags(filetype_id ft_id)
static gboolean goto_tag(const gchar *name, gboolean definition) { - const gint forward_types = tm_tag_prototype_t | tm_tag_externvar_t; - guint type; + const TMTagType forward_types = tm_tag_prototype_t | tm_tag_externvar_t; + TMTagType type; TMTag *tmtag = NULL; GeanyDocument *old_doc = document_get_current();
@@ -1941,7 +1932,7 @@ static gboolean goto_tag(const gchar *name, gboolean definition)
/* first look in the current document */ if (old_doc != NULL && old_doc->tm_file) - tmtag = find_work_object_tag(old_doc->tm_file, name, type); + tmtag = find_source_file_tag(old_doc->tm_file->tags_array, name, type);
/* if not found, look in the workspace */ if (tmtag == NULL) @@ -1950,13 +1941,13 @@ static gboolean goto_tag(const gchar *name, gboolean definition) if (tmtag != NULL) { GeanyDocument *new_doc = document_find_by_real_path( - tmtag->atts.entry.file->work_object.file_name); + tmtag->file->file_name);
if (new_doc) { /* If we are already on the tag line, swap definition/declaration */ if (new_doc == old_doc && - tmtag->atts.entry.line == (guint)sci_get_current_line(old_doc->editor->sci) + 1) + tmtag->line == (guint)sci_get_current_line(old_doc->editor->sci) + 1) { if (goto_tag(name, !definition)) return TRUE; @@ -1965,10 +1956,10 @@ static gboolean goto_tag(const gchar *name, gboolean definition) else { /* not found in opened document, should open */ - new_doc = document_open_file(tmtag->atts.entry.file->work_object.file_name, FALSE, NULL, NULL); + new_doc = document_open_file(tmtag->file->file_name, FALSE, NULL, NULL); }
- if (navqueue_goto_line(old_doc, new_doc, tmtag->atts.entry.line)) + if (navqueue_goto_line(old_doc, new_doc, tmtag->line)) return TRUE; } return FALSE; @@ -2122,7 +2113,7 @@ static gint get_fold_header_after(ScintillaObject *sci, gint line) }
-static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_types) +static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, TMTagType tag_types) { gint line; gint parent; @@ -2137,7 +2128,7 @@ static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_
if (tag) { - gint tag_line = tag->atts.entry.line - 1; + gint tag_line = tag->line - 1; gint last_child = line + 1;
/* if it may be a false positive because we're inside a fold level not inside anything @@ -2152,8 +2143,8 @@ static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_
if (line <= last_child) { - if (tag->atts.entry.scope) - *tagname = g_strconcat(tag->atts.entry.scope, + if (tag->scope) + *tagname = g_strconcat(tag->scope, symbols_get_context_separator(doc->file_type->id), tag->name, NULL); else *tagname = g_strdup(tag->name); @@ -2199,7 +2190,7 @@ static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_ }
-static gint get_current_tag_name_cached(GeanyDocument *doc, const gchar **tagname, guint tag_types) +static gint get_current_tag_name_cached(GeanyDocument *doc, const gchar **tagname, TMTagType tag_types) { static gint tag_line = -1; static gchar *cur_tag = NULL; @@ -2245,7 +2236,7 @@ gint symbols_get_current_function(GeanyDocument *doc, const gchar **tagname) /* same as symbols_get_current_function() but finds class, namespaces and more */ gint symbols_get_current_scope(GeanyDocument *doc, const gchar **tagname) { - guint tag_types = (tm_tag_function_t | tm_tag_method_t | tm_tag_class_t | + TMTagType tag_types = (tm_tag_function_t | tm_tag_method_t | tm_tag_class_t | tm_tag_struct_t | tm_tag_enum_t | tm_tag_union_t);
/* Python parser reports imports as namespaces which confuses the scope detection */
Modified: src/symbols.h 4 lines changed, 1 insertions(+), 3 deletions(-) =================================================================== @@ -34,8 +34,6 @@ const gchar *symbols_get_context_separator(gint ft_id);
#ifdef GEANY_PRIVATE
-extern const guint TM_GLOBAL_TYPE_MASK; - enum { SYMBOLS_SORT_BY_NAME, @@ -52,7 +50,7 @@ void symbols_reload_config_files(void);
void symbols_global_tags_loaded(guint file_type_idx);
-GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gint lang); +GString *symbols_find_typenames_as_string(gint lang, gboolean global);
const GList *symbols_get_tag_list(GeanyDocument *doc, guint tag_types);
Modified: tagmanager/ctags/read.c 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -298,7 +298,7 @@ extern boolean fileOpen (const char *const fileName, const langType language) * This func is NOT THREAD SAFE. * The user should not tamper with the buffer while this func is executing. */ -extern boolean bufferOpen (unsigned char *buffer, int buffer_size, +extern boolean bufferOpen (unsigned char *buffer, size_t buffer_size, const char *const fileName, const langType language ) { boolean opened = FALSE;
Modified: tagmanager/ctags/read.h 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -106,7 +106,7 @@ extern void fileUngetc (int c); extern const unsigned char *fileReadLine (void); extern char *readLine (vString *const vLine, MIO *const mio); extern char *readSourceLine (vString *const vLine, MIOPos location, long *const pSeekValue); -extern boolean bufferOpen (unsigned char *buffer, int buffer_size, +extern boolean bufferOpen (unsigned char *buffer, size_t buffer_size, const char *const fileName, const langType language ); #define bufferClose fileClose
Modified: tagmanager/src/Makefile.am 5 lines changed, 0 insertions(+), 5 deletions(-) =================================================================== @@ -17,20 +17,15 @@ tagmanager_include_HEADERS = \ tm_source_file.h \ tm_tag.h \ tm_tagmanager.h \ - tm_work_object.h \ tm_workspace.h
libtagmanager_a_SOURCES =\ tm_tagmanager.h \ tm_parser.h \ - tm_file_entry.h \ - tm_file_entry.c \ tm_source_file.h \ tm_source_file.c \ tm_tag.h \ tm_tag.c \ - tm_work_object.c \ - tm_work_object.h \ tm_workspace.h \ tm_workspace.c
Modified: tagmanager/src/makefile.win32 3 lines changed, 1 insertions(+), 2 deletions(-) =================================================================== @@ -44,8 +44,7 @@ all: $(COMPLIB) clean: -$(RM) deps.mak *.o $(COMPLIB)
-$(COMPLIB): tm_workspace.o tm_work_object.o tm_source_file.o tm_tag.o \ -tm_file_entry.o +$(COMPLIB): tm_workspace.o tm_source_file.o tm_tag.o $(AR) rc $@ $^ $(RANLIB) $@
Modified: tagmanager/src/tm_file_entry.c 282 lines changed, 0 insertions(+), 282 deletions(-) =================================================================== @@ -1,282 +0,0 @@ -/* -* -* Copyright (c) 2001-2002, Biswapesh Chattopadhyay -* -* This source code is released for free distribution under the terms of the -* GNU General Public License. -* -*/ - -#include "general.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <dirent.h> -#ifdef HAVE_FCNTL_H -# include <fcntl.h> -#endif -#include <sys/types.h> -#include <sys/stat.h> -#ifdef HAVE_FNMATCH_H -# include <fnmatch.h> -#endif -#include <glib/gstdio.h> - -#include "tm_work_object.h" -#include "tm_file_entry.h" - - -#define FILE_NEW(T) ((T) = g_slice_new0(TMFileEntry)) -#define FILE_FREE(T) g_slice_free(TMFileEntry, (T)) - - -void tm_file_entry_print(TMFileEntry *entry, gpointer UNUSED user_data - , guint level) -{ - guint i; - - g_return_if_fail(entry); - for (i=0; i < level; ++i) - fputc('\t', stderr); - fprintf(stderr, "%s\n", entry->name); -} - -gint tm_file_entry_compare(TMFileEntry *e1, TMFileEntry *e2) -{ - g_return_val_if_fail(e1 && e2 && e1->name && e2->name, 0); -#ifdef TM_DEBUG - g_message("Comparing %s and %s", e1->name, e2->name); -#endif - return strcmp(e1->name, e2->name); -} - -/* TTimo - modified to handle symlinks */ -static TMFileType tm_file_entry_type(const char *path) -{ - struct stat s; - -#ifndef G_OS_WIN32 - if (0 != g_lstat(path, &s)) - return tm_file_unknown_t; -#endif - if (S_ISDIR(s.st_mode)) - return tm_file_dir_t; -#ifndef G_OS_WIN32 - else if (S_ISLNK(s.st_mode)) - return tm_file_link_t; -#endif - else if (S_ISREG(s.st_mode)) - return tm_file_regular_t; - else - return tm_file_unknown_t; -} - -static gboolean apply_filter(const char *name, GList *match, GList *unmatch - , gboolean ignore_hidden) -{ - GList *tmp; - gboolean matched = (match == NULL); - g_return_val_if_fail(name, FALSE); - if (ignore_hidden && ('.' == name[0])) - return FALSE; - /* TTimo - ignore .svn directories */ - if (!strcmp(name, ".svn")) - return FALSE; - for (tmp = match; tmp; tmp = g_list_next(tmp)) - { - if (0 == fnmatch((char *) tmp->data, name, 0)) - { - matched = TRUE; - break; - } - } - if (!matched) - return FALSE; - for (tmp = unmatch; tmp; tmp = g_list_next(tmp)) - { - if (0 == fnmatch((char *) tmp->data, name, 0)) - { - return FALSE; - } - } - return matched; -} - -TMFileEntry *tm_file_entry_new(const char *path, TMFileEntry *parent - , gboolean recurse, GList *file_match, GList *file_unmatch - , GList *dir_match, GList *dir_unmatch, gboolean ignore_hidden_files - , gboolean ignore_hidden_dirs) -{ - TMFileEntry *entry; - /* GList *tmp; */ - char *real_path; - DIR *dir; - struct dirent *dir_entry; - TMFileEntry *new_entry; - char *file_name; - struct stat s; - char *entries = NULL; - - g_return_val_if_fail (path != NULL, NULL); - - /* TTimo - don't follow symlinks */ - if (tm_file_entry_type(path) == tm_file_link_t) - return NULL; - real_path = tm_get_real_path(path); - if (!real_path) - return NULL; - FILE_NEW(entry); - entry->type = tm_file_entry_type(real_path); - entry->parent = parent; - entry->path = real_path; - entry->name = strrchr(entry->path, '/'); - if (entry->name) - ++ (entry->name); - else - entry->name = entry->path; - switch(entry->type) - { - case tm_file_unknown_t: - g_free(real_path); - FILE_FREE(entry); - return NULL; - case tm_file_link_t: - case tm_file_regular_t: - if (parent && !apply_filter(entry->name, file_match, file_unmatch - , ignore_hidden_files)) - { - tm_file_entry_free(entry); - return NULL; - } - break; - case tm_file_dir_t: - if (parent && !(recurse && apply_filter(entry->name, dir_match - , dir_unmatch, ignore_hidden_dirs))) - { - tm_file_entry_free(entry); - return NULL; - } - file_name = g_strdup_printf("%s/CVS/Entries", entry->path); - if (0 == g_stat(file_name, &s)) - { - if (S_ISREG(s.st_mode)) - { - int fd; - entries = g_new(char, s.st_size + 2); - if (0 > (fd = open(file_name, O_RDONLY))) - { - g_free(entries); - entries = NULL; - } - else - { - off_t n =0; - off_t total_read = 1; - while (0 < (n = read(fd, entries + total_read, s.st_size - total_read))) - total_read += n; - entries[s.st_size] = '\0'; - entries[0] = '\n'; - close(fd); - entry->version = g_strdup("D"); - } - } - } - g_free(file_name); - if (NULL != (dir = opendir(entry->path))) - { - while (NULL != (dir_entry = readdir(dir))) - { - if ((0 == strcmp(dir_entry->d_name, ".")) - || (0 == strcmp(dir_entry->d_name, ".."))) - continue; - file_name = g_strdup_printf("%s/%s", entry->path, dir_entry->d_name); - new_entry = tm_file_entry_new(file_name, entry, recurse - , file_match, file_unmatch, dir_match, dir_unmatch - , ignore_hidden_files, ignore_hidden_dirs); - g_free(file_name); - if (new_entry) - { - if (entries) - { - char *str = g_strconcat("\n/", new_entry->name, "/", NULL); - char *name_pos = strstr(entries, str); - if (NULL != name_pos) - { - int len = strlen(str); - char *version_pos = strchr(name_pos + len, '/'); - if (NULL != version_pos) - { - *version_pos = '\0'; - new_entry->version = g_strdup(name_pos + len); - *version_pos = '/'; - } - } - g_free(str); - } - entry->children = g_slist_prepend(entry->children, new_entry); - } - } - } - closedir(dir); - entry->children = g_slist_sort(entry->children, (GCompareFunc) tm_file_entry_compare); - g_free(entries); - break; - } - return entry; -} - -void tm_file_entry_free(gpointer entry) -{ - if (entry) - { - TMFileEntry *file_entry = TM_FILE_ENTRY(entry); - if (file_entry->children) - { - GSList *tmp; - for (tmp = file_entry->children; tmp; tmp = g_slist_next(tmp)) - tm_file_entry_free(tmp->data); - g_slist_free(file_entry->children); - } - g_free(file_entry->version); - g_free(file_entry->path); - FILE_FREE(file_entry); - } -} - -void tm_file_entry_foreach(TMFileEntry *entry, TMFileEntryFunc func - , gpointer user_data, guint level, gboolean reverse) -{ - g_return_if_fail (entry != NULL); - g_return_if_fail (func != NULL); - - if ((reverse) && (entry->children)) - { - GSList *tmp; - for (tmp = entry->children; tmp; tmp = g_slist_next(tmp)) - tm_file_entry_foreach(TM_FILE_ENTRY(tmp->data), func - , user_data, level + 1, TRUE); - } - func(entry, user_data, level); - if ((!reverse) && (entry->children)) - { - GSList *tmp; - for (tmp = entry->children; tmp; tmp = g_slist_next(tmp)) - tm_file_entry_foreach(TM_FILE_ENTRY(tmp->data), func - , user_data, level + 1, FALSE); - } -} - -GList *tm_file_entry_list(TMFileEntry *entry, GList *files) -{ - GSList *tmp; - files = g_list_prepend(files, g_strdup(entry->path)); - for (tmp = entry->children; tmp; tmp = g_slist_next(tmp)) - { - files = tm_file_entry_list((TMFileEntry *) tmp->data, files); - } - if (!files) - files = g_list_reverse(files); - return files; -}
Modified: tagmanager/src/tm_file_entry.h 124 lines changed, 0 insertions(+), 124 deletions(-) =================================================================== @@ -1,124 +0,0 @@ -/* -* -* Copyright (c) 2001-2002, Biswapesh Chattopadhyay -* -* This source code is released for free distribution under the terms of the -* GNU General Public License. -* -*/ - -#ifndef TM_FILE_ENTRY_H -#define TM_FILE_ENTRY_H - -#include <glib.h> - -/* \file -The TMFileEntry structure and associated functions can be used -for file and directory traversal. The following example demonstrates -the use of TMFileEntry. -\include tm_file_tree_dump.c -*/ - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* Enum defining file types */ -typedef enum -{ - tm_file_unknown_t, /* Unknown file type/file does not exist */ - tm_file_regular_t, /* Is a regular file */ - tm_file_dir_t, /* Is a directory */ - tm_file_link_t /* Is a symbolic link */ -} TMFileType; - -/* - This example demonstrates the use of TMFileEntry and associated functions - for managing file hierarchies in a project. - - \example tm_file_tree_dump.c -*/ - -/* This structure stores the file tree */ -typedef struct _TMFileEntry -{ - TMFileType type; /* File type */ - char *path; /* Full path to the file (incl. dir and name) */ - char *name; /* Just the file name (path minus the directory) */ - char *version; /* CVS version in case there is a CVS entry for this file */ - struct _TMFileEntry *parent; /* The parent directory file entry */ - GSList *children; /* List of children (for directory) */ -} TMFileEntry; - -/* Prototype for the function that gets called for each entry when - tm_file_entry_foreach() is called. -*/ -typedef void (*TMFileEntryFunc) (TMFileEntry *entry, gpointer user_data - , guint level); - -/* Convinience casting macro */ -#define TM_FILE_ENTRY(E) ((TMFileEntry *) (E)) - -/* Function that compares two file entries on name and returns the - difference -*/ -gint tm_file_entry_compare(TMFileEntry *e1, TMFileEntry *e2); - -/* Function to create a new file entry structure. -\param path Path to the file for which the entry is to be created. -\param parent Should be NULL for the first call. Since the function calls - itself recursively, this parameter is required to build the hierarchy. -\param recurse Whether the entry is to be recursively scanned (for - directories only) -\param file_match List of file name patterns to match. If set to NULL, - all files match. You can use wildcards like '*.c'. See the example program - for usage. -\param file_unmatch Opposite of file_match. All files matching any of the patterns - supplied are ignored. If set to NULL, no file is ignored. -\param dir_match List of directory name patterns to match. If set to NULL, - all directories match. You can use wildcards like '.*'. -\param dir_unmatch Opposite of dir_match. All directories matching any of the - patterns supplied are ignored. If set to NULL, no directory is ignored. -\param ignore_hidden_files If set to TRUE, hidden files (starting with '.') - are ignored. -\param ignore_hidden_dirs If set to TRUE, hidden directories (starting with '.') - are ignored. -\return Populated TMFileEntry structure on success, NULL on failure. -*/ -TMFileEntry *tm_file_entry_new(const char *path, TMFileEntry *parent - , gboolean recurse, GList *file_match, GList *file_unmatch - , GList *dir_match, GList *dir_unmatch, gboolean ignore_hidden_files - , gboolean ignore_hidden_dirs); - -/* Frees a TMFileEntry structure. Freeing is recursive, so all child - entries are freed as well. -\param entry The TMFileEntry structure to be freed. -*/ -void tm_file_entry_free(gpointer entry); - -/* This will call the function func() for each file entry. -\param entry The root file entry. -\param func The function to be called. -\param user_data Extra information to be passed to the function. -\param level The recursion level. You should set this to 0 initially. -\param reverse If set to TRUE, traversal is in reverse hierarchical order -*/ -void tm_file_entry_foreach(TMFileEntry *entry, TMFileEntryFunc func - , gpointer user_data, guint level, gboolean reverse); - -/* This is a sample function to show the use of tm_file_entry_foreach(). -*/ -void tm_file_entry_print(TMFileEntry *entry, gpointer user_data, guint level); - -/* Creates a list of path names from a TMFileEntry structure. -\param entry The TMFileEntry structure. -\files Current file list. Should be NULL. -*/ -GList *tm_file_entry_list(TMFileEntry *entry, GList *files); - -#ifdef __cplusplus -} -#endif - -#endif /* TM_FILE_ENTRY_H */
Modified: tagmanager/src/tm_source_file.c 449 lines changed, 256 insertions(+), 193 deletions(-) =================================================================== @@ -18,37 +18,159 @@ #include <limits.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <glib/gstdio.h> +#ifdef G_OS_WIN32 +# define VC_EXTRALEAN +# define WIN32_LEAN_AND_MEAN +# include <windows.h> /* for GetFullPathName */ +#endif
#include "general.h" #include "entry.h" #include "parse.h" #include "read.h" -#define LIBCTAGS_DEFINED -#include "tm_work_object.h"
+#define LIBCTAGS_DEFINED #include "tm_source_file.h" #include "tm_tag.h"
-guint source_file_class_id = 0; static TMSourceFile *current_source_file = NULL;
-gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name - , gboolean update, const char* name) +static int get_path_max(const char *path) +{ +#ifdef PATH_MAX + return PATH_MAX; +#else + int path_max = pathconf(path, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 4096; + return path_max; +#endif +} + + +#ifdef G_OS_WIN32 +/* realpath implementation for Windows found at http://bugzilla.gnome.org/show_bug.cgi?id=342926 + * this one is better than e.g. liberty's lrealpath because this one uses Win32 API and works + * with special chars within the filename */ +static char *realpath (const char *pathname, char *resolved_path) +{ + int size; + + if (resolved_path != NULL) + { + int path_max = get_path_max(pathname); + size = GetFullPathNameA (pathname, path_max, resolved_path, NULL); + if (size > path_max) + return NULL; + else + return resolved_path; + } + else + { + size = GetFullPathNameA (pathname, 0, NULL, NULL); + resolved_path = g_new0 (char, size); + GetFullPathNameA (pathname, size, resolved_path, NULL); + return resolved_path; + } +} +#endif + +/** + Given a file name, returns a newly allocated string containing the realpath() + of the file. + @param file_name The original file_name + @return A newly allocated string containing the real path to the file. NULL if none is available. +*/ +gchar *tm_get_real_path(const gchar *file_name) +{ + if (file_name) + { + gsize len = get_path_max(file_name) + 1; + gchar *path = g_malloc0(len); + + if (realpath(file_name, path)) + return path; + else + g_free(path); + } + return NULL; +} + +/* + This function is registered into the ctags parser when a file is parsed for + the first time. The function is then called by the ctags parser each time + it finds a new tag. You should not have to use this function. + @see tm_source_file_parse() +*/ +static int tm_source_file_tags(const tagEntryInfo *tag) +{ + if (NULL == current_source_file) + return 0; + g_ptr_array_add(current_source_file->tags_array, + tm_tag_new(current_source_file, tag)); + return TRUE; +} + +/* Set the argument list of tag identified by its name */ +static void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist) +{ + guint count; + TMTag **tags, *tag; + + if (NULL == arglist || + NULL == tag_name || + NULL == current_source_file) + { + return; + } + + tags = tm_tags_find(current_source_file->tags_array, tag_name, FALSE, FALSE, + &count); + if (tags != NULL && count == 1) + { + tag = tags[0]; + g_free(tag->arglist); + tag->arglist = g_strdup(arglist); + } +} + +/* Initializes a TMSourceFile structure from a file name. */ +static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name, + const char* name) { - if (0 == source_file_class_id) - source_file_class_id = tm_work_object_register(tm_source_file_free - , tm_source_file_update, NULL); + struct stat s; + int status;
#ifdef TM_DEBUG g_message("Source File init: %s", file_name); #endif
- if (FALSE == tm_work_object_init(&(source_file->work_object), - source_file_class_id, file_name, FALSE)) - return FALSE; + if (file_name != NULL) + { + status = g_stat(file_name, &s); + if (0 != status) + { + /* g_warning("Unable to stat %s", file_name);*/ + return FALSE; + } + if (!S_ISREG(s.st_mode)) + { + g_warning("%s: Not a regular file", file_name); + return FALSE; + } + source_file->file_name = tm_get_real_path(file_name); + source_file->short_name = strrchr(source_file->file_name, '/'); + if (source_file->short_name) + ++ source_file->short_name; + else + source_file->short_name = source_file->file_name; + } + + source_file->tags_array = g_ptr_array_new();
- source_file->inactive = FALSE; if (NULL == LanguageTable) { initializeParsing(); @@ -64,37 +186,46 @@ gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name else source_file->lang = getNamedLanguage(name);
- if (update) - tm_source_file_update(TM_WORK_OBJECT(source_file), TRUE, FALSE, FALSE); return TRUE; }
-TMWorkObject *tm_source_file_new(const char *file_name, gboolean update, const char *name) +/** Initializes a TMSourceFile structure and returns a pointer to it. The + * TMSourceFile has to be added to TMWorkspace to start its parsing. + * @param file_name The file name. + * @param name Name of the used programming language, NULL for autodetection. + * @return The created unparsed TMSourceFile object. + * */ +TMSourceFile *tm_source_file_new(const char *file_name, const char *name) { TMSourceFile *source_file = g_new(TMSourceFile, 1); - if (TRUE != tm_source_file_init(source_file, file_name, update, name)) + if (TRUE != tm_source_file_init(source_file, file_name, name)) { g_free(source_file); return NULL; } - return (TMWorkObject *) source_file; + return source_file; }
-void tm_source_file_destroy(TMSourceFile *source_file) +/* Destroys the contents of the source file. Note that the tags are owned by the + source file and are also destroyed when the source file is destroyed. If pointers + to these tags are used elsewhere, then those tag arrays should be rebuilt. +*/ +static void tm_source_file_destroy(TMSourceFile *source_file) { #ifdef TM_DEBUG - g_message("Destroying source file: %s", source_file->work_object.file_name); + g_message("Destroying source file: %s", source_file->file_name); #endif
- if (NULL != TM_WORK_OBJECT(source_file)->tags_array) - { - tm_tags_array_free(TM_WORK_OBJECT(source_file)->tags_array, TRUE); - TM_WORK_OBJECT(source_file)->tags_array = NULL; - } - tm_work_object_destroy(&(source_file->work_object)); + g_free(source_file->file_name); + tm_tags_array_free(source_file->tags_array, TRUE); + source_file->tags_array = NULL; }
-void tm_source_file_free(gpointer source_file) +/** Frees a TMSourceFile structure, including all contents. Before calling this + function the TMSourceFile has to be removed from the TMWorkspace. + @param source_file The source file to free. +*/ +void tm_source_file_free(TMSourceFile *source_file) { if (NULL != source_file) { @@ -103,79 +234,63 @@ void tm_source_file_free(gpointer source_file) } }
-gboolean tm_source_file_parse(TMSourceFile *source_file) +/* Parses the text-buffer or source file and regenarates the tags. + @param source_file The source file to parse + @param text_buf The text buffer to parse + @param buf_size The size of text_buf. + @param use_buffer Set FALSE to ignore the buffer and parse the file directly or + TRUE to parse the buffer and ignore the file content. + @return TRUE on success, FALSE on failure +*/ +gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size, + gboolean use_buffer) { const char *file_name; - gboolean status = TRUE; - int passCount = 0; + gboolean retry = TRUE; + gboolean parse_file = FALSE; + gboolean free_buf = FALSE;
- if ((NULL == source_file) || (NULL == source_file->work_object.file_name)) + if ((NULL == source_file) || (NULL == source_file->file_name)) { g_warning("Attempt to parse NULL file"); return FALSE; } - - file_name = source_file->work_object.file_name; - if (NULL == LanguageTable) + + if (source_file->lang == LANG_IGNORE) { - initializeParsing(); - installLanguageMapDefaults(); - if (NULL == TagEntryFunction) - TagEntryFunction = tm_source_file_tags; - if (NULL == TagEntrySetArglistFunction) - TagEntrySetArglistFunction = tm_source_file_set_tag_arglist; + tm_tags_array_free(source_file->tags_array, FALSE); + return FALSE; } - current_source_file = source_file; - - if (LANG_AUTO == source_file->lang) - source_file->lang = getFileLanguage (file_name); - - if (source_file->lang < 0 || ! LanguageTable [source_file->lang]->enabled) - return status; - - while ((TRUE == status) && (passCount < 3)) + + file_name = source_file->file_name; + + if (!use_buffer) { - if (source_file->work_object.tags_array) - tm_tags_array_free(source_file->work_object.tags_array, FALSE); - if (fileOpen (file_name, source_file->lang)) + struct stat s; + + /* load file to memory and parse it from memory unless the file is too big */ + if (g_stat(file_name, &s) != 0 || s.st_size > 10*1024*1024) + parse_file = TRUE; + else { - if (LanguageTable [source_file->lang]->parser != NULL) + if (!g_file_get_contents(file_name, (gchar**)&text_buf, (gsize*)&buf_size, NULL)) { - LanguageTable [source_file->lang]->parser (); - fileClose (); - break; + g_warning("Unable to open %s", file_name); + return FALSE; } - else if (LanguageTable [source_file->lang]->parser2 != NULL) - status = LanguageTable [source_file->lang]->parser2 (passCount); - fileClose (); + free_buf = TRUE; } - else - { - g_warning("%s: Unable to open %s", G_STRFUNC, file_name); - return FALSE; - } - ++ passCount; - } - return status; -} - -gboolean tm_source_file_buffer_parse(TMSourceFile *source_file, guchar* text_buf, gint buf_size) -{ - const char *file_name; - gboolean status = TRUE; - - if ((NULL == source_file) || (NULL == source_file->work_object.file_name)) - { - g_warning("Attempt to parse NULL file"); - return FALSE; }
- if ((NULL == text_buf) || (0 == buf_size)) + if (!parse_file && (NULL == text_buf || 0 == buf_size)) { - g_warning("Attempt to parse a NULL text buffer"); + /* Empty buffer, "parse" by setting empty tag array */ + tm_tags_array_free(source_file->tags_array, FALSE); + if (free_buf) + g_free(text_buf); + return TRUE; }
- file_name = source_file->work_object.file_name; if (NULL == LanguageTable) { initializeParsing(); @@ -202,21 +317,34 @@ gboolean tm_source_file_buffer_parse(TMSourceFile *source_file, guchar* text_buf } else { - int passCount = 0; - while ((TRUE == status) && (passCount < 3)) + guint passCount = 0; + while (retry && passCount < 3) { - if (source_file->work_object.tags_array) - tm_tags_array_free(source_file->work_object.tags_array, FALSE); - if (bufferOpen (text_buf, buf_size, file_name, source_file->lang)) + tm_tags_array_free(source_file->tags_array, FALSE); + if (parse_file && fileOpen (file_name, source_file->lang)) + { + if (LanguageTable [source_file->lang]->parser != NULL) + { + LanguageTable [source_file->lang]->parser (); + fileClose (); + retry = FALSE; + break; + } + else if (LanguageTable [source_file->lang]->parser2 != NULL) + retry = LanguageTable [source_file->lang]->parser2 (passCount); + fileClose (); + } + else if (!parse_file && bufferOpen (text_buf, buf_size, file_name, source_file->lang)) { if (LanguageTable [source_file->lang]->parser != NULL) { LanguageTable [source_file->lang]->parser (); bufferClose (); + retry = FALSE; break; } else if (LanguageTable [source_file->lang]->parser2 != NULL) - status = LanguageTable [source_file->lang]->parser2 (passCount); + retry = LanguageTable [source_file->lang]->parser2 (passCount); bufferClose (); } else @@ -226,103 +354,66 @@ gboolean tm_source_file_buffer_parse(TMSourceFile *source_file, guchar* text_buf } ++ passCount; } - return TRUE; - } - return status; -} - -void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist) -{ - int count; - TMTag **tags, *tag; - - if (NULL == arglist || - NULL == tag_name || - NULL == current_source_file || - NULL == current_source_file->work_object.tags_array) - { - return; - } - - tags = tm_tags_find(current_source_file->work_object.tags_array, tag_name, FALSE, FALSE, - &count); - if (tags != NULL && count == 1) - { - tag = tags[0]; - g_free(tag->atts.entry.arglist); - tag->atts.entry.arglist = g_strdup(arglist); } + + if (free_buf) + g_free(text_buf); + return !retry; }
-int tm_source_file_tags(const tagEntryInfo *tag) -{ - if (NULL == current_source_file) - return 0; - if (NULL == current_source_file->work_object.tags_array) - current_source_file->work_object.tags_array = g_ptr_array_new(); - g_ptr_array_add(current_source_file->work_object.tags_array, - tm_tag_new(current_source_file, tag)); - return TRUE; -} - -gboolean tm_source_file_update(TMWorkObject *source_file, gboolean force - , gboolean UNUSED recurse, gboolean update_parent) +/* Gets the name associated with the language index. + @param lang The language index. + @return The language name, or NULL. +*/ +const gchar *tm_source_file_get_lang_name(gint lang) { - if (force) + if (NULL == LanguageTable) { - tm_source_file_parse(TM_SOURCE_FILE(source_file)); - tm_tags_sort(source_file->tags_array, NULL, FALSE); - /* source_file->analyze_time = tm_get_file_timestamp(source_file->file_name); */ - if ((source_file->parent) && update_parent) - { - tm_work_object_update(source_file->parent, TRUE, FALSE, TRUE); - } - return TRUE; - } - else { -#ifdef TM_DEBUG - g_message ("no parsing of %s has been done", source_file->file_name); -#endif - return FALSE; + initializeParsing(); + installLanguageMapDefaults(); + if (NULL == TagEntryFunction) + TagEntryFunction = tm_source_file_tags; + if (NULL == TagEntrySetArglistFunction) + TagEntrySetArglistFunction = tm_source_file_set_tag_arglist; } + return getLanguageName(lang); }
- -gboolean tm_source_file_buffer_update(TMWorkObject *source_file, guchar* text_buf, - gint buf_size, gboolean update_parent) +/* Gets the language index for \a name. + @param name The language name. + @return The language index, or -2. +*/ +gint tm_source_file_get_named_lang(const gchar *name) { -#ifdef TM_DEBUG - g_message("Buffer updating based on source file %s", source_file->file_name); -#endif - - tm_source_file_buffer_parse (TM_SOURCE_FILE(source_file), text_buf, buf_size); - tm_tags_sort(source_file->tags_array, NULL, FALSE); - /* source_file->analyze_time = time(NULL); */ - if ((source_file->parent) && update_parent) + if (NULL == LanguageTable) { -#ifdef TM_DEBUG - g_message("Updating parent from buffer.."); -#endif - tm_work_object_update(source_file->parent, TRUE, FALSE, TRUE); + initializeParsing(); + installLanguageMapDefaults(); + if (NULL == TagEntryFunction) + TagEntryFunction = tm_source_file_tags; + if (NULL == TagEntrySetArglistFunction) + TagEntrySetArglistFunction = tm_source_file_set_tag_arglist; } -#ifdef TM_DEBUG - else - g_message("Skipping parent update because parent is %s and update_parent is %s" - , source_file->parent?"NOT NULL":"NULL", update_parent?"TRUE":"FALSE"); - -#endif - return TRUE; + return getNamedLanguage(name); }
- -gboolean tm_source_file_write(TMWorkObject *source_file, FILE *fp, guint attrs) +#if 0 +/* + Writes all tags of a source file (including the file tag itself) to the passed + file pointer. + @param source_file The source file to write. + @param fp The file pointer to write to. + @param attrs The attributes to write. + @return TRUE on success, FALSE on failure. +*/ +static gboolean tm_source_file_write(TMSourceFile *source_file, FILE *fp, guint attrs) { TMTag *tag; guint i;
if (NULL != source_file) { - if (NULL != (tag = tm_tag_new(TM_SOURCE_FILE(source_file), NULL))) + if (NULL != (tag = tm_tag_new(source_file, NULL))) { tm_tag_write(tag, fp, tm_tag_attr_max_t); tm_tag_unref(tag); @@ -339,32 +430,4 @@ gboolean tm_source_file_write(TMWorkObject *source_file, FILE *fp, guint attrs) } return TRUE; } - -const gchar *tm_source_file_get_lang_name(gint lang) -{ - if (NULL == LanguageTable) - { - initializeParsing(); - installLanguageMapDefaults(); - if (NULL == TagEntryFunction) - TagEntryFunction = tm_source_file_tags; - if (NULL == TagEntrySetArglistFunction) - TagEntrySetArglistFunction = tm_source_file_set_tag_arglist; - } - return getLanguageName(lang); -} - -gint tm_source_file_get_named_lang(const gchar *name) -{ - if (NULL == LanguageTable) - { - initializeParsing(); - installLanguageMapDefaults(); - if (NULL == TagEntryFunction) - TagEntryFunction = tm_source_file_tags; - if (NULL == TagEntrySetArglistFunction) - TagEntrySetArglistFunction = tm_source_file_set_tag_arglist; - } - return getNamedLanguage(name); -} - +#endif
Modified: tagmanager/src/tm_source_file.h 136 lines changed, 18 insertions(+), 118 deletions(-) =================================================================== @@ -10,8 +10,8 @@ #ifndef TM_SOURCE_FILE_H #define TM_SOURCE_FILE_H
-#include "tm_work_object.h" - +#include <stdio.h> +#include <glib.h>
#ifndef LIBCTAGS_DEFINED typedef int langType; @@ -27,138 +27,38 @@ extern "C" #endif
/* Casts a pointer to a pointer to a TMSourceFile structure */ -#define TM_SOURCE_FILE(work_object) ((TMSourceFile *) work_object) +#define TM_SOURCE_FILE(source_file) ((TMSourceFile *) source_file) + +/* Evaluates to X is X is defined, else evaluates to Y */ +#define FALLBACK(X,Y) (X)?(X):(Y)
-/* Checks whether the object is a TMSourceFile */ -#define IS_TM_SOURCE_FILE(source_file) (((TMWorkObject *) (source_file))->type \ - == source_file_class_id)
-/*! - The TMSourceFile structure is derived from TMWorkObject and contains all it's - attributes, plus an integer representing the language of the file. +/** + The TMSourceFile structure represents the source file and its tags in the tag manager. */ typedef struct { - TMWorkObject work_object; /*!< The base work object */ - langType lang; /*!< Programming language used */ - gboolean inactive; /*!< Whether this file should be scanned for tags */ + langType lang; /**< Programming language used */ + char *file_name; /**< Full file name (inc. path) */ + char *short_name; /**< Just the name of the file (without the path) */ + GPtrArray *tags_array; /**< Sorted tag array obtained by parsing the object */ } TMSourceFile;
-/*! Initializes a TMSourceFile structure and returns a pointer to it. - * \param file_name The file name. - * \param update Update the tag array of the file. - * \param name Name of the used programming language, NULL for autodetection. - * \return The created TMSourceFile object. - * */ -TMWorkObject *tm_source_file_new(const char *file_name, gboolean update, const char *name); - -/*! Updates the source file by reparsing if the modification time is greater - than the timestamp in the structure, or if force is TRUE. The tags array and - the tags themselves are destroyed and re-created, hence any other tag arrays - pointing to these tags should be rebuilt as well. All sorting information is - also lost. The language parameter is automatically set the first time the file - is parsed. - \param source_file The source file to update. - \param force Ignored. The source file is always updated. - \param recurse This parameter is ignored for source files and is only there for consistency. - \param update_parent If set to TRUE, sends an update signal to parent if required. You should - always set this to TRUE if you are calling this function directly. - \return TRUE if the file was parsed, FALSE otherwise. - \sa tm_work_object_update(), tm_workspace_update() -*/ -gboolean tm_source_file_update(TMWorkObject *source_file, gboolean force - , gboolean recurse, gboolean update_parent); - - -#ifdef GEANY_PRIVATE - -/* Initializes a TMSourceFile structure from a file name. */ -gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name, - gboolean update, const char *name); - -/* Destroys the contents of the source file. Note that the tags are owned by the - source file and are also destroyed when the source file is destroyed. If pointers - to these tags are used elsewhere, then those tag arrays should be rebuilt. -*/ -void tm_source_file_destroy(TMSourceFile *source_file); - -/* Frees a TMSourceFile structure, including all contents */ -void tm_source_file_free(gpointer source_file); - -/* Updates the source file by reparsing the text-buffer passed as parameter. - Ctags will use a parsing based on buffer instead of on files. - You should call this function when you don't want a previous saving of the file - you're editing. It's useful for a "real-time" updating of the tags. - The tags array and the tags themselves are destroyed and re-created, hence any - other tag arrays pointing to these tags should be rebuilt as well. All sorting - information is also lost. The language parameter is automatically set the first - time the file is parsed. - \param source_file The source file to update with a buffer. - \param text_buf A text buffer. The user should take care of allocate and free it after - the use here. - \param buf_size The size of text_buf. - \param update_parent If set to TRUE, sends an update signal to parent if required. You should - always set this to TRUE if you are calling this function directly. - \return TRUE if the file was parsed, FALSE otherwise. - \sa tm_work_object_update(), tm_workspace_update() -*/ -gboolean tm_source_file_buffer_update(TMWorkObject *source_file, guchar* text_buf, - gint buf_size, gboolean update_parent); +TMSourceFile *tm_source_file_new(const char *file_name, const char *name);
-/* Parses the source file and regenarates the tags. - \param source_file The source file to parse - \return TRUE on success, FALSE on failure - \sa tm_source_file_update() -*/ -gboolean tm_source_file_parse(TMSourceFile *source_file); - -/* Parses the text-buffer and regenarates the tags. - \param source_file The source file to parse - \param text_buf The text buffer to parse - \param buf_size The size of text_buf. - \return TRUE on success, FALSE on failure - \sa tm_source_file_update() -*/ -gboolean tm_source_file_buffer_parse(TMSourceFile *source_file, guchar* text_buf, gint buf_size); +void tm_source_file_free(TMSourceFile *source_file);
-/* - This function is registered into the ctags parser when a file is parsed for - the first time. The function is then called by the ctags parser each time - it finds a new tag. You should not have to use this function. - \sa tm_source_file_parse() -*/ -int tm_source_file_tags(const tagEntryInfo *tag); +gchar *tm_get_real_path(const gchar *file_name);
-/* - Writes all tags of a source file (including the file tag itself) to the passed - file pointer. - \param source_file The source file to write. - \param fp The file pointer to write to. - \param attrs The attributes to write. - \return TRUE on success, FALSE on failure. -*/ -gboolean tm_source_file_write(TMWorkObject *source_file, FILE *fp, guint attrs);
-/* Contains the id obtained by registering the TMSourceFile class as a child of - TMWorkObject. - \sa tm_work_object_register() -*/ -extern guint source_file_class_id; +#ifdef GEANY_PRIVATE
-/* Gets the name associated with the language index. - \param lang The language index. - \return The language name, or NULL. -*/ const gchar *tm_source_file_get_lang_name(gint lang);
-/* Gets the language index for \a name. - \param name The language name. - \return The language index, or -2. -*/ gint tm_source_file_get_named_lang(const gchar *name);
-/* Set the argument list of tag identified by its name */ -void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist); +gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size, + gboolean use_buffer);
#endif /* GEANY_PRIVATE */
Modified: tagmanager/src/tm_tag.c 804 lines changed, 517 insertions(+), 287 deletions(-) =================================================================== @@ -78,6 +78,11 @@ static void log_tag_free(TMTag *tag) #endif /* DEBUG_TAG_REFS */
+const TMTagType TM_GLOBAL_TYPE_MASK = + tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t | + tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t; + + /* Note: To preserve binary compatibility, it is very important that you only *append* to this list ! */ enum @@ -95,12 +100,15 @@ enum TA_ACCESS, TA_IMPL, TA_LANG, - TA_INACTIVE, + TA_INACTIVE, /* Obsolete */ TA_POINTER };
-static guint *s_sort_attrs = NULL; -static gboolean s_partial = FALSE; +typedef struct +{ + guint *sort_attrs; + gboolean partial; +} TMSortOptions;
static const char *s_tag_type_names[] = { "class", /* classes */ @@ -123,7 +131,7 @@ static const char *s_tag_type_names[] = { "other" /* Other tag type (non C/C++/Java) */ };
-static int s_tag_types[] = { +static TMTagType s_tag_types[] = { tm_tag_class_t, tm_tag_enum_t, tm_tag_enumerator_t, @@ -144,6 +152,7 @@ static int s_tag_types[] = { tm_tag_other_t };
+/* Gets the GType for a TMTag */ GType tm_tag_get_type(void) { static GType gtype = 0; @@ -155,7 +164,7 @@ GType tm_tag_get_type(void) return gtype; }
-static int get_tag_type(const char *tag_name) +static TMTagType get_tag_type(const char *tag_name) { unsigned int i; int cmp; @@ -208,56 +217,59 @@ static char get_tag_access(const char *access) return TAG_ACCESS_UNKNOWN; }
-gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry) +/* + Initializes a TMTag structure with information from a tagEntryInfo struct + used by the ctags parsers. Note that the TMTag structure must be malloc()ed + before calling this function. This function is called by tm_tag_new() - you + should not need to call this directly. + @param tag The TMTag structure to initialize + @param file Pointer to a TMSourceFile struct (it is assigned to the file member) + @param tag_entry Tag information gathered by the ctags parser + @return TRUE on success, FALSE on failure +*/ +static gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry) { tag->refcount = 1; if (NULL == tag_entry) - { - /* This is a file tag */ - if (NULL == file) - return FALSE; - else - { - tag->name = g_strdup(file->work_object.file_name); - tag->type = tm_tag_file_t; - /* tag->atts.file.timestamp = file->work_object.analyze_time; */ - tag->atts.file.lang = file->lang; - tag->atts.file.inactive = FALSE; - return TRUE; - } - } - else - { - /* This is a normal tag entry */ - if (NULL == tag_entry->name) - return FALSE; - tag->name = g_strdup(tag_entry->name); - tag->type = get_tag_type(tag_entry->kindName); - tag->atts.entry.local = tag_entry->isFileScope; - tag->atts.entry.pointerOrder = 0; /* backward compatibility (use var_type instead) */ - tag->atts.entry.line = tag_entry->lineNumber; - if (NULL != tag_entry->extensionFields.arglist) - tag->atts.entry.arglist = g_strdup(tag_entry->extensionFields.arglist); - if ((NULL != tag_entry->extensionFields.scope[1]) && - (isalpha(tag_entry->extensionFields.scope[1][0]) || - tag_entry->extensionFields.scope[1][0] == '_' || - tag_entry->extensionFields.scope[1][0] == '$')) - tag->atts.entry.scope = g_strdup(tag_entry->extensionFields.scope[1]); - if (tag_entry->extensionFields.inheritance != NULL) - tag->atts.entry.inheritance = g_strdup(tag_entry->extensionFields.inheritance); - if (tag_entry->extensionFields.varType != NULL) - tag->atts.entry.var_type = g_strdup(tag_entry->extensionFields.varType); - if (tag_entry->extensionFields.access != NULL) - tag->atts.entry.access = get_tag_access(tag_entry->extensionFields.access); - if (tag_entry->extensionFields.implementation != NULL) - tag->atts.entry.impl = get_tag_impl(tag_entry->extensionFields.implementation); - if ((tm_tag_macro_t == tag->type) && (NULL != tag->atts.entry.arglist)) - tag->type = tm_tag_macro_with_arg_t; - tag->atts.entry.file = file; - return TRUE; - } + return FALSE; + + /* This is a normal tag entry */ + if (NULL == tag_entry->name) + return FALSE; + tag->name = g_strdup(tag_entry->name); + tag->type = get_tag_type(tag_entry->kindName); + tag->local = tag_entry->isFileScope; + tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */ + tag->line = tag_entry->lineNumber; + if (NULL != tag_entry->extensionFields.arglist) + tag->arglist = g_strdup(tag_entry->extensionFields.arglist); + if ((NULL != tag_entry->extensionFields.scope[1]) && + (isalpha(tag_entry->extensionFields.scope[1][0]) || + tag_entry->extensionFields.scope[1][0] == '_' || + tag_entry->extensionFields.scope[1][0] == '$')) + tag->scope = g_strdup(tag_entry->extensionFields.scope[1]); + if (tag_entry->extensionFields.inheritance != NULL) + tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance); + if (tag_entry->extensionFields.varType != NULL) + tag->var_type = g_strdup(tag_entry->extensionFields.varType); + if (tag_entry->extensionFields.access != NULL) + tag->access = get_tag_access(tag_entry->extensionFields.access); + if (tag_entry->extensionFields.implementation != NULL) + tag->impl = get_tag_impl(tag_entry->extensionFields.implementation); + if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist)) + tag->type = tm_tag_macro_with_arg_t; + tag->file = file; + tag->lang = file->lang; + return TRUE; }
+/* + Creates a new tag structure from a tagEntryInfo pointer and a TMSOurceFile pointer + and returns a pointer to it. + @param file - Pointer to the TMSourceFile structure containing the tag + @param tag_entry Contains tag information generated by ctags + @return the new TMTag structure. This should be free()-ed using tm_tag_free() +*/ TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry) { TMTag *tag; @@ -271,7 +283,15 @@ TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry) return tag; }
-gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp) +/* + Initializes an already malloc()ed TMTag structure by reading a tag entry + line from a file. The structure should be allocated beforehand. + @param tag The TMTag structure to populate + @param file The TMSourceFile struct (assigned to the file member) + @param fp FILE pointer from where the tag line is read + @return TRUE on success, FALSE on FAILURE +*/ +static gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp) { guchar buf[BUFSIZ]; guchar *start, *end; @@ -301,61 +321,40 @@ gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp) switch (*start) { case TA_LINE: - tag->atts.entry.line = atol((gchar*)start + 1); + tag->line = atol((gchar*)start + 1); break; case TA_LOCAL: - tag->atts.entry.local = atoi((gchar*)start + 1); + tag->local = atoi((gchar*)start + 1); break; case TA_TYPE: tag->type = (TMTagType) atoi((gchar*)start + 1); break; case TA_ARGLIST: - tag->atts.entry.arglist = g_strdup((gchar*)start + 1); + tag->arglist = g_strdup((gchar*)start + 1); break; case TA_SCOPE: - tag->atts.entry.scope = g_strdup((gchar*)start + 1); + tag->scope = g_strdup((gchar*)start + 1); break; case TA_POINTER: - tag->atts.entry.pointerOrder = atoi((gchar*)start + 1); + tag->pointerOrder = atoi((gchar*)start + 1); break; case TA_VARTYPE: - tag->atts.entry.var_type = g_strdup((gchar*)start + 1); + tag->var_type = g_strdup((gchar*)start + 1); break; case TA_INHERITS: - tag->atts.entry.inheritance = g_strdup((gchar*)start + 1); + tag->inheritance = g_strdup((gchar*)start + 1); break; - case TA_TIME: - if (tm_tag_file_t != tag->type) - { - g_warning("Got time attribute for non-file tag %s", tag->name); - return FALSE; - } - else - tag->atts.file.timestamp = atol((gchar*)start + 1); + case TA_TIME: /* Obsolete */ break; - case TA_LANG: - if (tm_tag_file_t != tag->type) - { - g_warning("Got lang attribute for non-file tag %s", tag->name); - return FALSE; - } - else - tag->atts.file.lang = atoi((gchar*)start + 1); + case TA_LANG: /* Obsolete */ break; - case TA_INACTIVE: - if (tm_tag_file_t != tag->type) - { - g_warning("Got inactive attribute for non-file tag %s", tag->name); - return FALSE; - } - else - tag->atts.file.inactive = (gboolean) atoi((gchar*)start + 1); + case TA_INACTIVE: /* Obsolete */ break; case TA_ACCESS: - tag->atts.entry.access = *(start + 1); + tag->access = (char) *(start + 1); break; case TA_IMPL: - tag->atts.entry.impl = *(start + 1); + tag->impl = (char) *(start + 1); break; default: #ifdef GEANY_DEBUG @@ -368,14 +367,13 @@ gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp) } if (NULL == tag->name) return FALSE; - if (tm_tag_file_t != tag->type) - tag->atts.entry.file = file; + tag->file = file; return TRUE; }
/* alternative parser for Pascal and LaTeX global tags files with the following format * tagname|return value|arglist|description\n */ -gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp) +static gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp) { guchar buf[BUFSIZ]; guchar *start, *end; @@ -404,8 +402,8 @@ gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
if (field_len >= 1) tag->name = g_strdup(fields[0]); else tag->name = NULL; - if (field_len >= 2 && fields[1] != NULL) tag->atts.entry.var_type = g_strdup(fields[1]); - if (field_len >= 3 && fields[2] != NULL) tag->atts.entry.arglist = g_strdup(fields[2]); + if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]); + if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]); tag->type = tm_tag_prototype_t; g_strfreev(fields); } @@ -413,13 +411,15 @@ gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
if (NULL == tag->name) return FALSE; - if (tm_tag_file_t != tag->type) - tag->atts.entry.file = file; + tag->file = file; return TRUE; }
-/* Reads ctags format (http://ctags.sourceforge.net/FORMAT) */ -gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp) +/* + Same as tm_tag_init_from_file(), but parsing CTags tag file format + (http://ctags.sourceforge.net/FORMAT) +*/ +static gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp) { gchar buf[BUFSIZ]; gchar *p, *tab; @@ -460,7 +460,7 @@ gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp) } } else /* assume a line */ - tag->atts.entry.line = atol(p); + tag->line = atol(p); tab = strstr(p, ";""); /* read extension fields */ if (tab) @@ -501,7 +501,7 @@ gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp) case 'c': tag->type = tm_tag_class_t; break; case 'd': tag->type = tm_tag_macro_t; break; case 'e': tag->type = tm_tag_enumerator_t; break; - case 'F': tag->type = tm_tag_file_t; break; + case 'F': tag->type = tm_tag_other_t; break; /* Obsolete */ case 'f': tag->type = tm_tag_function_t; break; case 'g': tag->type = tm_tag_enum_t; break; case 'I': tag->type = tm_tag_class_t; break; @@ -527,39 +527,42 @@ gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp) } else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */ { - g_free(tag->atts.entry.inheritance); - tag->atts.entry.inheritance = g_strdup(value); + g_free(tag->inheritance); + tag->inheritance = g_strdup(value); } else if (0 == strcmp(key, "implementation")) /* implementation limit */ - tag->atts.entry.impl = get_tag_impl(value); + tag->impl = get_tag_impl(value); else if (0 == strcmp(key, "line")) /* line */ - tag->atts.entry.line = atol(value); + tag->line = atol(value); else if (0 == strcmp(key, "access")) /* access */ - tag->atts.entry.access = get_tag_access(value); + tag->access = get_tag_access(value); else if (0 == strcmp(key, "class") || 0 == strcmp(key, "enum") || 0 == strcmp(key, "function") || 0 == strcmp(key, "struct") || 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */ { - g_free(tag->atts.entry.scope); - tag->atts.entry.scope = g_strdup(value); + g_free(tag->scope); + tag->scope = g_strdup(value); } else if (0 == strcmp(key, "file")) /* static (local) tag */ - tag->atts.entry.local = TRUE; + tag->local = TRUE; else if (0 == strcmp(key, "signature")) /* arglist */ { - g_free(tag->atts.entry.arglist); - tag->atts.entry.arglist = g_strdup(value); + g_free(tag->arglist); + tag->arglist = g_strdup(value); } } }
- if (tm_tag_file_t != tag->type) - tag->atts.entry.file = file; + tag->file = file; return TRUE; }
+/* + Same as tm_tag_new() except that the tag attributes are read from file. + @param mode langType to use for the tag. +*/ TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format) { TMTag *tag; @@ -585,70 +588,67 @@ TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileForma TAG_FREE(tag); return NULL; } - tag->atts.file.lang = mode; + tag->lang = mode; return tag; }
-gboolean tm_tag_write(TMTag *tag, FILE *fp, guint attrs) +/* + Writes tag information to the given FILE *. + @param tag The tag information to write. + @param file FILE pointer to which the tag information is written. + @param attrs Attributes to be written (bitmask). + @return TRUE on success, FALSE on failure. +*/ +gboolean tm_tag_write(TMTag *tag, FILE *fp, TMTagAttrType attrs) { fprintf(fp, "%s", tag->name); if (attrs & tm_tag_attr_type_t) fprintf(fp, "%c%d", TA_TYPE, tag->type); - if (tag->type == tm_tag_file_t) - { - if (attrs & tm_tag_attr_time_t) - fprintf(fp, "%c%ld", TA_TIME, tag->atts.file.timestamp); - if (attrs & tm_tag_attr_lang_t) - fprintf(fp, "%c%d", TA_LANG, tag->atts.file.lang); - if ((attrs & tm_tag_attr_inactive_t) && tag->atts.file.inactive) - fprintf(fp, "%c%d", TA_INACTIVE, tag->atts.file.inactive); - } - else - { - if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->atts.entry.arglist)) - fprintf(fp, "%c%s", TA_ARGLIST, tag->atts.entry.arglist); - if (attrs & tm_tag_attr_line_t) - fprintf(fp, "%c%ld", TA_LINE, tag->atts.entry.line); - if (attrs & tm_tag_attr_local_t) - fprintf(fp, "%c%d", TA_LOCAL, tag->atts.entry.local); - if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->atts.entry.scope)) - fprintf(fp, "%c%s", TA_SCOPE, tag->atts.entry.scope); - if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->atts.entry.inheritance)) - fprintf(fp, "%c%s", TA_INHERITS, tag->atts.entry.inheritance); - if (attrs & tm_tag_attr_pointer_t) - fprintf(fp, "%c%d", TA_POINTER, tag->atts.entry.pointerOrder); - if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->atts.entry.var_type)) - fprintf(fp, "%c%s", TA_VARTYPE, tag->atts.entry.var_type); - if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->atts.entry.access)) - fprintf(fp, "%c%c", TA_ACCESS, tag->atts.entry.access); - if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->atts.entry.impl)) - fprintf(fp, "%c%c", TA_IMPL, tag->atts.entry.impl); - } + if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist)) + fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist); + if (attrs & tm_tag_attr_line_t) + fprintf(fp, "%c%ld", TA_LINE, tag->line); + if (attrs & tm_tag_attr_local_t) + fprintf(fp, "%c%d", TA_LOCAL, tag->local); + if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope)) + fprintf(fp, "%c%s", TA_SCOPE, tag->scope); + if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance)) + fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance); + if (attrs & tm_tag_attr_pointer_t) + fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder); + if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type)) + fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type); + if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access)) + fprintf(fp, "%c%c", TA_ACCESS, tag->access); + if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl)) + fprintf(fp, "%c%c", TA_IMPL, tag->impl); + if (fprintf(fp, "\n")) return TRUE; else return FALSE; }
+/* + Destroys a TMTag structure, i.e. frees all elements except the tag itself. + @param tag The TMTag structure to destroy + @see tm_tag_free() +*/ static void tm_tag_destroy(TMTag *tag) { g_free(tag->name); - if (tm_tag_file_t != tag->type) - { - g_free(tag->atts.entry.arglist); - g_free(tag->atts.entry.scope); - g_free(tag->atts.entry.inheritance); - g_free(tag->atts.entry.var_type); - } + g_free(tag->arglist); + g_free(tag->scope); + g_free(tag->inheritance); + g_free(tag->var_type); }
-#if 0 -void tm_tag_free(gpointer tag) -{ - tm_tag_unref(tag); -} -#endif
+/* + Drops a reference from a TMTag. If the reference count reaches 0, this function + destroys all data in the tag and frees the tag structure as well. + @param tag Pointer to a TMTag structure +*/ void tm_tag_unref(TMTag *tag) { /* be NULL-proof because tm_tag_free() was NULL-proof and we indent to be a @@ -660,77 +660,86 @@ void tm_tag_unref(TMTag *tag) } }
+/* + Adds a reference to a TMTag. + @param tag Pointer to a TMTag structure + @return the passed-in TMTag +*/ TMTag *tm_tag_ref(TMTag *tag) { g_atomic_int_inc(&tag->refcount); return tag; }
-int tm_tag_compare(const void *ptr1, const void *ptr2) +/* + Inbuilt tag comparison function. +*/ +static gint tm_tag_compare(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data) { unsigned int *sort_attr; int returnval = 0; TMTag *t1 = *((TMTag **) ptr1); TMTag *t2 = *((TMTag **) ptr2); + TMSortOptions *sort_options = user_data;
if ((NULL == t1) || (NULL == t2)) { g_warning("Found NULL tag"); return t2 - t1; } - if (NULL == s_sort_attrs) + if (NULL == sort_options->sort_attrs) { - if (s_partial) + if (sort_options->partial) return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, ""))); else return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, "")); }
- for (sort_attr = s_sort_attrs; *sort_attr != tm_tag_attr_none_t; ++ sort_attr) + for (sort_attr = sort_options->sort_attrs; returnval == 0 && *sort_attr != tm_tag_attr_none_t; ++ sort_attr) { switch (*sort_attr) { case tm_tag_attr_name_t: - if (s_partial) + if (sort_options->partial) returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, ""))); else returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, "")); - if (0 != returnval) - return returnval; - break; - case tm_tag_attr_type_t: - if (0 != (returnval = (t1->type - t2->type))) - return returnval; break; case tm_tag_attr_file_t: - if (0 != (returnval = (t1->atts.entry.file - t2->atts.entry.file))) - return returnval; + returnval = t1->file - t2->file; + break; + case tm_tag_attr_line_t: + returnval = t1->line - t2->line; + break; + case tm_tag_attr_type_t: + returnval = t1->type - t2->type; break; case tm_tag_attr_scope_t: - if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.scope, ""), FALLBACK(t2->atts.entry.scope, "")))) - return returnval; + returnval = strcmp(FALLBACK(t1->scope, ""), FALLBACK(t2->scope, "")); break; case tm_tag_attr_arglist_t: - if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.arglist, ""), FALLBACK(t2->atts.entry.arglist, "")))) + returnval = strcmp(FALLBACK(t1->arglist, ""), FALLBACK(t2->arglist, "")); + if (returnval != 0) { - int line_diff = (t1->atts.entry.line - t2->atts.entry.line); + int line_diff = (t1->line - t2->line);
- return line_diff ? line_diff : returnval; + returnval = line_diff ? line_diff : returnval; } break; case tm_tag_attr_vartype_t: - if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.var_type, ""), FALLBACK(t2->atts.entry.var_type, "")))) - return returnval; - break; - case tm_tag_attr_line_t: - if (0 != (returnval = (t1->atts.entry.line - t2->atts.entry.line))) - return returnval; + returnval = strcmp(FALLBACK(t1->var_type, ""), FALLBACK(t2->var_type, "")); break; } } return returnval; }
+/* + Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() since + this function substitutes duplicate entries with NULL + @param tags_array Array of tags to dedup + @return TRUE on success, FALSE on failure +*/ gboolean tm_tags_prune(GPtrArray *tags_array) { guint i, count; @@ -743,18 +752,29 @@ gboolean tm_tags_prune(GPtrArray *tags_array) return TRUE; }
-gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes) +/* + Deduplicates an array on tags using the inbuilt comparison function based on + the attributes specified. Called by tm_tags_sort() when dedup is TRUE. + @param tags_array Array of tags to dedup. + @param sort_attributes Attributes the array is sorted on. They will be deduped + on the same criteria. + @return TRUE on success, FALSE on failure +*/ +gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates) { + TMSortOptions sort_options; guint i;
if ((!tags_array) || (!tags_array->len)) return TRUE; - s_sort_attrs = sort_attributes; - s_partial = FALSE; + sort_options.sort_attrs = sort_attributes; + sort_options.partial = FALSE; for (i = 1; i < tags_array->len; ++i) { - if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]))) + if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]), &sort_options)) { + if (unref_duplicates) + tm_tag_unref(tags_array->pdata[i-1]); tags_array->pdata[i-1] = NULL; } } @@ -762,91 +782,200 @@ gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes) return TRUE; }
-gboolean tm_tags_custom_dedup(GPtrArray *tags_array, TMTagCompareFunc compare_func) +/* + Sort an array of tags on the specified attribuites using the inbuilt comparison + function. + @param tags_array The array of tags to be sorted + @param sort_attributes Attributes to be sorted on (int array terminated by 0) + @param dedup Whether to deduplicate the sorted array + @return TRUE on success, FALSE on failure +*/ +gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes, + gboolean dedup, gboolean unref_duplicates) { - guint i; - + TMSortOptions sort_options; + if ((!tags_array) || (!tags_array->len)) return TRUE; - for (i = 1; i < tags_array->len; ++i) + sort_options.sort_attrs = sort_attributes; + sort_options.partial = FALSE; + g_ptr_array_sort_with_data(tags_array, tm_tag_compare, &sort_options); + if (dedup) + tm_tags_dedup(tags_array, sort_attributes, unref_duplicates); + return TRUE; +} + +void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array) +{ + guint i; + GPtrArray *to_delete = g_ptr_array_sized_new(source_file->tags_array->len); + + for (i = 0; i < source_file->tags_array->len; i++) { - if (0 == compare_func(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]))) - tags_array->pdata[i-1] = NULL; + guint j; + guint tag_count; + TMTag **found; + TMTag *tag = source_file->tags_array->pdata[i]; + + found = tm_tags_find(tags_array, tag->name, FALSE, TRUE, &tag_count); + + for (j = 0; j < tag_count; j++) + { + if (*found != NULL && (*found)->file == source_file) + { + /* we cannot set the pointer to NULL now because the search wouldn't work */ + g_ptr_array_add(to_delete, found); + /* no break - if there are multiple tags of the same name, we would + * always find the first instance and wouldn't remove others; duplicates + * in the to_delete list aren't a problem */ + } + found++; + } } + + for (i = 0; i < to_delete->len; i++) + { + TMTag **tag = to_delete->pdata[i]; + *tag = NULL; + } + g_ptr_array_free(to_delete, TRUE); + tm_tags_prune(tags_array); - return TRUE; }
-/* Sorts newly-added tags and merges them in order with existing tags. - * This is much faster than resorting the whole array. - * Note: Having the caller append to the existing array should be faster - * than creating a new array which would likely get resized more than once. - * tags_array: array with new (perhaps unsorted) tags appended. - * orig_len: number of existing tags. */ -gboolean tm_tags_merge(GPtrArray *tags_array, gsize orig_len, - TMTagAttrType *sort_attributes, gboolean dedup) -{ - gpointer *copy, *a, *b; - gsize copy_len, i; +/* Optimized merge sort for merging sorted values from one array to another + * where one of the arrays is much smaller than the other. + * The merge complexity depends mostly on the size of the small array + * and is almost independent of the size of the big array. + * In addition, get rid of the duplicates (if both big_array and small_array are duplicate-free). */ +static GPtrArray *merge(GPtrArray *big_array, GPtrArray *small_array, + TMSortOptions *sort_options, gboolean unref_duplicates) { + guint i1 = 0; /* index to big_array */ + guint i2 = 0; /* index to small_array */ + guint initial_step; + guint step; + GPtrArray *res_array = g_ptr_array_sized_new(big_array->len + small_array->len); +#ifdef TM_DEBUG + guint cmpnum = 0; +#endif
- if ((!tags_array) || (!tags_array->len) || orig_len >= tags_array->len) - return TRUE; - if (!orig_len) - return tm_tags_sort(tags_array, sort_attributes, dedup); - copy_len = tags_array->len - orig_len; - copy = g_memdup(tags_array->pdata + orig_len, copy_len * sizeof(gpointer)); - s_sort_attrs = sort_attributes; - s_partial = FALSE; - /* enforce copy sorted with same attributes for merge */ - qsort(copy, copy_len, sizeof(gpointer), tm_tag_compare); - a = tags_array->pdata + orig_len - 1; - b = copy + copy_len - 1; - for (i = tags_array->len - 1;; i--) + /* swap the arrays if len(small) > len(big) */ + if (small_array->len > big_array->len) + { + GPtrArray *tmp = small_array; + small_array = big_array; + big_array = tmp; + } + + /* on average, we are merging a value from small_array every + * len(big_array) / len(small_array) values - good approximation for fast jump + * step size */ + initial_step = (small_array->len > 0) ? big_array->len / small_array->len : 1; + initial_step = initial_step > 4 ? initial_step : 1; + step = initial_step; + + while (i1 < big_array->len && i2 < small_array->len) { - gint cmp = tm_tag_compare(a, b); + gpointer val1; + gpointer val2 = small_array->pdata[i2];
- tags_array->pdata[i] = (cmp >= 0) ? *a-- : *b--; - if (a < tags_array->pdata) + if (step > 4) /* fast path start */ { - /* include remainder of copy as well as current value of b */ - memcpy(tags_array->pdata, copy, ((b + 1) - copy) * sizeof(gpointer)); - break; + guint j1 = (i1 + step < big_array->len) ? i1 + step : big_array->len - 1; + + val1 = big_array->pdata[j1]; +#ifdef TM_DEBUG + cmpnum++; +#endif + /* if the value in big_array after making the big step is still smaller + * than the value in small_array, we can copy all the values inbetween + * into the result without making expensive string comparisons */ + if (tm_tag_compare(&val1, &val2, sort_options) < 0) + { + while (i1 <= j1) + { + val1 = big_array->pdata[i1]; + /* we allocated enough space so we are sure we don't need to reallocate + * the array - copy and increment the size directly so it can be inlined */ + res_array->pdata[res_array->len++] = val1; + i1++; + } + } + else + { + /* lower the step and try again */ + step /= 2; + } + } /* fast path end */ + else + { + gint cmpval; + +#ifdef TM_DEBUG + cmpnum++; +#endif + val1 = big_array->pdata[i1]; + cmpval = tm_tag_compare(&val1, &val2, sort_options); + if (cmpval < 0) + { + g_ptr_array_add(res_array, val1); + i1++; + } + else + { + g_ptr_array_add(res_array, val2); + i2++; + /* value from small_array gets merged - reset the step size */ + step = initial_step; + if (cmpval == 0) + { + i1++; /* remove the duplicate, keep just the newly merged value */ + if (unref_duplicates) + tm_tag_unref(val1); + } + } } - if (b < copy) - break; /* remaining elements of 'a' are in place already */ - g_assert(i != 0); } - s_sort_attrs = NULL; - g_free(copy); - if (dedup) - tm_tags_dedup(tags_array, sort_attributes); - return TRUE; -}
-gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean dedup) -{ - if ((!tags_array) || (!tags_array->len)) - return TRUE; - s_sort_attrs = sort_attributes; - s_partial = FALSE; - qsort(tags_array->pdata, tags_array->len, sizeof(gpointer), tm_tag_compare); - s_sort_attrs = NULL; - if (dedup) - tm_tags_dedup(tags_array, sort_attributes); - return TRUE; + /* end of one of the arrays reached - copy the rest from the other array */ + while (i1 < big_array->len) + g_ptr_array_add(res_array, big_array->pdata[i1++]); + while (i2 < small_array->len) + g_ptr_array_add(res_array, small_array->pdata[i2++]); + +#ifdef TM_DEBUG + printf("cmpnums: %u\n", cmpnum); + printf("total tags: %u\n", big_array->len); + printf("merged tags: %u\n\n", small_array->len); +#endif + + return res_array; }
-gboolean tm_tags_custom_sort(GPtrArray *tags_array, TMTagCompareFunc compare_func, gboolean dedup) +GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array, + TMTagAttrType *sort_attributes, gboolean unref_duplicates) { - if ((!tags_array) || (!tags_array->len)) - return TRUE; - qsort(tags_array->pdata, tags_array->len, sizeof(gpointer), compare_func); - if (dedup) - tm_tags_custom_dedup(tags_array, compare_func); - return TRUE; + GPtrArray *res_array; + TMSortOptions sort_options; + + sort_options.sort_attrs = sort_attributes; + sort_options.partial = FALSE; + res_array = merge(big_array, small_array, &sort_options, unref_duplicates); + return res_array; }
-GPtrArray *tm_tags_extract(GPtrArray *tags_array, guint tag_types) +/* + This function will extract the tags of the specified types from an array of tags. + The returned value is a GPtrArray which should be free-d with a call to + g_ptr_array_free(array, TRUE). However, do not free the tags themselves since they + are not duplicated. + @param tags_array The original array of tags + @param tag_types - The tag types to extract. Can be a bitmask. For example, passing + (tm_tag_typedef_t | tm_tag_struct_t) will extract all typedefs and structures from + the original array. + @return an array of tags (NULL on failure) +*/ +GPtrArray *tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types) { GPtrArray *new_tags; guint i; @@ -864,6 +993,11 @@ GPtrArray *tm_tags_extract(GPtrArray *tags_array, guint tag_types) return new_tags; }
+/* + Completely frees an array of tags. + @param tags_array Array of tags to be freed. + @param free_array Whether the GptrArray is to be freed as well. +*/ void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all) { if (tags_array) @@ -878,46 +1012,86 @@ void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all) } }
-static TMTag **tags_search(const GPtrArray *tags_array, TMTag *tag, gboolean partial, - gboolean tags_array_sorted) +/* copy/pasted bsearch() from libc extended with user_data for comparison function + * and using glib types */ +static gpointer binary_search(gpointer key, gpointer base, size_t nmemb, + GCompareDataFunc compar, gpointer user_data) +{ + gsize l, u, idx; + gpointer p; + gint comparison; + + l = 0; + u = nmemb; @@ Diff output truncated at 100000 characters. @@
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).