diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 7ec299b54..d139fdfaf 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -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 diff --git a/doc/Makefile.am b/doc/Makefile.am index d5ffecc8e..31985f76f 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -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) diff --git a/plugins/geanyfunctions.h b/plugins/geanyfunctions.h index 1a52b0436..24bd0a79f 100644 --- a/plugins/geanyfunctions.h +++ b/plugins/geanyfunctions.h @@ -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 \ diff --git a/src/dialogs.c b/src/dialogs.c index c0d1dd84e..55750c8a0 100644 --- a/src/dialogs.c +++ b/src/dialogs.c @@ -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); diff --git a/src/document.c b/src/document.c index be788a908..ba9663ed4 100644 --- a/src/document.c +++ b/src/document.c @@ -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) */ diff --git a/src/document.h b/src/document.h index eaa125ba9..ece1b6de3 100644 --- a/src/document.h +++ b/src/document.h @@ -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. */ diff --git a/src/editor.c b/src/editor.c index 3dbd5d029..9e0c95fee 100644 --- a/src/editor.c +++ b/src/editor.c @@ -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 */ diff --git a/src/highlighting.c b/src/highlighting.c index ee8b8aeb8..3b5c59fa1 100644 --- a/src/highlighting.c +++ b/src/highlighting.c @@ -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 diff --git a/src/main.c b/src/main.c index cc9290653..af4a9a1fd 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/plugindata.h b/src/plugindata.h index 5121cd429..ec0ed040f 100644 --- a/src/plugindata.h +++ b/src/plugindata.h @@ -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; diff --git a/src/plugins.c b/src/plugins.c index 6a7000dae..715e6b71d 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -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 = { diff --git a/src/sidebar.c b/src/sidebar.c index 4f75cdd06..412fac314 100644 --- a/src/sidebar.c +++ b/src/sidebar.c @@ -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(); diff --git a/src/symbols.c b/src/symbols.c index d2055e87d..fc4685c76 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -64,11 +64,6 @@ #include -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 */ diff --git a/src/symbols.h b/src/symbols.h index 75436f25e..0e9186279 100644 --- a/src/symbols.h +++ b/src/symbols.h @@ -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); diff --git a/tagmanager/ctags/read.c b/tagmanager/ctags/read.c index 2c4537f47..554c1e64e 100644 --- a/tagmanager/ctags/read.c +++ b/tagmanager/ctags/read.c @@ -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; diff --git a/tagmanager/ctags/read.h b/tagmanager/ctags/read.h index 641031893..cbe4d79cd 100644 --- a/tagmanager/ctags/read.h +++ b/tagmanager/ctags/read.h @@ -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 diff --git a/tagmanager/src/Makefile.am b/tagmanager/src/Makefile.am index 0bb839f6a..ec8bf8f6a 100644 --- a/tagmanager/src/Makefile.am +++ b/tagmanager/src/Makefile.am @@ -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 diff --git a/tagmanager/src/makefile.win32 b/tagmanager/src/makefile.win32 index 0ffcfa6be..f199d444f 100644 --- a/tagmanager/src/makefile.win32 +++ b/tagmanager/src/makefile.win32 @@ -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) $@ diff --git a/tagmanager/src/tm_file_entry.c b/tagmanager/src/tm_file_entry.c deleted file mode 100644 index 55fa79fa7..000000000 --- a/tagmanager/src/tm_file_entry.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#ifdef HAVE_FCNTL_H -# include -#endif -#include -#include -#ifdef HAVE_FNMATCH_H -# include -#endif -#include - -#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; -} diff --git a/tagmanager/src/tm_file_entry.h b/tagmanager/src/tm_file_entry.h deleted file mode 100644 index 8d398e4b9..000000000 --- a/tagmanager/src/tm_file_entry.h +++ /dev/null @@ -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 - -/* \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 */ diff --git a/tagmanager/src/tm_source_file.c b/tagmanager/src/tm_source_file.c index 62a51bba6..40118a9bd 100644 --- a/tagmanager/src/tm_source_file.c +++ b/tagmanager/src/tm_source_file.c @@ -18,37 +18,159 @@ #include #include #include +#include +#include +#ifdef G_OS_WIN32 +# define VC_EXTRALEAN +# define WIN32_LEAN_AND_MEAN +# include /* 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) { - if (0 == source_file_class_id) - source_file_class_id = tm_work_object_register(tm_source_file_free - , tm_source_file_update, NULL); +#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) +{ + 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)) - { - if (LanguageTable [source_file->lang]->parser != NULL) - { - LanguageTable [source_file->lang]->parser (); - fileClose (); - break; - } - else if (LanguageTable [source_file->lang]->parser2 != NULL) - status = LanguageTable [source_file->lang]->parser2 (passCount); - fileClose (); - } + 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 { - g_warning("%s: Unable to open %s", G_STRFUNC, file_name); - return FALSE; + if (!g_file_get_contents(file_name, (gchar**)&text_buf, (gsize*)&buf_size, NULL)) + { + g_warning("Unable to open %s", file_name); + return FALSE; + } + free_buf = TRUE; } - ++ 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)) + if (!parse_file && (NULL == text_buf || 0 == buf_size)) { - g_warning("Attempt to parse NULL file"); - return FALSE; + /* 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; } - if ((NULL == text_buf) || (0 == buf_size)) - { - g_warning("Attempt to parse a NULL text buffer"); - } - - 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; + + if (free_buf) + g_free(text_buf); + return !retry; } -void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist) +/* 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) { - int count; - TMTag **tags, *tag; - - if (NULL == arglist || - NULL == tag_name || - NULL == current_source_file || - NULL == current_source_file->work_object.tags_array) + if (NULL == LanguageTable) { - 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); + initializeParsing(); + installLanguageMapDefaults(); + if (NULL == TagEntryFunction) + TagEntryFunction = tm_source_file_tags; + if (NULL == TagEntrySetArglistFunction) + TagEntrySetArglistFunction = tm_source_file_set_tag_arglist; } + return getLanguageName(lang); } -int tm_source_file_tags(const tagEntryInfo *tag) +/* 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) { - 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) -{ - 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 getNamedLanguage(name); } - -gboolean tm_source_file_buffer_update(TMWorkObject *source_file, guchar* text_buf, - gint buf_size, gboolean update_parent) -{ -#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) - { -#ifdef TM_DEBUG - g_message("Updating parent from buffer.."); -#endif - tm_work_object_update(source_file->parent, TRUE, FALSE, TRUE); - } -#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; -} - - -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 diff --git a/tagmanager/src/tm_source_file.h b/tagmanager/src/tm_source_file.h index 45f0e75a3..173b14a11 100644 --- a/tagmanager/src/tm_source_file.h +++ b/tagmanager/src/tm_source_file.h @@ -10,8 +10,8 @@ #ifndef TM_SOURCE_FILE_H #define TM_SOURCE_FILE_H -#include "tm_work_object.h" - +#include +#include #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) -/* Checks whether the object is a TMSourceFile */ -#define IS_TM_SOURCE_FILE(source_file) (((TMWorkObject *) (source_file))->type \ - == source_file_class_id) +/* Evaluates to X is X is defined, else evaluates to Y */ +#define FALLBACK(X,Y) (X)?(X):(Y) -/*! - 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); +TMSourceFile *tm_source_file_new(const char *file_name, 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); +void tm_source_file_free(TMSourceFile *source_file); + +gchar *tm_get_real_path(const gchar *file_name); #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); - -/* 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); - -/* - 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); - -/* - 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; - -/* 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 */ diff --git a/tagmanager/src/tm_tag.c b/tagmanager/src/tm_tag.c index d307bc72b..a617544ee 100644 --- a/tagmanager/src/tm_tag.c +++ b/tagmanager/src/tm_tag.c @@ -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) +{ + TMSortOptions sort_options; + + if ((!tags_array) || (!tags_array->len)) + return TRUE; + 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; - - if ((!tags_array) || (!tags_array->len)) - return TRUE; - for (i = 1; i < tags_array->len; ++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; - } - 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; - - 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--) - { - gint cmp = tm_tag_compare(a, b); - - tags_array->pdata[i] = (cmp >= 0) ? *a-- : *b--; - if (a < tags_array->pdata) + 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++) { - /* include remainder of copy as well as current value of b */ - memcpy(tags_array->pdata, copy, ((b + 1) - copy) * sizeof(gpointer)); - break; + 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++; } - 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; + + 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); } -gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean dedup) +/* 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 + + /* 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) + { + gpointer val1; + gpointer val2 = small_array->pdata[i2]; + + if (step > 4) /* fast path start */ + { + 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); + } + } + } + } + + /* 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; +} + +GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array, + TMTagAttrType *sort_attributes, gboolean unref_duplicates) { - 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; + 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; } -gboolean tm_tags_custom_sort(GPtrArray *tags_array, TMTagCompareFunc compare_func, gboolean dedup) -{ - 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 *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; + while (l < u) + { + idx = (l + u) / 2; + p = (gpointer) (((const gchar *) base) + (idx * sizeof(gpointer))); + comparison = (*compar) (key, p, user_data); + if (comparison < 0) + u = idx; + else if (comparison > 0) + l = idx + 1; + else + return (gpointer) p; + } + + return NULL; +} + +static TMTag **tags_search(const GPtrArray *tags_array, TMTag *tag, + gboolean tags_array_sorted, TMSortOptions *sort_options) { if (tags_array_sorted) { /* fast binary search on sorted tags array */ - return (TMTag **) bsearch(&tag, tags_array->pdata, tags_array->len - , sizeof(gpointer), tm_tag_compare); + return (TMTag **) binary_search(&tag, tags_array->pdata, tags_array->len, + tm_tag_compare, sort_options); } else { /* the slow way: linear search (to make it a bit faster, search reverse assuming * that the tag to search was added recently) */ - int i; + guint i; TMTag **t; - for (i = tags_array->len - 1; i >= 0; i--) + for (i = tags_array->len; i > 0; i--) { - t = (TMTag **) &tags_array->pdata[i]; - if (0 == tm_tag_compare(&tag, t)) + t = (TMTag **) &tags_array->pdata[i - 1]; + if (0 == tm_tag_compare(&tag, t, sort_options)) return t; } } return NULL; } +/* + Returns a pointer to the position of the first matching tag in a (sorted) tags array. + The passed array of tags should be already sorted by name for optimal performance. If + \c tags_array_sorted is set to FALSE, it may be unsorted but the lookup will be slower. + @param tags_array Tag array (may be sorted on name) + @param name Name of the tag to locate. + @param partial If TRUE, matches the first part of the name instead of doing exact match. + @param tags_array_sorted If TRUE, the passed \c tags_array is sorted by name so it can be + searched with binary search. Otherwise it is searched linear which is obviously slower. + @param tagCount Return location of the matched tags. +*/ TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name, - gboolean partial, gboolean tags_array_sorted, int * tagCount) + gboolean partial, gboolean tags_array_sorted, guint * tagCount) { static TMTag *tag = NULL; TMTag **result; - int tagMatches=0; + guint tagMatches=0; + TMSortOptions sort_options; + *tagCount = 0; if ((!tags_array) || (!tags_array->len)) return NULL; if (NULL == tag) tag = g_new0(TMTag, 1); tag->name = (char *) name; - s_sort_attrs = NULL; - s_partial = partial; + sort_options.sort_attrs = NULL; + sort_options.partial = partial; - result = tags_search(tags_array, tag, partial, tags_array_sorted); + result = tags_search(tags_array, tag, tags_array_sorted, &sort_options); /* There can be matches on both sides of result */ if (result) { @@ -929,24 +1103,70 @@ TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name, adv++; for (; adv <= last && *adv; ++ adv) { - if (0 != tm_tag_compare(&tag, adv)) + if (0 != tm_tag_compare(&tag, adv, &sort_options)) break; ++tagMatches; } /* Now look for matches from result and below */ for (; result >= (TMTag **) tags_array->pdata; -- result) { - if (0 != tm_tag_compare(&tag, (TMTag **) result)) + if (0 != tm_tag_compare(&tag, (TMTag **) result, &sort_options)) break; ++tagMatches; } *tagCount=tagMatches; ++ result; /* Correct address for the last successful match */ } - s_partial = FALSE; return (TMTag **) result; } +/* Returns TMTag which "own" given line + @param line Current line in edited file. + @param file_tags A GPtrArray of edited file TMTag pointers. + @param tag_types the tag types to include in the match + @return TMTag pointers to owner tag. */ +const TMTag * +tm_get_current_tag (GPtrArray * file_tags, const gulong line, const TMTagType tag_types) +{ + TMTag *matching_tag = NULL; + if (file_tags && file_tags->len) + { + guint i; + gulong matching_line = 0; + + for (i = 0; (i < file_tags->len); ++i) + { + TMTag *tag = TM_TAG (file_tags->pdata[i]); + if (tag && tag->type & tag_types && + tag->line <= line && tag->line > matching_line) + { + matching_tag = tag; + matching_line = tag->line; + } + } + } + return matching_tag; +} + +#if 0 +/* Returns TMTag to function or method which "own" given line + @param line Current line in edited file. + @param file_tags A GPtrArray of edited file TMTag pointers. + @return TMTag pointers to owner function. */ +static const TMTag * +tm_get_current_function (GPtrArray * file_tags, const gulong line) +{ + return tm_get_current_tag (file_tags, line, tm_tag_function_t | tm_tag_method_t); +} +#endif + + +#ifdef TM_DEBUG /* various debugging functions */ + +/* + Returns the type of tag as a string + @param tag The tag whose type is required +*/ const char *tm_tag_type_name(const TMTag *tag) { g_return_val_if_fail(tag, NULL); @@ -970,12 +1190,15 @@ const char *tm_tag_type_name(const TMTag *tag) case tm_tag_externvar_t: return "extern"; case tm_tag_macro_t: return "define"; case tm_tag_macro_with_arg_t: return "macro"; - case tm_tag_file_t: return "file"; default: return NULL; } return NULL; } +/* + Returns the TMTagType given the name of the type. Reverse of tm_tag_type_name. + @param tag_name Name of the tag type +*/ TMTagType tm_tag_name_type(const char* tag_name) { g_return_val_if_fail(tag_name, tm_tag_undef_t); @@ -998,14 +1221,13 @@ TMTagType tm_tag_name_type(const char* tag_name) else if (strcmp(tag_name, "extern") == 0) return tm_tag_externvar_t; else if (strcmp(tag_name, "define") == 0) return tm_tag_macro_t; else if (strcmp(tag_name, "macro") == 0) return tm_tag_macro_with_arg_t; - else if (strcmp(tag_name, "file") == 0) return tm_tag_file_t; else return tm_tag_undef_t; } static const char *tm_tag_impl_name(TMTag *tag) { - g_return_val_if_fail(tag && (tm_tag_file_t != tag->type), NULL); - if (TAG_IMPL_VIRTUAL == tag->atts.entry.impl) + g_return_val_if_fail(tag, NULL); + if (TAG_IMPL_VIRTUAL == tag->impl) return "virtual"; else return NULL; @@ -1013,27 +1235,27 @@ static const char *tm_tag_impl_name(TMTag *tag) static const char *tm_tag_access_name(TMTag *tag) { - g_return_val_if_fail(tag && (tm_tag_file_t != tag->type), NULL); - if (TAG_ACCESS_PUBLIC == tag->atts.entry.access) + g_return_val_if_fail(tag, NULL); + if (TAG_ACCESS_PUBLIC == tag->access) return "public"; - else if (TAG_ACCESS_PROTECTED == tag->atts.entry.access) + else if (TAG_ACCESS_PROTECTED == tag->access) return "protected"; - else if (TAG_ACCESS_PRIVATE == tag->atts.entry.access) + else if (TAG_ACCESS_PRIVATE == tag->access) return "private"; else return NULL; } +/* + Prints information about a tag to the given file pointer. + @param tag The tag whose info is required. + @param fp The file pointer of teh file to print the info to. +*/ void tm_tag_print(TMTag *tag, FILE *fp) { const char *laccess, *impl, *type; if (!tag || !fp) return; - if (tm_tag_file_t == tag->type) - { - fprintf(fp, "%s\n", tag->name); - return; - } laccess = tm_tag_access_name(tag); impl = tm_tag_impl_name(tag); type = tm_tag_type_name(tag); @@ -1043,21 +1265,24 @@ void tm_tag_print(TMTag *tag, FILE *fp) fprintf(fp, "%s ", impl); if (type) fprintf(fp, "%s ", type); - if (tag->atts.entry.var_type) - fprintf(fp, "%s ", tag->atts.entry.var_type); - if (tag->atts.entry.scope) - fprintf(fp, "%s::", tag->atts.entry.scope); + if (tag->var_type) + fprintf(fp, "%s ", tag->var_type); + if (tag->scope) + fprintf(fp, "%s::", tag->scope); fprintf(fp, "%s", tag->name); - if (tag->atts.entry.arglist) - fprintf(fp, "%s", tag->atts.entry.arglist); - if (tag->atts.entry.inheritance) - fprintf(fp, " : from %s", tag->atts.entry.inheritance); - if ((tag->atts.entry.file) && (tag->atts.entry.line > 0)) - fprintf(fp, "[%s:%ld]", tag->atts.entry.file->work_object.file_name - , tag->atts.entry.line); + if (tag->arglist) + fprintf(fp, "%s", tag->arglist); + if (tag->inheritance) + fprintf(fp, " : from %s", tag->inheritance); + if ((tag->file) && (tag->line > 0)) + fprintf(fp, "[%s:%ld]", tag->file->file_name + , tag->line); fprintf(fp, "\n"); } +/* + Prints info about all tags in the array to the given file pointer. +*/ void tm_tags_array_print(GPtrArray *tags, FILE *fp) { guint i; @@ -1071,16 +1296,21 @@ void tm_tags_array_print(GPtrArray *tags, FILE *fp) } } +/* + Returns the depth of tag scope (useful for finding tag hierarchy +*/ gint tm_tag_scope_depth(const TMTag *t) { gint depth; char *s; - if(!(t && t->atts.entry.scope)) + if(!(t && t->scope)) return 0; - for (s = t->atts.entry.scope, depth = 0; s; s = strstr(s, "::")) + for (s = t->scope, depth = 0; s; s = strstr(s, "::")) { ++ depth; ++ s; } return depth; } + +#endif /* TM_DEBUG */ diff --git a/tagmanager/src/tm_tag.h b/tagmanager/src/tm_tag.h index 19d589a5a..3d7e73c37 100644 --- a/tagmanager/src/tm_tag.h +++ b/tagmanager/src/tm_tag.h @@ -10,15 +10,15 @@ #ifndef TM_TAG_H #define TM_TAG_H -/* \file +/* @file The TMTag structure and the associated functions are used to manipulate tags and arrays of tags. Normally, you should not create tags individually but through an external interface such as tm_source_file_parse(), which generates an array of tags for the given source file. Once the tag list is generated, you can do various operations such as: -# Extract relevant tags using tm_tags_extract() - -# Sort an array of tags using tm_tags_sort() or tm_tags_custom_sort() - -# Deduplicate an array of tags using tm_tags_dedup() or tm_tags_dedup_custom(). + -# Sort an array of tags using tm_tags_sort() + -# Deduplicate an array of tags using tm_tags_dedup(). An important thing to remember here is that the tags operations such as extraction, sorting and deduplication do not change the tag itself in any way, but rather, @@ -37,41 +37,41 @@ extern "C" { #endif -/*! Use the TM_TAG() macro to cast a pointer to (TMTag *) */ +/** Use the TM_TAG() macro to cast a pointer to (TMTag *) */ #define TM_TAG(tag) ((TMTag *) tag) -/*! +/** Types of tags. It is a bitmask so that multiple tag types can be used simultaneously by 'OR'-ing them bitwise. e.g. tm_tag_class_t | tm_tag_struct_t */ typedef enum { - tm_tag_undef_t = 0, /*!< Unknown type */ - tm_tag_class_t = 1, /*!< Class declaration */ - tm_tag_enum_t = 2, /*!< Enum declaration */ - tm_tag_enumerator_t = 4, /*!< Enumerator value */ - tm_tag_field_t = 8, /*!< Field (Java only) */ - tm_tag_function_t = 16, /*!< Function definition */ - tm_tag_interface_t = 32, /*!< Interface (Java only) */ - tm_tag_member_t = 64, /*!< Member variable of class/struct */ - tm_tag_method_t = 128, /*!< Class method (Java only) */ - tm_tag_namespace_t = 256, /*!< Namespace declaration */ - tm_tag_package_t = 512, /*!< Package (Java only) */ - tm_tag_prototype_t = 1024, /*!< Function prototype */ - tm_tag_struct_t = 2048, /*!< Struct declaration */ - tm_tag_typedef_t = 4096, /*!< Typedef */ - tm_tag_union_t = 8192, /*!< Union */ - tm_tag_variable_t = 16384, /*!< Variable */ - tm_tag_externvar_t = 32768, /*!< Extern or forward declaration */ - tm_tag_macro_t = 65536, /*!< Macro (without arguments) */ - tm_tag_macro_with_arg_t = 131072, /*!< Parameterized macro */ - tm_tag_file_t = 262144, /*!< File (Pseudo tag) */ - tm_tag_other_t = 524288, /*!< Other (non C/C++/Java tag) */ - tm_tag_max_t = 1048575 /*!< Maximum value of TMTagType */ + tm_tag_undef_t = 0, /**< Unknown type */ + tm_tag_class_t = 1, /**< Class declaration */ + tm_tag_enum_t = 2, /**< Enum declaration */ + tm_tag_enumerator_t = 4, /**< Enumerator value */ + tm_tag_field_t = 8, /**< Field (Java only) */ + tm_tag_function_t = 16, /**< Function definition */ + tm_tag_interface_t = 32, /**< Interface (Java only) */ + tm_tag_member_t = 64, /**< Member variable of class/struct */ + tm_tag_method_t = 128, /**< Class method (Java only) */ + tm_tag_namespace_t = 256, /**< Namespace declaration */ + tm_tag_package_t = 512, /**< Package (Java only) */ + tm_tag_prototype_t = 1024, /**< Function prototype */ + tm_tag_struct_t = 2048, /**< Struct declaration */ + tm_tag_typedef_t = 4096, /**< Typedef */ + tm_tag_union_t = 8192, /**< Union */ + tm_tag_variable_t = 16384, /**< Variable */ + tm_tag_externvar_t = 32768, /**< Extern or forward declaration */ + tm_tag_macro_t = 65536, /**< Macro (without arguments) */ + tm_tag_macro_with_arg_t = 131072, /**< Parameterized macro */ + tm_tag_file_t = 262144, /**< File (Pseudo tag) - obsolete */ + tm_tag_other_t = 524288, /**< Other (non C/C++/Java tag) */ + tm_tag_max_t = 1048575 /**< Maximum value of TMTagType */ } TMTagType; -/*! +/** Tag Attributes. Note that some attributes are available to file pseudotags only. Attributes are useful for specifying as arguments to the builtin sort and dedup functions, and during printing or writing @@ -81,311 +81,129 @@ typedef enum */ typedef enum { - tm_tag_attr_none_t = 0, /*!< Undefined */ - tm_tag_attr_name_t = 1, /*!< Tag Name */ - tm_tag_attr_type_t = 2, /*!< Tag Type */ - tm_tag_attr_file_t = 4, /*!< File in which tag exists */ - tm_tag_attr_line_t = 8, /*!< Line number of tag */ - tm_tag_attr_pos_t = 16, /*!< Byte position of tag in the file (Obsolete) */ - tm_tag_attr_scope_t = 32, /*!< Scope of the tag */ - tm_tag_attr_inheritance_t = 64, /*!< Parent classes */ - tm_tag_attr_arglist_t = 128, /*!< Argument list */ - tm_tag_attr_local_t = 256, /*!< If it has local scope */ - tm_tag_attr_time_t = 512, /*!< Modification time (File tag only) */ - tm_tag_attr_vartype_t = 1024, /*!< Variable Type */ - tm_tag_attr_access_t = 2048, /*!< Access type (public/protected/private) */ - tm_tag_attr_impl_t = 4096, /*!< Implementation (e.g. virtual) */ - tm_tag_attr_lang_t = 8192, /*!< Language (File tag only) */ - tm_tag_attr_inactive_t = 16384, /*!< Inactive file (File tag only) */ - tm_tag_attr_pointer_t = 32768, /*!< Pointer type */ - tm_tag_attr_max_t = 65535 /*!< Maximum value */ + tm_tag_attr_none_t = 0, /**< Undefined */ + tm_tag_attr_name_t = 1, /**< Tag Name */ + tm_tag_attr_type_t = 2, /**< Tag Type */ + tm_tag_attr_file_t = 4, /**< File in which tag exists */ + tm_tag_attr_line_t = 8, /**< Line number of tag */ + tm_tag_attr_pos_t = 16, /**< Byte position of tag in the file (Obsolete) */ + tm_tag_attr_scope_t = 32, /**< Scope of the tag */ + tm_tag_attr_inheritance_t = 64, /**< Parent classes */ + tm_tag_attr_arglist_t = 128, /**< Argument list */ + tm_tag_attr_local_t = 256, /**< If it has local scope */ + tm_tag_attr_time_t = 512, /**< Modification time (File tag only) */ + tm_tag_attr_vartype_t = 1024, /**< Variable Type */ + tm_tag_attr_access_t = 2048, /**< Access type (public/protected/private) */ + tm_tag_attr_impl_t = 4096, /**< Implementation (e.g. virtual) */ + tm_tag_attr_lang_t = 8192, /**< Language (File tag only) */ + tm_tag_attr_inactive_t = 16384, /**< Inactive file (File tag only, obsolete) */ + tm_tag_attr_pointer_t = 32768, /**< Pointer type */ + tm_tag_attr_max_t = 65535 /**< Maximum value */ } TMTagAttrType; -/*! Tag access type for C++/Java member functions and variables */ -#define TAG_ACCESS_PUBLIC 'p' /*!< Public member */ -#define TAG_ACCESS_PROTECTED 'r' /*!< Protected member */ -#define TAG_ACCESS_PRIVATE 'v' /*!< Private member */ -#define TAG_ACCESS_FRIEND 'f' /*!< Friend members/functions */ -#define TAG_ACCESS_DEFAULT 'd' /*!< Default access (Java) */ -#define TAG_ACCESS_UNKNOWN 'x' /*!< Unknown access type */ +/** Tag access type for C++/Java member functions and variables */ +#define TAG_ACCESS_PUBLIC 'p' /**< Public member */ +#define TAG_ACCESS_PROTECTED 'r' /**< Protected member */ +#define TAG_ACCESS_PRIVATE 'v' /**< Private member */ +#define TAG_ACCESS_FRIEND 'f' /**< Friend members/functions */ +#define TAG_ACCESS_DEFAULT 'd' /**< Default access (Java) */ +#define TAG_ACCESS_UNKNOWN 'x' /**< Unknown access type */ -/*! Tag implementation type for functions */ -#define TAG_IMPL_VIRTUAL 'v' /*!< Virtual implementation */ -#define TAG_IMPL_UNKNOWN 'x' /*!< Unknown implementation */ +/** Tag implementation type for functions */ +#define TAG_IMPL_VIRTUAL 'v' /**< Virtual implementation */ +#define TAG_IMPL_UNKNOWN 'x' /**< Unknown implementation */ -/*! +/** This structure holds all information about a tag, including the file pseudo tag. It should always be created indirectly with one of the tag creation functions such as tm_source_file_parse() or tm_tag_new_from_file(). Once created, they can be sorted, deduped, etc. using functions such as - tm_tags_custom_sort(), tm_tags_sort(), tm_tags_dedup() and tm_tags_custom_dedup() + tm_tags_sort() or tm_tags_dedup() */ typedef struct _TMTag { - char *name; /*!< Name of tag */ - TMTagType type; /*!< Tag Type */ - union - { - /*! These are *real* tag attributes */ - struct - { - TMSourceFile *file; /*!< File in which the tag occurs */ - gulong line; /*!< Line number of the tag */ - gboolean local; /*!< Is the tag of local scope */ - guint pointerOrder; - char *arglist; /*!< Argument list (functions/prototypes/macros) */ - char *scope; /*!< Scope of tag */ - char *inheritance; /*!< Parent classes */ - char *var_type; /*!< Variable type (maps to struct for typedefs) */ - char access; /*!< Access type (public/protected/private/etc.) */ - char impl; /*!< Implementation (e.g. virtual) */ - } entry; - /*! These are pseudo tag attributes representing a file */ - struct - { - time_t timestamp; /*!< Time of parsing of the file */ - langType lang; /*!< Programming language of the file */ - gboolean inactive; /*!< Whether this file is to be parsed */ - } file; - } atts; - gint refcount; /*!< the reference count of the tag */ + char *name; /**< Name of tag */ + TMTagType type; /**< Tag Type */ + gint refcount; /* the reference count of the tag */ + + /** These are tag attributes */ + TMSourceFile *file; /**< File in which the tag occurs; NULL for global tags */ + gulong line; /**< Line number of the tag */ + gboolean local; /**< Is the tag of local scope */ + guint pointerOrder; + char *arglist; /**< Argument list (functions/prototypes/macros) */ + char *scope; /**< Scope of tag */ + char *inheritance; /**< Parent classes */ + char *var_type; /**< Variable type (maps to struct for typedefs) */ + char access; /**< Access type (public/protected/private/etc.) */ + char impl; /**< Implementation (e.g. virtual) */ + langType lang; /**< Programming language of the file */ } TMTag; #ifdef GEANY_PRIVATE +extern const TMTagType TM_GLOBAL_TYPE_MASK; + + typedef enum { TM_FILE_FORMAT_TAGMANAGER, TM_FILE_FORMAT_PIPE, TM_FILE_FORMAT_CTAGS } TMFileFormat; -/* - Prototype for user-defined tag comparison function. This is the type - of argument that needs to be passed to tm_tags_sort_custom() and - tm_tags_dedup_custom(). The function should take two void pointers, - cast them to (TMTag **) and return 0, 1 or -1 depending on whether the - first tag is equal to, greater than or less than the second tag. -*/ -typedef int (*TMTagCompareFunc) (const void *ptr1, const void *ptr2); /* The GType for a TMTag */ #define TM_TYPE_TAG (tm_tag_get_type()) -/* Gets the GType for a TMTag */ GType tm_tag_get_type(void) G_GNUC_CONST; -/* - 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 -*/ -gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry); - -/* - 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 -*/ -gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp); - -/* - Same as tm_tag_init_from_file(), but using an alternative parser for PHP and - LaTeX global tags files. -*/ -gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp); - -/* - Same as tm_tag_init_from_file(), but parsing CTags tag file format -*/ -gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp); - -/* - 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); -/* - 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); -/* - 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 *file, guint attrs); -/* - Inbuilt tag comparison function. Do not call directly since it needs some - static variables to be set. Always use tm_tags_sort() and tm_tags_dedup() - instead. -*/ -int tm_tag_compare(const void *ptr1, const void *ptr2); +void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array); -gboolean tm_tags_merge(GPtrArray *tags_array, gsize orig_len, - TMTagAttrType *sort_attributes, gboolean dedup); +GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array, + TMTagAttrType *sort_attributes, gboolean unref_duplicates); -/* - 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 tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes, + gboolean dedup, gboolean unref_duplicates); -/* - This function should be used whenever more involved sorting is required. For this, - you need to write a function as per the prototype of TMTagCompareFunc() and pass - the function as a parameter to this function. - \param tags_array Array of tags to be sorted - \param compare_func A function which takes two pointers to (TMTag *)s and returns - 0, 1 or -1 depending on whether the first value is equal to, greater than or less that - the second - \param dedup Whether to deduplicate the sorted array. Note that the same comparison - function will be used - \return TRUE on success, FALSE on failure -*/ -gboolean tm_tags_custom_sort(GPtrArray *tags_array, TMTagCompareFunc compare_func, gboolean dedup); - -/* - 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, guint tag_types); -/* - Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() and - tm_tags_custom_dedup() since these functions substitute 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); -/* - 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 tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates); -/* - This is a more powerful form of tm_tags_dedup() since it can accomodate user - defined comparison functions. Called by tm_tags_custom_sort() is dedup is TRUE. - \param tags_array Array of tags to dedup. - \compare_function Comparison function - \return TRUE on success, FALSE on FAILURE - \sa TMTagCompareFunc -*/ -gboolean tm_tags_custom_dedup(GPtrArray *tags_array, TMTagCompareFunc compare_func); - -/* - Returns a pointer to the position of the first matching tag in a (sorted) tags array. - The passed array of tags should be already sorted by name for optimal performance. If - \c tags_array_sorted is set to FALSE, it may be unsorted but the lookup will be slower. - \param tags_array Tag array (may be sorted on name) - \param name Name of the tag to locate. - \param partial If TRUE, matches the first part of the name instead of doing exact match. - \param tags_array_sorted If TRUE, the passed \c tags_array is sorted by name so it can be - searched with binary search. Otherwise it is searched linear which is obviously slower. - \param tagCount Return location of the matched tags. -*/ TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name, - gboolean partial, gboolean tags_array_sorted, int * tagCount); + gboolean partial, gboolean tags_array_sorted, guint * tagCount); -/* - 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 0 -/* - Destroys a TMTag structure, i.e. frees all elements except the tag itself. - \param tag The TMTag structure to destroy - \sa tm_tag_free() -*/ -void tm_tag_destroy(TMTag *tag); +const TMTag *tm_get_current_tag(GPtrArray *file_tags, const gulong line, const TMTagType tag_types); -/* - Destroys all data in the tag and frees the tag structure as well. - \param tag Pointer to a TMTag structure -*/ -void tm_tag_free(gpointer 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); -/* - Adds a reference to a TMTag. - \param tag Pointer to a TMTag structure - \return the passed-in TMTag -*/ TMTag *tm_tag_ref(TMTag *tag); -/* - Returns the type of tag as a string - \param tag The tag whose type is required -*/ + +#ifdef TM_DEBUG /* various debugging functions */ + const char *tm_tag_type_name(const TMTag *tag); -/* - Returns the TMTagType given the name of the type. Reverse of tm_tag_type_name. - \param tag_name Name of the tag type -*/ TMTagType tm_tag_name_type(const char* tag_name); -/* - Prints information about a tag to the given file pointer. - \param tag The tag whose info is required. - \fp The file pointer of teh file to print the info to. -*/ void tm_tag_print(TMTag *tag, FILE *fp); -/* - Prints info about all tags in the array to the given file pointer. -*/ void tm_tags_array_print(GPtrArray *tags, FILE *fp); -/* - Returns the depth of tag scope (useful for finding tag hierarchy -*/ gint tm_tag_scope_depth(const TMTag *t); +#endif /* TM_DEBUG */ + #endif /* GEANY_PRIVATE */ #ifdef __cplusplus diff --git a/tagmanager/src/tm_tagmanager.h b/tagmanager/src/tm_tagmanager.h index 025ed9af3..450b4834d 100644 --- a/tagmanager/src/tm_tagmanager.h +++ b/tagmanager/src/tm_tagmanager.h @@ -12,27 +12,25 @@ #include "tm_tag.h" #include "tm_workspace.h" -#include "tm_work_object.h" #include "tm_source_file.h" #ifdef GEANY_PRIVATE -#include "tm_file_entry.h" #include "tm_parser.h" #endif /* GEANY_PRIVATE */ -/*! \mainpage Introduction - \section Introduction +/** @mainpage Introduction + @section Introduction TagManager is a library and a set of utility programs which can be integrated into Integrated Development Environments to provide features such as code completion, calltips, etc. Tag Manager is based on Exuberent Ctags with some added features. - \section Licence + @section Licence TagManager is free software, licenced under the GPL. You can only use it with free software (GPL compatible) projects. This is chiefly because it uses code from ctags which is under GPL. */ -/*! \file +/** @file Include this file in all programs using the tag manager library. Including this automatically includes all the necessary files, namely, tm_tag.h, tm_source_file.h and tm_workspace.h diff --git a/tagmanager/src/tm_work_object.c b/tagmanager/src/tm_work_object.c deleted file mode 100644 index e8d216adb..000000000 --- a/tagmanager/src/tm_work_object.c +++ /dev/null @@ -1,298 +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. -* -*/ - -/** - * @file tm_work_object.h - * A TMWorkObject structure is the base class for TMSourceFile. -*/ - -#include "general.h" /* must always come first */ - -#include -#include -#include -#include -#include -#include -#ifdef G_OS_WIN32 -# define VC_EXTRALEAN -# define WIN32_LEAN_AND_MEAN -# include /* for GetFullPathName */ -#endif - -#include "tm_tag.h" -#include "tm_work_object.h" - -static GPtrArray *s_work_object_subclasses = NULL; - - -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 - -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; -} - -guint tm_work_object_register(GFreeFunc free_func, TMUpdateFunc update_func, TMFindFunc find_func) -{ - TMWorkObjectClass *object_class; - if (NULL == s_work_object_subclasses) - { - s_work_object_subclasses = g_ptr_array_new(); - object_class = g_new(TMWorkObjectClass, 1); - object_class->free_func = tm_work_object_free; - object_class->update_func = NULL; - object_class->find_func = NULL; - g_ptr_array_add(s_work_object_subclasses, object_class); - } - object_class = g_new(TMWorkObjectClass, 1); - object_class->free_func = free_func; - object_class->update_func = update_func; - object_class->find_func = find_func; - g_ptr_array_add(s_work_object_subclasses, object_class); - return (s_work_object_subclasses->len - 1); -} - -gboolean tm_work_object_init(TMWorkObject *work_object, guint type, const char *file_name - , gboolean create) -{ - struct stat s; - int status; - - if (file_name != NULL) - { - if (0 != (status = g_stat(file_name, &s))) - { - if (create) - { - FILE *f; - if (NULL == (f = g_fopen(file_name, "a+"))) - { - g_warning("Unable to create file %s", file_name); - return FALSE; - } - fclose(f); - 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; - } - work_object->file_name = tm_get_real_path(file_name); - work_object->short_name = strrchr(work_object->file_name, '/'); - if (work_object->short_name) - ++ work_object->short_name; - else - work_object->short_name = work_object->file_name; - } - else - { - work_object->file_name = NULL; - work_object->short_name = NULL; - } - work_object->type = type; - work_object->parent = NULL; - work_object->analyze_time = 0; - work_object->tags_array = NULL; - return TRUE; -} - -/* -time_t tm_get_file_timestamp(const char *file_name) -{ - struct stat s; - - g_return_val_if_fail(file_name, 0); - - if (0 != g_stat(file_name, &s)) - { - return (time_t) 0; - } - else - return s.st_mtime; -} - -gboolean tm_work_object_is_changed(TMWorkObject *work_object) -{ - return (gboolean) (work_object->analyze_time < tm_get_file_timestamp(work_object->file_name)); -} -*/ - -TMWorkObject *tm_work_object_new(guint type, const char *file_name, gboolean create) -{ - TMWorkObject *work_object = g_new(TMWorkObject, 1); - if (!tm_work_object_init(work_object, type, file_name, create)) - { - g_free(work_object); - return NULL; - } - return work_object; -} - -void tm_work_object_destroy(TMWorkObject *work_object) -{ - if (work_object) - { - g_free(work_object->file_name); - if (work_object->tags_array) - g_ptr_array_free(work_object->tags_array, TRUE); - } -} - -void tm_work_object_free(gpointer work_object) -{ - if (NULL != work_object) - { - TMWorkObject *w = (TMWorkObject *) work_object; - if ((w->type > 0) && (w->type < s_work_object_subclasses->len) && - (s_work_object_subclasses->pdata[w->type] != NULL)) - { - GFreeFunc free_func = - ((TMWorkObjectClass *)s_work_object_subclasses->pdata[w->type])->free_func; - if (NULL != free_func) - free_func(work_object); - return; - } - tm_work_object_destroy(w); - g_free(work_object); - } -} - -void tm_work_object_write_tags(TMWorkObject *work_object, FILE *file, guint attrs) -{ - if (NULL != work_object->tags_array) - { - guint i; - for (i=0; i < work_object->tags_array->len; ++i) - tm_tag_write((TMTag *) g_ptr_array_index(work_object->tags_array, i) - , file, (TMTagAttrType) attrs); - } -} - -gboolean tm_work_object_update(TMWorkObject *work_object, gboolean force - , gboolean recurse, gboolean update_parent) -{ - if ((NULL != work_object) && (work_object->type > 0) && - (work_object->type < s_work_object_subclasses->len) && - (s_work_object_subclasses->pdata[work_object->type] != NULL)) - { - TMUpdateFunc update_func = - ((TMWorkObjectClass *)s_work_object_subclasses->pdata[work_object->type])->update_func; - if (NULL != update_func) - return update_func(work_object, force, recurse, update_parent); - } - return FALSE; -} - -TMWorkObject *tm_work_object_find(TMWorkObject *work_object, const char *file_name - , gboolean name_only) -{ - if ((NULL != work_object) && (work_object->type > 0) && - (work_object->type < s_work_object_subclasses->len) && - (s_work_object_subclasses->pdata[work_object->type] != NULL)) - { - TMFindFunc find_func = - ((TMWorkObjectClass *)s_work_object_subclasses->pdata[work_object->type])->find_func; - if (NULL == find_func) - { - if (name_only) - { - const char *short_name = strrchr(file_name, '/'); - if (short_name) - ++ short_name; - else - short_name = file_name; - if (0 == strcmp(work_object->short_name, short_name)) - return work_object; - else - return NULL; - } - else - { - char *path = tm_get_real_path(file_name); - int cmp = strcmp(work_object->file_name, file_name); - g_free(path); - if (0 == cmp) - return work_object; - else - return NULL; - } - } - else - return find_func(work_object, file_name, name_only); - } - return NULL; -} - -void tm_work_object_dump(const TMWorkObject *w) -{ - if (w) - { - fprintf(stderr, "%s", w->file_name); - if (w->parent) - fprintf(stderr, " <- %s\n", w->parent->file_name); - else - fprintf(stderr, " <- NULL\n"); - } -} diff --git a/tagmanager/src/tm_work_object.h b/tagmanager/src/tm_work_object.h deleted file mode 100644 index 600439c32..000000000 --- a/tagmanager/src/tm_work_object.h +++ /dev/null @@ -1,191 +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_WORK_OBJECT_H -#define TM_WORK_OBJECT_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* Macro to cast a pointer to (TMWorkObject *) */ -#define TM_WORK_OBJECT(work_object) ((TMWorkObject *) work_object) - -/*! - A TMWorkObject structure is the base class for TMSourceFile. - This struct contains data common to all work objects, namely, a file name, - time when the file was analyzed (for caching) and an array of tags which - should be populated when the object is analyzed. -*/ -typedef struct TMWorkObject -{ - guint type; /*!< The type of object. Can be a source file or a project */ - char *file_name; /*!< Full file name (inc. path) of the work object */ - char *short_name; /*!< Just the name of the file (without the path) */ - struct TMWorkObject *parent; - time_t analyze_time; /*!< UNUSED Time when the object was last analyzed */ - GPtrArray *tags_array; /*!< Tags obtained by parsing the object */ -} TMWorkObject; - - -/*! - 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); - - -/*! - Deallocates a work object and it's component structures. The user can call this - function directly since it will automatically call the correct deallocator function - of the derived class if required. - \param work_object Pointer to a work object or an object derived from it. -*/ -void tm_work_object_free(gpointer work_object); - - -#ifdef GEANY_PRIVATE - -/* Evaluates to X is X is defined, else evaluates to Y */ -#define FALLBACK(X,Y) (X)?(X):(Y) - -#define TM_OBJECT_TYPE(work_object) ((TMWorkObject *) work_object)->type /*< Type of the work object */ -#define TM_OBJECT_FILE(work_object) ((TMWorkObject *) work_object)->file_name /*< File name of the work object */ -#define TM_OBJECT_TAGS(work_object) ((TMWorkObject *) work_object)->tags_array /*< Tag array of the work object */ - -/* Prototype of the update function required to be written by all classes - derived from TMWorkObject. The function should take a pointer to the - object and a flag indicating whether the cache should be ignored, and - update the object's tag array accordingly. - \sa tm_work_object_update(), tm_workspace_update(), - tm_source_file_update(). -*/ -typedef gboolean (*TMUpdateFunc) (TMWorkObject *work_object, gboolean force - , gboolean recurse, gboolean update_parent); - -/* Prototype of the find function required to be written by all classed - derived from TMWorkObject. The function should take a pointer to the work - object and a file name and return a pointer to the work object corresponding - to the file name if the file is part of the object, and NULL otherwise. - \sa tm_work_object_find() -*/ -typedef TMWorkObject *(*TMFindFunc) (TMWorkObject *work_object, const char *file_name - , gboolean name_only); - -/* - Contains pointers to functions necessary to handle virtual function calls - correctly. To create a new object derived from TMWorkObject, you - need to write the three functions specified as the members of this class and - register your class before first use using tm_work_object_register() -*/ -typedef struct _TMWorkObjectClass -{ - GFreeFunc free_func; /* Function to free the derived object */ - TMUpdateFunc update_func; /* Function to update the derived object */ - TMFindFunc find_func; /* Function to locate contained work objects */ -} TMWorkObjectClass; - -/* - Initializes the work object structure. This function should be called by the - initialization routine of the derived classes to ensure that the base members - are initialized properly. The library user should not have to call this under - any circumstance. - \param work_object The work object to be initialized. - \param type The type of the work object obtained by registering the derived class. - \param file_name The name of the file corresponding to the work object. - \param create Whether to create the file if it doesn't exist. - \return TRUE on success, FALSE on failure. - \sa tm_work_object_register() -*/ -gboolean tm_work_object_init(TMWorkObject *work_object, guint type, const char *file_name - , gboolean create); - -/* - Initializes a new TMWorkObject structure and returns a pointer to it. You shouldn't - have to call this function. - \return NULL on failure - \sa tm_source_file_new() -*/ -TMWorkObject *tm_work_object_new(guint type, const char *file_name, gboolean create); - -/* - Utility function - Given a file name, returns the timestamp of modification. - \param file_name Full path to the file. - \return Timestamp of the file's modification time. 0 on failure. -*/ -time_t tm_get_file_timestamp(const char *file_name); - -/* - Destroys a work object's data without freeing the structure itself. It should - be called by the deallocator function of classes derived from TMWorkObject. The - user shouldn't have to call this function. -*/ -void tm_work_object_destroy(TMWorkObject *work_object); - -/* - This function should be called exactly once by all classes derived from TMWorkObject, - since it supplies a unique ID on each call and stores the functions to call for - updation and deallocation of objects of the type allocated. The user should not - have to use this function unless he/she wants to create a new class derived from - TMWorkObject. - \param free_func The function to call to free the derived object. - \param update_func The function to call to update the derived object. - \return A unique ID for the derived class. - \sa TMSourceFile -*/ -guint tm_work_object_register(GFreeFunc free_func, TMUpdateFunc update_func, TMFindFunc find_func); - -/* - Writes the tags for the work object to the file specified. - \param work_object The work object whose tags need to be written. - \param file The file to which the tags are to be written. - \param attrs The attributes to write (Can be a bitmask). -*/ -void tm_work_object_write_tags(TMWorkObject *work_object, FILE *file, guint attrs); - -/* - Updates the tags array if necessary. Automatically calls the update function - of the type to which the object belongs. - \param work_object Pointer to a work object or an object derived from it. - \param force Whether the cache is to be ignored. - \param recurse Whether to recurse into child work objects (for workspace). - \param update_parent If set to TRUE, calls the update function of the parent if required. - If you are calling this function, you should set this to TRUE. - \return TRUE on success, FALSE on failure. - \sa tm_source_file_update() -*/ -gboolean tm_work_object_update(TMWorkObject *work_object, gboolean force - , gboolean recurse, gboolean update_parent); - -/* - Finds the work object corresponding to the file name passed and returns a pointer - to it. If not found, returns NULL. This is a virtual function which automatically - calls the registered find function of teh derived object. - \sa TMFindFunc -*/ -TMWorkObject *tm_work_object_find(TMWorkObject *work_object, const char *file_name - , gboolean name_only); - -/* Dumps the contents of a work object - useful for debugging */ -void tm_work_object_dump(const TMWorkObject *w); - -#endif /* GEANY_PRIVATE */ - -#ifdef __cplusplus -} -#endif - -#endif /* TM_WORK_OBJECT_H */ diff --git a/tagmanager/src/tm_workspace.c b/tagmanager/src/tm_workspace.c index 60e7ab466..5edf1e4f3 100644 --- a/tagmanager/src/tm_workspace.c +++ b/tagmanager/src/tm_workspace.c @@ -7,16 +7,13 @@ * */ -/*! +/** * @file tm_workspace.h The TMWorkspace structure is meant to be used as a singleton to store application wide tag information. The workspace is intended to contain a list of global tags - and a set of work objects (individual files). You need not use the - workspace, though, to use tag manager, unless you need things like global tags - and a place to store all current open files. TMWorkspace - is derived from TMWorkObject. + and a set of individual source files. */ #include "general.h" @@ -33,63 +30,75 @@ #endif #include -#include "tm_tag.h" #include "tm_workspace.h" +#include "tm_tag.h" + + +/* when changing, always keep the three sort criteria below in sync */ +static TMTagAttrType workspace_tags_sort_attrs[] = +{ + tm_tag_attr_name_t, tm_tag_attr_file_t, tm_tag_attr_line_t, + tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0 +}; + +/* for file tags the file is always identical, don't use for sorting */ +static TMTagAttrType file_tags_sort_attrs[] = +{ + tm_tag_attr_name_t, tm_tag_attr_line_t, + tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0 +}; + +/* global tags don't have file/line information */ +static TMTagAttrType global_tags_sort_attrs[] = +{ + tm_tag_attr_name_t, + tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0 +}; static TMWorkspace *theWorkspace = NULL; -guint workspace_class_id = 0; + static gboolean tm_create_workspace(void) { - workspace_class_id = tm_work_object_register(tm_workspace_free, tm_workspace_update - , tm_workspace_find_object); theWorkspace = g_new(TMWorkspace, 1); - if (FALSE == tm_work_object_init(TM_WORK_OBJECT(theWorkspace), - workspace_class_id, NULL, TRUE)) - { - g_free(theWorkspace); - theWorkspace = NULL; - g_warning("Failed to initialize workspace"); - return FALSE; - } + theWorkspace->tags_array = g_ptr_array_new(); - theWorkspace->global_tags = NULL; - theWorkspace->work_objects = NULL; + theWorkspace->global_tags = g_ptr_array_new(); + theWorkspace->source_files = g_ptr_array_new(); + theWorkspace->typename_array = g_ptr_array_new(); return TRUE; } -void tm_workspace_free(gpointer workspace) + +/* Frees the workspace structure and all child source files. Use only when + exiting from the main program. +*/ +void tm_workspace_free(void) { guint i; - if (workspace != theWorkspace) - return; - #ifdef TM_DEBUG g_message("Workspace destroyed"); #endif - if (theWorkspace) - { - if (theWorkspace->work_objects) - { - for (i=0; i < theWorkspace->work_objects->len; ++i) - tm_work_object_free(theWorkspace->work_objects->pdata[i]); - g_ptr_array_free(theWorkspace->work_objects, TRUE); - } - if (theWorkspace->global_tags) - { - for (i=0; i < theWorkspace->global_tags->len; ++i) - tm_tag_unref(theWorkspace->global_tags->pdata[i]); - g_ptr_array_free(theWorkspace->global_tags, TRUE); - } - tm_work_object_destroy(TM_WORK_OBJECT(theWorkspace)); - g_free(theWorkspace); - theWorkspace = NULL; - } + for (i=0; i < theWorkspace->source_files->len; ++i) + tm_source_file_free(theWorkspace->source_files->pdata[i]); + g_ptr_array_free(theWorkspace->source_files, TRUE); + tm_tags_array_free(theWorkspace->global_tags, TRUE); + g_ptr_array_free(theWorkspace->tags_array, TRUE); + g_ptr_array_free(theWorkspace->typename_array, TRUE); + g_free(theWorkspace); + theWorkspace = NULL; } + +/* Since TMWorkspace is a singleton, you should not create multiple + workspaces, but get a pointer to the workspace whenever required. The first + time a pointer is requested, or a source file is added to the workspace, + a workspace is created. Subsequent calls to the function will return the + created workspace. +*/ const TMWorkspace *tm_get_workspace(void) { if (NULL == theWorkspace) @@ -97,63 +106,231 @@ const TMWorkspace *tm_get_workspace(void) return theWorkspace; } -gboolean tm_workspace_add_object(TMWorkObject *work_object) + +static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_array) { - /* theWorkspace should already have been created otherwise something went wrong */ - if (NULL == theWorkspace) - return FALSE; - if (NULL == theWorkspace->work_objects) - theWorkspace->work_objects = g_ptr_array_new(); - g_ptr_array_add(theWorkspace->work_objects, work_object); - work_object->parent = TM_WORK_OBJECT(theWorkspace); - return TRUE; + GPtrArray *new_tags = tm_tags_merge(*big_array, small_array, workspace_tags_sort_attrs, FALSE); + /* tags owned by TMSourceFile - free just the pointer array */ + g_ptr_array_free(*big_array, TRUE); + *big_array = new_tags; } -gboolean tm_workspace_remove_object(TMWorkObject *w, gboolean do_free, gboolean update) + +static void update_source_file(TMSourceFile *source_file, guchar* text_buf, + gsize buf_size, gboolean use_buffer, gboolean update_workspace) +{ +#ifdef TM_DEBUG + g_message("Source file updating based on source file %s", source_file->file_name); +#endif + + if (update_workspace) + { + /* tm_source_file_parse() deletes the tag objects - remove the tags from + * workspace while they exist and can be scanned */ + tm_tags_remove_file_tags(source_file, theWorkspace->tags_array); + tm_tags_remove_file_tags(source_file, theWorkspace->typename_array); + } + tm_source_file_parse(source_file, text_buf, buf_size, use_buffer); + tm_tags_sort(source_file->tags_array, file_tags_sort_attrs, FALSE, TRUE); + if (update_workspace) + { + GPtrArray *sf_typedefs; + +#ifdef TM_DEBUG + g_message("Updating workspace from source file"); +#endif + tm_workspace_merge_tags(&theWorkspace->tags_array, source_file->tags_array); + + sf_typedefs = tm_tags_extract(source_file->tags_array, TM_GLOBAL_TYPE_MASK); + tm_workspace_merge_tags(&theWorkspace->typename_array, sf_typedefs); + g_ptr_array_free(sf_typedefs, TRUE); + } +#ifdef TM_DEBUG + else + g_message("Skipping workspace update because update_workspace is %s", + update_workspace?"TRUE":"FALSE"); + +#endif +} + + +/** Adds a source file to the workspace, parses it and updates the workspace tags. + @param source_file The source file to add to the workspace. +*/ +void tm_workspace_add_source_file(TMSourceFile *source_file) +{ + g_return_if_fail(source_file != NULL); + + g_ptr_array_add(theWorkspace->source_files, source_file); + update_source_file(source_file, NULL, 0, FALSE, TRUE); +} + + +void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file) +{ + g_return_if_fail(source_file != NULL); + + g_ptr_array_add(theWorkspace->source_files, 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 detected + the first time the file is parsed if it is set to LANG_AUTO. + @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. +*/ +void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* text_buf, + gsize buf_size) +{ + update_source_file(source_file, text_buf, buf_size, TRUE, TRUE); +} + + +/** Removes a source file from the workspace if it exists. This function also removes + the tags belonging to this file from the workspace. To completely free the TMSourceFile + pointer call tm_source_file_free() on it. + @param source_file Pointer to the source file to be removed. +*/ +void tm_workspace_remove_source_file(TMSourceFile *source_file) { guint i; - if ((NULL == theWorkspace) || (NULL == theWorkspace->work_objects) - || (NULL == w)) - return FALSE; + g_return_if_fail(source_file != NULL); - for (i=0; i < theWorkspace->work_objects->len; ++i) + for (i=0; i < theWorkspace->source_files->len; ++i) { - if (theWorkspace->work_objects->pdata[i] == w) + if (theWorkspace->source_files->pdata[i] == source_file) { - if (do_free) - tm_work_object_free(w); - g_ptr_array_remove_index_fast(theWorkspace->work_objects, i); - if (update) - tm_workspace_update(TM_WORK_OBJECT(theWorkspace), TRUE, FALSE, FALSE); - return TRUE; + tm_tags_remove_file_tags(source_file, theWorkspace->tags_array); + tm_tags_remove_file_tags(source_file, theWorkspace->typename_array); + g_ptr_array_remove_index_fast(theWorkspace->source_files, i); + return; } } - - return FALSE; } -static TMTagAttrType global_tags_sort_attrs[] = -{ - tm_tag_attr_name_t, tm_tag_attr_scope_t, - tm_tag_attr_type_t, tm_tag_attr_arglist_t, 0 -}; +/* Recreates workspace tag array from all member TMSourceFile objects. Use if you + want to globally refresh the workspace. This function does not call tm_source_file_update() + which should be called before this function on source files which need to be + reparsed. +*/ +static void tm_workspace_update(void) +{ + guint i, j; + TMSourceFile *source_file; + +#ifdef TM_DEBUG + g_message("Recreating workspace tags array"); +#endif + + g_ptr_array_set_size(theWorkspace->tags_array, 0); + +#ifdef TM_DEBUG + g_message("Total %d objects", theWorkspace->source_files->len); +#endif + for (i=0; i < theWorkspace->source_files->len; ++i) + { + source_file = theWorkspace->source_files->pdata[i]; +#ifdef TM_DEBUG + g_message("Adding tags of %s", source_file->file_name); +#endif + if (source_file->tags_array->len > 0) + { + for (j = 0; j < source_file->tags_array->len; ++j) + { + g_ptr_array_add(theWorkspace->tags_array, + source_file->tags_array->pdata[j]); + } + } + } +#ifdef TM_DEBUG + g_message("Total: %d tags", theWorkspace->tags_array->len); +#endif + tm_tags_sort(theWorkspace->tags_array, workspace_tags_sort_attrs, TRUE, FALSE); + + theWorkspace->typename_array = tm_tags_extract(theWorkspace->tags_array, TM_GLOBAL_TYPE_MASK); +} + + +/** Adds multiple source files to the workspace and updates the workspace tag arrays. + This is more efficient than calling tm_workspace_add_source_file() and + tm_workspace_update_source_file() separately for each of the files. + @param source_files The source files to be added to the workspace. +*/ +void tm_workspace_add_source_files(GPtrArray *source_files) +{ + guint i; + + g_return_if_fail(source_files != NULL); + + for (i = 0; i < source_files->len; i++) + { + TMSourceFile *source_file = source_files->pdata[i]; + + tm_workspace_add_source_file_noupdate(source_file); + update_source_file(source_file, NULL, 0, FALSE, FALSE); + } + + tm_workspace_update(); +} + + +/** Removes multiple source files from the workspace and updates the workspace tag + arrays. This is more efficient than calling tm_workspace_remove_source_file() + separately for each of the files. To completely free the TMSourceFile pointers + call tm_source_file_free() on each of them. + @param source_files The source files to be removed from the workspace. +*/ +void tm_workspace_remove_source_files(GPtrArray *source_files) +{ + guint i, j; + + g_return_if_fail(source_files != NULL); + + //TODO: sort both arrays by pointer value and remove in single pass + for (i = 0; i < source_files->len; i++) + { + TMSourceFile *source_file = source_files->pdata[i]; + + for (j = 0; j < theWorkspace->source_files->len; j++) + { + if (theWorkspace->source_files->pdata[j] == source_file) + { + g_ptr_array_remove_index_fast(theWorkspace->source_files, j); + break; + } + } + } + + tm_workspace_update(); +} + + +/* Loads the global tag list from the specified file. The global tag list should + have been first created using tm_workspace_create_global_tags(). + @param tags_file The file containing global tags. + @return TRUE on success, FALSE on failure. + @see tm_workspace_create_global_tags() +*/ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode) { - gsize orig_len; guchar buf[BUFSIZ]; FILE *fp; + GPtrArray *file_tags, *new_tags; TMTag *tag; TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER; - if (NULL == theWorkspace) - return FALSE; if (NULL == (fp = g_fopen(tags_file, "r"))) return FALSE; - if (NULL == theWorkspace->global_tags) - theWorkspace->global_tags = g_ptr_array_new(); - orig_len = theWorkspace->global_tags->len; if ((NULL == fgets((gchar*) buf, BUFSIZ, fp)) || ('\0' == *buf)) { fclose(fp); @@ -188,15 +365,25 @@ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode) } rewind(fp); /* reset the file pointer, to start reading again from the beginning */ } + + file_tags = g_ptr_array_new(); while (NULL != (tag = tm_tag_new_from_file(NULL, fp, mode, format))) - g_ptr_array_add(theWorkspace->global_tags, tag); + g_ptr_array_add(file_tags, tag); fclose(fp); + + tm_tags_sort(file_tags, global_tags_sort_attrs, TRUE, TRUE); /* reorder the whole array, because tm_tags_find expects a sorted array */ - tm_tags_merge(theWorkspace->global_tags, orig_len, global_tags_sort_attrs, TRUE); + new_tags = tm_tags_merge(theWorkspace->global_tags, + file_tags, global_tags_sort_attrs, TRUE); + g_ptr_array_free(theWorkspace->global_tags, TRUE); + g_ptr_array_free(file_tags, TRUE); + theWorkspace->global_tags = new_tags; + return TRUE; } + static guint tm_file_inode_hash(gconstpointer key) { struct stat file_stat; @@ -212,6 +399,7 @@ static guint tm_file_inode_hash(gconstpointer key) } } + static void tm_move_entries_to_g_list(gpointer key, gpointer value, gpointer user_data) { GList **pp_list = (GList**)user_data; @@ -222,6 +410,7 @@ static void tm_move_entries_to_g_list(gpointer key, gpointer value, gpointer use *pp_list = g_list_prepend(*pp_list, value); } + static void write_includes_file(FILE *fp, GList *includes_files) { GList *node; @@ -230,7 +419,7 @@ static void write_includes_file(FILE *fp, GList *includes_files) while (node) { char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data); - int str_len = strlen(str); + size_t str_len = strlen(str); fwrite(str, str_len, 1, fp); g_free(str); @@ -266,6 +455,7 @@ static void append_to_temp_file(FILE *fp, GList *file_list) } } + static gchar *create_temp_file(const gchar *tpl) { gchar *name; @@ -280,6 +470,18 @@ static gchar *create_temp_file(const gchar *tpl) return name; } + +/* Creates a list of global tags. Ideally, this should be created once during + installations so that all users can use the same file. Thsi is because a full + scale global tag list can occupy several megabytes of disk space. + @param pre_process The pre-processing command. This is executed via system(), + so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'. + @param includes Include files to process. Wildcards such as '/usr/include/a*.h' + are allowed. + @param tags_file The file where the tags will be stored. + @param lang The language to use for the tags file. + @return TRUE on success, FALSE on failure. +*/ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes, int includes_count, const char *tags_file, int lang) { @@ -291,7 +493,7 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i char *command; guint i; FILE *fp; - TMWorkObject *source_file; + TMSourceFile *source_file; GPtrArray *tags_array; GHashTable *includes_files_hash; GList *includes_files = NULL; @@ -299,7 +501,7 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i gchar *temp_file2 = create_temp_file("tmp_XXXXXX.cpp"); if (NULL == temp_file || NULL == temp_file2 || - NULL == theWorkspace || NULL == (fp = g_fopen(temp_file, "w"))) + NULL == (fp = g_fopen(temp_file, "w"))) { g_free(temp_file); g_free(temp_file2); @@ -316,7 +518,7 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i if (includes[0][0] == '"') /* leading \" char for glob matching */ for(idx_inc = 0; idx_inc < includes_count; idx_inc++) { - int dirty_len = strlen(includes[idx_inc]); + size_t dirty_len = strlen(includes[idx_inc]); char *clean_path = g_malloc(dirty_len - 1); strncpy(clean_path, includes[idx_inc] + 1, dirty_len - 1); @@ -418,7 +620,8 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i temp_file2 = temp_file; temp_file = NULL; } - source_file = tm_source_file_new(temp_file2, TRUE, tm_source_file_get_lang_name(lang)); + source_file = tm_source_file_new(temp_file2, tm_source_file_get_lang_name(lang)); + update_source_file(source_file, NULL, 0, FALSE, FALSE); if (NULL == source_file) { g_unlink(temp_file2); @@ -426,7 +629,7 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i } g_unlink(temp_file2); g_free(temp_file2); - if ((NULL == source_file->tags_array) || (0 == source_file->tags_array->len)) + if (0 == source_file->tags_array->len) { tm_source_file_free(source_file); return FALSE; @@ -439,7 +642,7 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i tm_source_file_free(source_file); return FALSE; } - if (FALSE == tm_tags_sort(tags_array, global_tags_sort_attrs, TRUE)) + if (FALSE == tm_tags_sort(tags_array, global_tags_sort_attrs, TRUE, FALSE)) { tm_source_file_free(source_file); return FALSE; @@ -462,125 +665,26 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i return TRUE; } -TMWorkObject *tm_workspace_find_object(TMWorkObject *work_object, const char *file_name - , gboolean name_only) -{ - TMWorkObject *w = NULL; - guint i; - if (work_object != TM_WORK_OBJECT(theWorkspace)) - return NULL; - if ((NULL == theWorkspace) || (NULL == theWorkspace->work_objects) - || (0 == theWorkspace->work_objects->len)) - return NULL; - for (i = 0; i < theWorkspace->work_objects->len; ++i) - { - if (NULL != (w = tm_work_object_find(TM_WORK_OBJECT(theWorkspace->work_objects->pdata[i]) - , file_name, name_only))) - return w; - } - return NULL; -} - -void tm_workspace_recreate_tags_array(void) -{ - guint i, j; - TMWorkObject *w; - TMTagAttrType sort_attrs[] = { tm_tag_attr_name_t, tm_tag_attr_file_t - , tm_tag_attr_scope_t, tm_tag_attr_type_t, tm_tag_attr_arglist_t, 0}; - -#ifdef TM_DEBUG - g_message("Recreating workspace tags array"); -#endif - - if ((NULL == theWorkspace) || (NULL == theWorkspace->work_objects)) - return; - if (NULL != theWorkspace->work_object.tags_array) - g_ptr_array_set_size(theWorkspace->work_object.tags_array, 0); - else - theWorkspace->work_object.tags_array = g_ptr_array_new(); - -#ifdef TM_DEBUG - g_message("Total %d objects", theWorkspace->work_objects->len); -#endif - for (i=0; i < theWorkspace->work_objects->len; ++i) - { - w = TM_WORK_OBJECT(theWorkspace->work_objects->pdata[i]); -#ifdef TM_DEBUG - g_message("Adding tags of %s", w->file_name); -#endif - if ((NULL != w) && (NULL != w->tags_array) && (w->tags_array->len > 0)) - { - for (j = 0; j < w->tags_array->len; ++j) - { - g_ptr_array_add(theWorkspace->work_object.tags_array, - w->tags_array->pdata[j]); - } - } - } -#ifdef TM_DEBUG - g_message("Total: %d tags", theWorkspace->work_object.tags_array->len); -#endif - tm_tags_sort(theWorkspace->work_object.tags_array, sort_attrs, TRUE); -} - -gboolean tm_workspace_update(TMWorkObject *workspace, gboolean force - , gboolean recurse, gboolean UNUSED update_parent) -{ - guint i; - gboolean update_tags = force; - -#ifdef TM_DEBUG - g_message("Updating workspace"); -#endif - - if (workspace != TM_WORK_OBJECT(theWorkspace)) - return FALSE; - if (NULL == theWorkspace) - return TRUE; - if ((recurse) && (theWorkspace->work_objects)) - { - for (i=0; i < theWorkspace->work_objects->len; ++i) - { - if (TRUE == tm_work_object_update(TM_WORK_OBJECT( - theWorkspace->work_objects->pdata[i]), FALSE, TRUE, FALSE)) - update_tags = TRUE; - } - } - if (update_tags) - tm_workspace_recreate_tags_array(); - /* workspace->analyze_time = time(NULL); */ - return update_tags; -} - -void tm_workspace_dump(void) -{ - if (theWorkspace) - { -#ifdef TM_DEBUG - g_message("Dumping TagManager workspace tree.."); -#endif - tm_work_object_dump(TM_WORK_OBJECT(theWorkspace)); - if (theWorkspace->work_objects) - { - guint i; - for (i=0; i < theWorkspace->work_objects->len; ++i) - { - tm_work_object_dump(TM_WORK_OBJECT(theWorkspace->work_objects->pdata[i])); - } - } - } -} - -const GPtrArray *tm_workspace_find(const char *name, int type, TMTagAttrType *attrs - , gboolean partial, langType lang) +/* Returns all matching tags found in the workspace. + @param name The name of the tag to find. + @param type The tag types to return (TMTagType). Can be a bitmask. + @param attrs The attributes to sort and dedup on (0 terminated integer array). + @param partial Whether partial match is allowed. + @param lang Specifies the language(see the table in parsers.h) of the tags to be found, + -1 for all + @return Array of matching tags. Do not free() it since it is a static member. +*/ +const GPtrArray *tm_workspace_find(const char *name, TMTagType type, TMTagAttrType *attrs, + gboolean partial, langType lang) { static GPtrArray *tags = NULL; TMTag **matches[2]; - int len, tagCount[2]={0,0}, tagIter; + size_t len; + guint tagCount[2]={0,0}, tagIter; gint tags_lang; - if ((!theWorkspace) || (!name)) + if (!name) return NULL; len = strlen(name); if (!len) @@ -590,16 +694,14 @@ const GPtrArray *tm_workspace_find(const char *name, int type, TMTagAttrType *at else tags = g_ptr_array_new(); - matches[0] = tm_tags_find(theWorkspace->work_object.tags_array, name, partial, TRUE, + matches[0] = tm_tags_find(theWorkspace->tags_array, name, partial, TRUE, &tagCount[0]); matches[1] = tm_tags_find(theWorkspace->global_tags, name, partial, TRUE, &tagCount[1]); /* file tags */ if (matches[0] && *matches[0]) { - /* tag->atts.file.lang contains the line of the tag and - * tags->atts.entry.file->lang contains the language */ - tags_lang = (*matches[0])->atts.entry.file->lang; + tags_lang = (*matches[0])->lang; for (tagIter=0;tagIteratts.file.lang contains the language and - * tags->atts.entry.file is NULL */ - tags_lang = (*matches[1])->atts.file.lang; + tags_lang = (*matches[1])->lang; /* tags_lang_alt is used to load C global tags only once for C and C++ * lang = 1 is C++, lang = 0 is C * if we have lang 0, than accept also lang 1 for C++ */ @@ -655,34 +755,36 @@ const GPtrArray *tm_workspace_find(const char *name, int type, TMTagAttrType *at } if (attrs) - tm_tags_sort(tags, attrs, TRUE); + tm_tags_sort(tags, attrs, TRUE, FALSE); return tags; } + static gboolean match_langs(gint lang, const TMTag *tag) { - if (tag->atts.entry.file) + if (tag->file) { /* workspace tag */ - if (lang == tag->atts.entry.file->lang) + if (lang == tag->file->lang) return TRUE; } else { /* global tag */ - if (lang == tag->atts.file.lang) + if (lang == tag->lang) return TRUE; } return FALSE; } + /* scope can be NULL. * lang can be -1 */ -static int +static guint fill_find_tags_array (GPtrArray *dst, const GPtrArray *src, - const char *name, const char *scope, int type, gboolean partial, + const char *name, const char *scope, TMTagType type, gboolean partial, gint lang, gboolean first) { TMTag **match; - int tagIter, count; + guint tagIter, count; if ((!src) || (!dst) || (!name) || (!*name)) return 0; @@ -692,8 +794,8 @@ fill_find_tags_array (GPtrArray *dst, const GPtrArray *src, { for (tagIter = 0; tagIter < count; ++tagIter) { - if (! scope || (match[tagIter]->atts.entry.scope && - 0 == strcmp(match[tagIter]->atts.entry.scope, scope))) + if (! scope || (match[tagIter]->scope && + 0 == strcmp(match[tagIter]->scope, scope))) { if (type & match[tagIter]->type) if (lang == -1 || match_langs(lang, match[tagIter])) @@ -709,22 +811,28 @@ fill_find_tags_array (GPtrArray *dst, const GPtrArray *src, } -/* adapted from tm_workspace_find, Anjuta 2.02 */ +/* Returns all matching tags found in the workspace. Adapted from tm_workspace_find, Anjuta 2.02 + @param name The name of the tag to find. + @param scope The scope name of the tag to find, or NULL. + @param type The tag types to return (TMTagType). Can be a bitmask. + @param attrs The attributes to sort and dedup on (0 terminated integer array). + @param partial Whether partial match is allowed. + @param lang Specifies the language(see the table in parsers.h) of the tags to be found, + -1 for all + @return Array of matching tags. Do not free() it since it is a static member. +*/ const GPtrArray * -tm_workspace_find_scoped (const char *name, const char *scope, gint type, +tm_workspace_find_scoped (const char *name, const char *scope, TMTagType type, TMTagAttrType *attrs, gboolean partial, langType lang, gboolean global_search) { static GPtrArray *tags = NULL; - if ((!theWorkspace)) - return NULL; - if (tags) g_ptr_array_set_size (tags, 0); else tags = g_ptr_array_new (); - fill_find_tags_array (tags, theWorkspace->work_object.tags_array, + fill_find_tags_array (tags, theWorkspace->tags_array, name, scope, type, partial, lang, FALSE); if (global_search) { @@ -733,42 +841,11 @@ tm_workspace_find_scoped (const char *name, const char *scope, gint type, name, scope, type, partial, lang, FALSE); } if (attrs) - tm_tags_sort (tags, attrs, TRUE); + tm_tags_sort (tags, attrs, TRUE, FALSE); return tags; } -const TMTag * -tm_get_current_tag (GPtrArray * file_tags, const gulong line, const guint tag_types) -{ - TMTag *matching_tag = NULL; - if (file_tags && file_tags->len) - { - guint i; - gulong matching_line = 0; - - for (i = 0; (i < file_tags->len); ++i) - { - TMTag *tag = TM_TAG (file_tags->pdata[i]); - if (tag && tag->type & tag_types && - tag->atts.entry.line <= line && tag->atts.entry.line > matching_line) - { - matching_tag = tag; - matching_line = tag->atts.entry.line; - } - } - } - return matching_tag; -} - - -const TMTag * -tm_get_current_function (GPtrArray * file_tags, const gulong line) -{ - return tm_get_current_tag (file_tags, line, tm_tag_function_t | tm_tag_method_t); -} - - static int find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, const langType langJava, const char *name, @@ -781,15 +858,15 @@ find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, for (i = 0; (i < all->len); ++i) { tag = TM_TAG (all->pdata[i]); - if (no_definitions && filename && tag->atts.entry.file && + if (no_definitions && filename && tag->file && 0 != strcmp (filename, - tag->atts.entry.file->work_object.short_name)) + tag->file->short_name)) { continue; } - if (tag && tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0') + if (tag && tag->scope && tag->scope[0] != '\0') { - if (0 == strncmp (name, tag->atts.entry.scope, len)) + if (0 == strncmp (name, tag->scope, len)) { g_ptr_array_add (local, tag); } @@ -806,7 +883,7 @@ find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, for (i = 0; (i < local->len); ++i) { tag = TM_TAG (local->pdata[i]); - scope = tag->atts.entry.scope; + scope = tag->scope; if (scope && 0 == strcmp (name, scope)) { g_ptr_array_add (tags, tag); @@ -820,23 +897,23 @@ find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, { backup = s_backup[0]; s_backup[0] = '\0'; - if (0 == strcmp (name, tag->atts.entry.scope)) + if (0 == strcmp (name, tag->scope)) { j = local->len; s_backup[0] = backup; break; } } - if (tag->atts.entry.file - && tag->atts.entry.file->lang == langJava) + if (tag->file + && tag->file->lang == langJava) { - scope = strrchr (tag->atts.entry.scope, '.'); + scope = strrchr (tag->scope, '.'); if (scope) var_type = scope + 1; } else { - scope = strrchr (tag->atts.entry.scope, ':'); + scope = strrchr (tag->scope, ':'); if (scope) { var_type = scope + 1; @@ -859,8 +936,8 @@ find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, if (i == j) continue; tag2 = TM_TAG (local->pdata[j]); - if (tag2->atts.entry.var_type && - 0 == strcmp (var_type, tag2->atts.entry.var_type)) + if (tag2->var_type && + 0 == strcmp (var_type, tag2->var_type)) { break; } @@ -885,7 +962,169 @@ find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, } +/* Returns all matching members tags found in given struct/union/class name. + @param name Name of the struct/union/class. + @param file_tags A GPtrArray of edited file TMTag pointers (for search speedup, can be NULL). + @return A GPtrArray of TMTag pointers to struct/union/class members */ +const GPtrArray * +tm_workspace_find_scope_members (const GPtrArray * file_tags, const char *name, + gboolean search_global, gboolean no_definitions) +{ + static GPtrArray *tags = NULL; + GPtrArray *local = NULL; + char *new_name = (char *) name; + char *filename = NULL; + int found = 0, del = 0; + static langType langJava = -1; + TMTag *tag = NULL; + + /* FIXME */ + /* langJava = getNamedLanguage ("Java"); */ + + g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL); + + if (!tags) + tags = g_ptr_array_new (); + + while (1) + { + const GPtrArray *tags2; + guint got = 0; + TMTagType types = (tm_tag_class_t | tm_tag_namespace_t | + tm_tag_struct_t | tm_tag_typedef_t | + tm_tag_union_t | tm_tag_enum_t); + + if (file_tags) + { + g_ptr_array_set_size (tags, 0); + got = fill_find_tags_array (tags, file_tags, + new_name, NULL, types, FALSE, -1, FALSE); + } + if (got) + { + tags2 = tags; + } + else + { + TMTagAttrType attrs[] = { + tm_tag_attr_name_t, tm_tag_attr_type_t, + tm_tag_attr_none_t + }; + tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1); + } + + if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0]))) + { + if (tag->type == tm_tag_typedef_t && tag->var_type + && tag->var_type[0] != '\0') + { + char *tmp_name; + tmp_name = tag->var_type; + if (strcmp(tmp_name, new_name) == 0) { + new_name = NULL; + } + else { + new_name = tmp_name; + } + continue; + } + filename = (tag->file ? + tag->file->short_name : NULL); + if (tag->scope && tag->scope[0] != '\0') + { + del = 1; + if (tag->file && + tag->file->lang == langJava) + { + new_name = g_strdup_printf ("%s.%s", + tag->scope, + new_name); + } + else + { + new_name = g_strdup_printf ("%s::%s", + tag->scope, + new_name); + } + } + break; + } + else + { + return NULL; + } + } + + g_ptr_array_set_size (tags, 0); + + if (no_definitions && tag && tag->file) + { + local = tm_tags_extract (tag->file->tags_array, + (tm_tag_function_t | tm_tag_prototype_t | + tm_tag_member_t | tm_tag_field_t | + tm_tag_method_t | tm_tag_enumerator_t)); + } + else + { + local = tm_tags_extract (theWorkspace->tags_array, + (tm_tag_function_t | tm_tag_prototype_t | + tm_tag_member_t | tm_tag_field_t | + tm_tag_method_t | tm_tag_enumerator_t)); + } + if (local) + { + found = find_scope_members_tags (local, tags, langJava, new_name, + filename, no_definitions); + g_ptr_array_free (local, TRUE); + } + if (!found && search_global) + { + GPtrArray *global = tm_tags_extract (theWorkspace->global_tags, + (tm_tag_member_t | + tm_tag_prototype_t | + tm_tag_field_t | + tm_tag_method_t | + tm_tag_function_t | + tm_tag_enumerator_t + |tm_tag_struct_t | tm_tag_typedef_t | + tm_tag_union_t | tm_tag_enum_t)); + if (global) + { + find_scope_members_tags (global, tags, langJava, new_name, + filename, no_definitions); + g_ptr_array_free (global, TRUE); + } + } + if (del) + { + g_free (new_name); + } + + return tags; +} + + +#ifdef TM_DEBUG + +/* Dumps the workspace tree - useful for debugging */ +void tm_workspace_dump(void) +{ + guint i; + +#ifdef TM_DEBUG + g_message("Dumping TagManager workspace tree.."); +#endif + for (i=0; i < theWorkspace->source_files->len; ++i) + { + TMSourceFile *source_file = theWorkspace->source_files->pdata[i]; + fprintf(stderr, "%s", source_file->file_name); + } +} +#endif /* TM_DEBUG */ + + #if 0 + static int find_namespace_members_tags (const GPtrArray * all, GPtrArray * tags, const langType langJava, const char *name, @@ -901,16 +1140,16 @@ find_namespace_members_tags (const GPtrArray * all, GPtrArray * tags, for (i = 0; (i < all->len); ++i) { tag = TM_TAG (all->pdata[i]); - if (filename && tag->atts.entry.file && + if (filename && tag->file && 0 != strcmp (filename, - tag->atts.entry.file->work_object.short_name)) + tag->file->short_name)) { continue; } - if (tag && tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0') + if (tag && tag->scope && tag->scope[0] != '\0') { - if (0 == strncmp (name, tag->atts.entry.scope, len)) + if (0 == strncmp (name, tag->scope, len)) { g_ptr_array_add (local, tag); } @@ -923,7 +1162,7 @@ find_namespace_members_tags (const GPtrArray * all, GPtrArray * tags, for (i = 0; (i < local->len); ++i) { tag = TM_TAG (local->pdata[i]); - scope = tag->atts.entry.scope; + scope = tag->scope; /* if we wanna complete something like * namespace1:: @@ -942,7 +1181,7 @@ find_namespace_members_tags (const GPtrArray * all, GPtrArray * tags, return (int) tags->len; } -const GPtrArray * +static const GPtrArray * tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *name, gboolean search_global) { @@ -954,7 +1193,7 @@ tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *na static langType langJava = -1; TMTag *tag = NULL; - g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL); + g_return_val_if_fail (name && name[0] != '\0', NULL); if (!tags) tags = g_ptr_array_new (); @@ -962,9 +1201,10 @@ tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *na while (1) { const GPtrArray *tags2; - int got = 0, types = (tm_tag_class_t | tm_tag_namespace_t | - tm_tag_struct_t | tm_tag_typedef_t | - tm_tag_union_t | tm_tag_enum_t); + guint got = 0; + TMTagType types = (tm_tag_class_t + tm_tag_struct_t | tm_tag_typedef_t | + tm_tag_union_t | tm_tag_enum_t); if (file_tags) { @@ -989,28 +1229,28 @@ tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *na if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0]))) { - if (tag->type == tm_tag_typedef_t && tag->atts.entry.var_type - && tag->atts.entry.var_type[0] != '\0') + if (tag->type == tm_tag_typedef_t && tag->var_type + && tag->var_type[0] != '\0') { - new_name = tag->atts.entry.var_type; + new_name = tag->var_type; continue; } - filename = (tag->atts.entry.file ? - tag->atts.entry.file->work_object.short_name : NULL); - if (tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0') + filename = (tag->file ? + tag->file->short_name : NULL); + if (tag->scope && tag->scope[0] != '\0') { del = 1; - if (tag->atts.entry.file && - tag->atts.entry.file->lang == langJava) + if (tag->file && + tag->file->lang == langJava) { new_name = g_strdup_printf ("%s.%s", - tag->atts.entry.scope, + tag->scope, new_name); } else { new_name = g_strdup_printf ("%s::%s", - tag->atts.entry.scope, + tag->scope, new_name); } } @@ -1024,16 +1264,16 @@ tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *na g_ptr_array_set_size (tags, 0); - if (tag && tag->atts.entry.file) + if (tag && tag->file) { - local = tm_tags_extract (tag->atts.entry.file->work_object.tags_array, + local = tm_tags_extract (tag->file->tags_array, (tm_tag_function_t | tm_tag_field_t | tm_tag_enumerator_t | tm_tag_namespace_t | tm_tag_class_t )); } else { - local = tm_tags_extract (theWorkspace->work_object.tags_array, + local = tm_tags_extract (theWorkspace->tags_array, (tm_tag_function_t | tm_tag_prototype_t | tm_tag_member_t | tm_tag_field_t | tm_tag_enumerator_t | @@ -1086,145 +1326,12 @@ tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *na return tags; } -#endif -const GPtrArray * -tm_workspace_find_scope_members (const GPtrArray * file_tags, const char *name, - gboolean search_global, gboolean no_definitions) -{ - static GPtrArray *tags = NULL; - GPtrArray *local = NULL; - char *new_name = (char *) name; - char *filename = NULL; - int found = 0, del = 0; - static langType langJava = -1; - TMTag *tag = NULL; - /* FIXME */ - /* langJava = getNamedLanguage ("Java"); */ - - g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL); - - if (!tags) - tags = g_ptr_array_new (); - - while (1) - { - const GPtrArray *tags2; - int got = 0, types = (tm_tag_class_t | tm_tag_namespace_t | - tm_tag_struct_t | tm_tag_typedef_t | - tm_tag_union_t | tm_tag_enum_t); - - if (file_tags) - { - g_ptr_array_set_size (tags, 0); - got = fill_find_tags_array (tags, file_tags, - new_name, NULL, types, FALSE, -1, FALSE); - } - if (got) - { - tags2 = tags; - } - else - { - TMTagAttrType attrs[] = { - tm_tag_attr_name_t, tm_tag_attr_type_t, - tm_tag_attr_none_t - }; - tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1); - } - - if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0]))) - { - if (tag->type == tm_tag_typedef_t && tag->atts.entry.var_type - && tag->atts.entry.var_type[0] != '\0') - { - char *tmp_name; - tmp_name = tag->atts.entry.var_type; - if (strcmp(tmp_name, new_name) == 0) { - new_name = NULL; - } - else { - new_name = tmp_name; - } - continue; - } - filename = (tag->atts.entry.file ? - tag->atts.entry.file->work_object.short_name : NULL); - if (tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0') - { - del = 1; - if (tag->atts.entry.file && - tag->atts.entry.file->lang == langJava) - { - new_name = g_strdup_printf ("%s.%s", - tag->atts.entry.scope, - new_name); - } - else - { - new_name = g_strdup_printf ("%s::%s", - tag->atts.entry.scope, - new_name); - } - } - break; - } - else - { - return NULL; - } - } - - g_ptr_array_set_size (tags, 0); - - if (no_definitions && tag && tag->atts.entry.file) - { - local = tm_tags_extract (tag->atts.entry.file->work_object.tags_array, - (tm_tag_function_t | tm_tag_prototype_t | - tm_tag_member_t | tm_tag_field_t | - tm_tag_method_t | tm_tag_enumerator_t)); - } - else - { - local = tm_tags_extract (theWorkspace->work_object.tags_array, - (tm_tag_function_t | tm_tag_prototype_t | - tm_tag_member_t | tm_tag_field_t | - tm_tag_method_t | tm_tag_enumerator_t)); - } - if (local) - { - found = find_scope_members_tags (local, tags, langJava, new_name, - filename, no_definitions); - g_ptr_array_free (local, TRUE); - } - if (!found && search_global) - { - GPtrArray *global = tm_tags_extract (theWorkspace->global_tags, - (tm_tag_member_t | - tm_tag_prototype_t | - tm_tag_field_t | - tm_tag_method_t | - tm_tag_function_t | - tm_tag_enumerator_t - |tm_tag_struct_t | tm_tag_typedef_t | - tm_tag_union_t | tm_tag_enum_t)); - if (global) - { - find_scope_members_tags (global, tags, langJava, new_name, - filename, no_definitions); - g_ptr_array_free (global, TRUE); - } - } - if (del) - { - g_free (new_name); - } - - return tags; -} - -const GPtrArray *tm_workspace_get_parents(const gchar *name) +/* Returns a list of parent classes for the given class name + @param name Name of the class + @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */ +static const GPtrArray *tm_workspace_get_parents(const gchar *name) { static TMTagAttrType type[] = { tm_tag_attr_name_t, tm_tag_attr_none_t }; static GPtrArray *parents = NULL; @@ -1248,9 +1355,9 @@ const GPtrArray *tm_workspace_get_parents(const gchar *name) while (i < parents->len) { tag = TM_TAG(parents->pdata[i]); - if ((NULL != tag->atts.entry.inheritance) && (isalpha(tag->atts.entry.inheritance[0]))) + if ((NULL != tag->inheritance) && (isalpha(tag->inheritance[0]))) { - klasses = g_strsplit(tag->atts.entry.inheritance, ",", 10); + klasses = g_strsplit(tag->inheritance, ",", 10); for (klass = klasses; (NULL != *klass); ++ klass) { for (j=0; j < parents->len; ++j) @@ -1271,3 +1378,5 @@ const GPtrArray *tm_workspace_get_parents(const gchar *name) } return parents; } + +#endif diff --git a/tagmanager/src/tm_workspace.h b/tagmanager/src/tm_workspace.h index aca5a9e38..4283804ec 100644 --- a/tagmanager/src/tm_workspace.h +++ b/tagmanager/src/tm_workspace.h @@ -13,7 +13,7 @@ #include -#include "tm_work_object.h" +#include "tm_tag.h" #ifdef __cplusplus extern "C" @@ -21,166 +21,63 @@ extern "C" #endif -/*! The Tag Manager Workspace. This is a singleton work object containing a list - of work objects - individual source files. There is also a global tag list +/** The Tag Manager Workspace. This is a singleton object containing a list + of individual source files. There is also a global tag list which can be loaded or created. This contains global tags gleaned from /usr/include, etc. and should be used for autocompletion, calltips, etc. */ typedef struct { - TMWorkObject work_object; /*!< The parent work object */ - GPtrArray *global_tags; /*!< Global tags loaded at startup */ - GPtrArray *work_objects; /*!< An array of TMWorkObject pointers */ + GPtrArray *global_tags; /**< Global tags loaded at startup */ + GPtrArray *source_files; /**< An array of TMSourceFile pointers */ + GPtrArray *tags_array; /**< Sorted tags from all source files + (just pointers to source file tags, the tag objects are owned by the source files) */ + GPtrArray *typename_array; /* Typename tags for syntax highlighting (pointers owned by source files) */ } TMWorkspace; -/*! Adds a work object (source file) to the workspace. - \param work_object The work object to add to the workspace. - \return TRUE on success, FALSE on failure (e.g. object already exixts). -*/ -gboolean tm_workspace_add_object(TMWorkObject *work_object); -/*! Removes a member object from the workspace if it exists. - \param work_object Pointer to the work object to be removed. - \param do_free Whether the work object is to be freed as well. - \param update Whether to update workspace objects. - \return TRUE on success, FALSE on failure (e.g. the work object does not exist). -*/ -gboolean tm_workspace_remove_object(TMWorkObject *work_object, gboolean do_free, gboolean update); +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); #ifdef GEANY_PRIVATE -/* Since TMWorkspace is a singleton, you should not create multiple - workspaces, but get a pointer to the workspace whenever required. The first - time a pointer is requested, or a work object is added to the workspace, - a workspace is created. Subsequent calls to the function will return the - created workspace. -*/ const TMWorkspace *tm_get_workspace(void); -/* Given a file name, returns a pointer to the object if the object's file - name is same as the passed file name, otherwise retruns NULL. This is an - overloaded version of tm_work_object_find(). - \param work_object Pointer to the workspace. - \param file_name The name of the file to search. - \param name_only If you want to match just the name and not the full path. - \return Pointer to the work object matching the file name (NULL if not found). - \sa tm_work_object_find(). -*/ -TMWorkObject *tm_workspace_find_object(TMWorkObject *work_object, const char *file_name - ,gboolean name_only); - -/* Loads the global tag list from the specified file. The global tag list should - have been first created using tm_workspace_create_global_tags(). - \param tags_file The file containing global tags. - \return TRUE on success, FALSE on failure. - \sa tm_workspace_create_global_tags() -*/ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode); -/*gboolean tm_workspace_load_global_tags(const char *tags_file);*/ -/* Creates a list of global tags. Ideally, this should be created once during - installations so that all users can use the same file. Thsi is because a full - scale global tag list can occupy several megabytes of disk space. - \param pre_process The pre-processing command. This is executed via system(), - so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'. - \param includes Include files to process. Wildcards such as '/usr/include/a*.h' - are allowed. - \param tags_file The file where the tags will be stored. - \param lang The language to use for the tags file. - \return TRUE on success, FALSE on failure. -*/ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes, - int includes_count, const char *tags_file, int lang); + int includes_count, const char *tags_file, int lang); -/* Recreates the tag array of the workspace by collecting the tags of - all member work objects. You shouldn't have to call this directly since - this is called automatically by tm_workspace_update(). -*/ -void tm_workspace_recreate_tags_array(void); +const GPtrArray *tm_workspace_find(const char *name, TMTagType type, TMTagAttrType *attrs, + gboolean partial, langType lang); -/* Calls tm_work_object_update() for all workspace member work objects. - Use if you want to globally refresh the workspace. - \param workspace Pointer to the workspace. - \param force Whether the cache should be ignored. - \param recurse If set to TRUE, updates all children before updating the tag image. - \param update_parent This parameter is ignored for the workspace since it is at the - top of the work object hierarchy. - \sa tm_work_object_update(), tm_source_file_update() -*/ -gboolean tm_workspace_update(TMWorkObject *workspace, gboolean force - , gboolean recurse, gboolean update_parent); - -/* Dumps the workspace tree - useful for debugging */ -void tm_workspace_dump(void); - -/* Returns all matching tags found in the workspace. - \param name The name of the tag to find. - \param type The tag types to return (TMTagType). Can be a bitmask. - \param attrs The attributes to sort and dedup on (0 terminated integer array). - \param partial Whether partial match is allowed. - \param lang Specifies the language(see the table in parsers.h) of the tags to be found, - -1 for all - \return Array of matching tags. Do not free() it since it is a static member. -*/ -const GPtrArray *tm_workspace_find(const char *name, int type, TMTagAttrType *attrs - , gboolean partial, langType lang); - -/* Returns all matching tags found in the workspace. - \param name The name of the tag to find. - \param scope The scope name of the tag to find, or NULL. - \param type The tag types to return (TMTagType). Can be a bitmask. - \param attrs The attributes to sort and dedup on (0 terminated integer array). - \param partial Whether partial match is allowed. - \param lang Specifies the language(see the table in parsers.h) of the tags to be found, - -1 for all - \return Array of matching tags. Do not free() it since it is a static member. -*/ const GPtrArray * -tm_workspace_find_scoped (const char *name, const char *scope, gint type, - TMTagAttrType *attrs, gboolean partial, langType lang, gboolean global_search); +tm_workspace_find_scoped (const char *name, const char *scope, TMTagType type, + TMTagAttrType *attrs, gboolean partial, langType lang, gboolean global_search); -/* Returns all matching members tags found in given struct/union/class name. - \param name Name of the struct/union/class. - \param file_tags A GPtrArray of edited file TMTag pointers (for search speedup, can be NULL). - \return A GPtrArray of TMTag pointers to struct/union/class members */ const GPtrArray *tm_workspace_find_scope_members(const GPtrArray *file_tags, const char *scope_name, gboolean find_global, gboolean no_definitions); -const GPtrArray * -tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *name, - gboolean search_global); +void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file); -/* Returns TMTag which "own" given line - \param line Current line in edited file. - \param file_tags A GPtrArray of edited file TMTag pointers. - \param tag_types the tag types to include in the match - \return TMTag pointers to owner tag. */ -const TMTag *tm_get_current_tag(GPtrArray *file_tags, const gulong line, const guint tag_types); +void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* text_buf, + gsize buf_size); -/* Returns TMTag to function or method which "own" given line - \param line Current line in edited file. - \param file_tags A GPtrArray of edited file TMTag pointers. - \return TMTag pointers to owner function. */ -const TMTag *tm_get_current_function(GPtrArray *file_tags, const gulong line); +void tm_workspace_free(void); -/* Returns a list of parent classes for the given class name - \param name Name of the class - \return A GPtrArray of TMTag pointers (includes the TMTag for the class) */ -const GPtrArray *tm_workspace_get_parents(const gchar *name); -/* Frees the workspace structure and all child work objects. Use only when - exiting from the main program. -*/ -void tm_workspace_free(gpointer workspace); +#ifdef TM_DEBUG +void tm_workspace_dump(void); +#endif /* TM_DEBUG */ -/* Contains the id obtained by registering the TMWorkspace class as a child of - TMWorkObject. - \sa tm_work_object_register() -*/ -extern guint workspace_class_id; #endif /* GEANY_PRIVATE */ diff --git a/tests/ctags/debian_432872.f90.tags b/tests/ctags/debian_432872.f90.tags index 51d6f9b66..8e9a04b50 100644 --- a/tests/ctags/debian_432872.f90.tags +++ b/tests/ctags/debian_432872.f90.tags @@ -1,3 +1,3 @@ # format=tagmanager -FOOÌ256Ö0 FOOÌ128ÎFOOÖ0 +FOOÌ256Ö0 diff --git a/tests/ctags/keyword_explicit.cs.tags b/tests/ctags/keyword_explicit.cs.tags index c5c285ae5..d63abaef1 100644 --- a/tests/ctags/keyword_explicit.cs.tags +++ b/tests/ctags/keyword_explicit.cs.tags @@ -1,6 +1,6 @@ # format=tagmanager -DigitÌ2048Ö0 DigitÌ128Í(byte value)ÎDigitÖ0 +DigitÌ2048Ö0 MainÌ128Í()ÎTestÖ0Ïpublic void TestÌ1Ö0 operator DigitÌ128Í(byte b)ÎDigitÖ0 diff --git a/tests/ctags/keyword_implicit.cs.tags b/tests/ctags/keyword_implicit.cs.tags index 0118566c8..4ce9d5489 100644 --- a/tests/ctags/keyword_implicit.cs.tags +++ b/tests/ctags/keyword_implicit.cs.tags @@ -1,6 +1,6 @@ # format=tagmanager -DigitÌ2048Ö0 DigitÌ128Í(byte value)ÎDigitÖ0 +DigitÌ2048Ö0 MainÌ128Í()ÎTestÖ0Ïpublic void TestÌ1Ö0 operator byteÌ128Í(Digit d)ÎDigitÖ0Ïpublic implicit diff --git a/tests/ctags/keyword_names.f90.tags b/tests/ctags/keyword_names.f90.tags index 67623a12d..8824440b8 100644 --- a/tests/ctags/keyword_names.f90.tags +++ b/tests/ctags/keyword_names.f90.tags @@ -2,7 +2,7 @@ DataÌ1ÎProgramÖ0 InterfaceÌ2048Ö0 MyFuncÌ16ÎProgramÖ0 -ProgramÌ256Ö0 ProgramÌ32ÎProgramÖ0 +ProgramÌ256Ö0 contentsÌ64ÎDataÖ0 iÌ16384ÎProgramÖ0 diff --git a/tests/ctags/keyword_struct.cs.tags b/tests/ctags/keyword_struct.cs.tags index 3de676b42..aa8effe0a 100644 --- a/tests/ctags/keyword_struct.cs.tags +++ b/tests/ctags/keyword_struct.cs.tags @@ -1,7 +1,7 @@ # format=tagmanager MainÌ128Í()ÎMainClassÖ0Ïpublic void MainClassÌ1Ö0 -PointÌ2048Ö0 PointÌ128Í(int p1, int p2)ÎPointÖ0 +PointÌ2048Ö0 xÌ8ÎPointÖ0Ïint yÌ8ÎPointÖ0Ïint diff --git a/wscript b/wscript index 343b68e0a..889e8342c 100644 --- a/wscript +++ b/wscript @@ -118,10 +118,8 @@ ctags_sources = set([ 'tagmanager/ctags/vstring.c']) tagmanager_sources = set([ - 'tagmanager/src/tm_file_entry.c', 'tagmanager/src/tm_source_file.c', 'tagmanager/src/tm_tag.c', - 'tagmanager/src/tm_work_object.c', 'tagmanager/src/tm_workspace.c']) scintilla_sources = set(['scintilla/gtk/scintilla-marshal.c']) @@ -555,7 +553,7 @@ def build(bld): bld.install_files('${PREFIX}/include/geany/tagmanager', ''' tagmanager/src/tm_source_file.h tagmanager/src/tm_tag.h - tagmanager/src/tm_tagmanager.h tagmanager/src/tm_work_object.h + tagmanager/src/tm_tagmanager.h tagmanager/src/tm_workspace.h ''') # Docs base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'