[geany/geany] 8c77ac: Merge PR#356 from 'b4n/techee/tm'

Colomban Wendling git-noreply at xxxxx
Sat Nov 8 18:37:20 UTC 2014


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Sat, 08 Nov 2014 18:37:20 UTC
Commit:      8c77accfd090c794e8eb1366772f417596052eb1
             https://github.com/geany/geany/commit/8c77accfd090c794e8eb1366772f417596052eb1

Log Message:
-----------
Merge PR#356 from 'b4n/techee/tm'

Huge TagManager improvements


Modified Paths:
--------------
    doc/Doxyfile.in
    doc/Makefile.am
    plugins/geanyfunctions.h
    src/dialogs.c
    src/document.c
    src/document.h
    src/editor.c
    src/highlighting.c
    src/main.c
    src/plugindata.h
    src/plugins.c
    src/sidebar.c
    src/symbols.c
    src/symbols.h
    tagmanager/ctags/read.c
    tagmanager/ctags/read.h
    tagmanager/src/Makefile.am
    tagmanager/src/makefile.win32
    tagmanager/src/tm_file_entry.c
    tagmanager/src/tm_file_entry.h
    tagmanager/src/tm_source_file.c
    tagmanager/src/tm_source_file.h
    tagmanager/src/tm_tag.c
    tagmanager/src/tm_tag.h
    tagmanager/src/tm_tagmanager.h
    tagmanager/src/tm_work_object.c
    tagmanager/src/tm_work_object.h
    tagmanager/src/tm_workspace.c
    tagmanager/src/tm_workspace.h
    tests/ctags/debian_432872.f90.tags
    tests/ctags/keyword_explicit.cs.tags
    tests/ctags/keyword_implicit.cs.tags
    tests/ctags/keyword_names.f90.tags
    tests/ctags/keyword_struct.cs.tags
    wscript

Modified: doc/Doxyfile.in
2 lines changed, 0 insertions(+), 2 deletions(-)
===================================================================
@@ -778,8 +778,6 @@ INPUT                  = @top_srcdir@/src/ \
                          @top_srcdir@/plugins/geanyfunctions.h \
                          @top_srcdir@/tagmanager/src/tm_source_file.c \
                          @top_srcdir@/tagmanager/src/tm_source_file.h \
-                         @top_srcdir@/tagmanager/src/tm_work_object.c \
-                         @top_srcdir@/tagmanager/src/tm_work_object.h \
                          @top_srcdir@/tagmanager/src/tm_workspace.c \
                          @top_srcdir@/tagmanager/src/tm_workspace.h
 


Modified: doc/Makefile.am
1 lines changed, 0 insertions(+), 1 deletions(-)
===================================================================
@@ -98,7 +98,6 @@ doxygen_sources = \
 	$(top_srcdir)/plugins/geanyplugin.h \
 	$(top_srcdir)/plugins/geanyfunctions.h \
 	$(top_srcdir)/tagmanager/src/tm_source_file.[ch] \
-	$(top_srcdir)/tagmanager/src/tm_work_object.[ch] \
 	$(top_srcdir)/tagmanager/src/tm_workspace.[ch]
 
 Doxyfile.stamp: Doxyfile $(doxygen_sources)


Modified: plugins/geanyfunctions.h
18 lines changed, 10 insertions(+), 8 deletions(-)
===================================================================
@@ -346,14 +346,16 @@
 	geany_functions->p_tm->tm_get_real_path
 #define tm_source_file_new \
 	geany_functions->p_tm->tm_source_file_new
-#define tm_workspace_add_object \
-	geany_functions->p_tm->tm_workspace_add_object
-#define tm_source_file_update \
-	geany_functions->p_tm->tm_source_file_update
-#define tm_work_object_free \
-	geany_functions->p_tm->tm_work_object_free
-#define tm_workspace_remove_object \
-	geany_functions->p_tm->tm_workspace_remove_object
+#define tm_source_file_free \
+	geany_functions->p_tm->tm_source_file_free
+#define tm_workspace_add_source_file \
+	geany_functions->p_tm->tm_workspace_add_source_file
+#define tm_workspace_remove_source_file \
+	geany_functions->p_tm->tm_workspace_remove_source_file
+#define tm_workspace_add_source_files \
+	geany_functions->p_tm->tm_workspace_add_source_files
+#define tm_workspace_remove_source_files \
+	geany_functions->p_tm->tm_workspace_remove_source_files
 #define search_show_find_in_files_dialog \
 	geany_functions->p_search->search_show_find_in_files_dialog
 #define highlighting_get_style \


Modified: src/dialogs.c
10 lines changed, 7 insertions(+), 3 deletions(-)
===================================================================
@@ -501,9 +501,13 @@ static gboolean handle_save_as(const gchar *utf8_filename, gboolean rename_file)
 		{
 			document_rename_file(doc, utf8_filename);
 		}
-		/* create a new tm_source_file object otherwise tagmanager won't work correctly */
-		tm_workspace_remove_object(doc->tm_file, TRUE, TRUE);
-		doc->tm_file = NULL;
+		if (doc->tm_file)
+		{
+			/* create a new tm_source_file object otherwise tagmanager won't work correctly */
+			tm_workspace_remove_source_file(doc->tm_file);
+			tm_source_file_free(doc->tm_file);
+			doc->tm_file = NULL;
+		}
 	}
 	success = document_save_file_as(doc, utf8_filename);
 


Modified: src/document.c
40 lines changed, 16 insertions(+), 24 deletions(-)
===================================================================
@@ -140,7 +140,7 @@ static GtkWidget* document_show_message(GeanyDocument *doc, GtkMessageType msgty
  * string returned by @c tm_get_real_path().
  *
  * @return The matching document, or @c NULL.
- * @note This is only really useful when passing a @c TMWorkObject::file_name.
+ * @note This is only really useful when passing a @c TMSourceFile::file_name.
  * @see GeanyDocument::real_path.
  * @see document_find_by_filename().
  *
@@ -714,7 +714,11 @@ static gboolean remove_page(guint page_num)
 	g_free(doc->priv->saved_encoding.encoding);
 	g_free(doc->file_name);
 	g_free(doc->real_path);
-	tm_workspace_remove_object(doc->tm_file, TRUE, !main_status.quitting);
+	if (doc->tm_file)
+	{
+		tm_workspace_remove_source_file(doc->tm_file);
+		tm_source_file_free(doc->tm_file);
+	}
 
 	if (doc->priv->tag_tree)
 		gtk_widget_destroy(doc->priv->tag_tree);
@@ -2483,17 +2487,14 @@ void document_update_tags(GeanyDocument *doc)
 
 		/* lookup the name rather than using filetype name to support custom filetypes */
 		name = tm_source_file_get_lang_name(doc->file_type->lang);
-		doc->tm_file = tm_source_file_new(locale_filename, FALSE, name);
+		doc->tm_file = tm_source_file_new(locale_filename, name);
 		g_free(locale_filename);
 
-		if (doc->tm_file && !tm_workspace_add_object(doc->tm_file))
-		{
-			tm_work_object_free(doc->tm_file);
-			doc->tm_file = NULL;
-		}
+		if (doc->tm_file)
+			tm_workspace_add_source_file_noupdate(doc->tm_file);
 	}
 
-	/* early out if there's no work object and we couldn't create one */
+	/* early out if there's no tm source file and we couldn't create one */
 	if (doc->tm_file == NULL)
 	{
 		/* We must call sidebar_update_tag_list() before returning,
@@ -2503,20 +2504,11 @@ void document_update_tags(GeanyDocument *doc)
 		return;
 	}
 
-	len = sci_get_length(doc->editor->sci);
-	/* tm_source_file_buffer_update() below don't support 0-length data,
-	 * so just empty the tags array and leave */
-	if (len < 1)
-	{
-		tm_tags_array_free(doc->tm_file->tags_array, FALSE);
-		sidebar_update_tag_list(doc, FALSE);
-		return;
-	}
-
 	/* Parse Scintilla's buffer directly using TagManager
 	 * Note: this buffer *MUST NOT* be modified */
+	len = sci_get_length(doc->editor->sci);
 	buffer_ptr = (guchar *) scintilla_send_message(doc->editor->sci, SCI_GETCHARACTERPOINTER, 0, 0);
-	tm_source_file_buffer_update(doc->tm_file, buffer_ptr, len, TRUE);
+	tm_workspace_update_source_file_buffer(doc->tm_file, buffer_ptr, len);
 
 	sidebar_update_tag_list(doc, TRUE);
 	document_highlight_tags(doc);
@@ -2555,13 +2547,12 @@ void document_highlight_tags(GeanyDocument *doc)
 		default:
 			return; /* early out if type keywords are not supported */
 	}
-	if (!app->tm_workspace->work_object.tags_array)
+	if (!app->tm_workspace->tags_array)
 		return;
 
 	/* get any type keywords and tell scintilla about them
 	 * this will cause the type keywords to be colourized in scintilla */
-	keywords_str = symbols_find_tags_as_string(app->tm_workspace->work_object.tags_array,
-		TM_GLOBAL_TYPE_MASK, doc->file_type->lang);
+	keywords_str = symbols_find_typenames_as_string(doc->file_type->lang, FALSE);
 	if (keywords_str)
 	{
 		keywords = g_string_free(keywords_str, FALSE);
@@ -2617,7 +2608,8 @@ static void document_load_config(GeanyDocument *doc, GeanyFiletype *type,
 		/* delete tm file object to force creation of a new one */
 		if (doc->tm_file != NULL)
 		{
-			tm_workspace_remove_object(doc->tm_file, TRUE, TRUE);
+			tm_workspace_remove_source_file(doc->tm_file);
+			tm_source_file_free(doc->tm_file);
 			doc->tm_file = NULL;
 		}
 		/* load tags files before highlighting (some lexers highlight global typenames) */


Modified: src/document.h
4 lines changed, 2 insertions(+), 2 deletions(-)
===================================================================
@@ -93,8 +93,8 @@ typedef struct GeanyDocument
 	/** The filetype for this document, it's only a reference to one of the elements of the global
 	 *  filetypes array. */
 	GeanyFiletype	*file_type;
-	/** TMWorkObject object for this document, or @c NULL. */
-	TMWorkObject	*tm_file;
+	/** TMSourceFile object for this document, or @c NULL. */
+	TMSourceFile	*tm_file;
 	/** Whether this document is read-only. */
 	gboolean		 readonly;
 	/** Whether this document has been changed since it was last saved. */


Modified: src/editor.c
32 lines changed, 16 insertions(+), 16 deletions(-)
===================================================================
@@ -639,7 +639,7 @@ static void show_tags_list(GeanyEditor *editor, const GPtrArray *tags, gsize roo
 			g_string_append(words, tag->name);
 
 			/* for now, tag types don't all follow C, so just look at arglist */
-			if (!EMPTY(tag->atts.entry.arglist))
+			if (!EMPTY(tag->arglist))
 				g_string_append(words, "?2");
 			else
 				g_string_append(words, "?1");
@@ -732,10 +732,10 @@ static void autocomplete_scope(GeanyEditor *editor)
 		return;
 
 	tag = g_ptr_array_index(tags, 0);
-	name = tag->atts.entry.var_type;
+	name = tag->var_type;
 	if (name)
 	{
-		TMWorkObject *obj = editor->document->tm_file;
+		TMSourceFile *obj = editor->document->tm_file;
 
 		tags = tm_workspace_find_scope_members(obj ? obj->tags_array : NULL,
 			name, TRUE, FALSE);
@@ -1784,43 +1784,43 @@ static gint find_start_bracket(ScintillaObject *sci, gint pos)
 
 static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id)
 {
-	if (! tag->atts.entry.arglist)
+	if (! tag->arglist)
 		return FALSE;
 
 	if (ft_id != GEANY_FILETYPES_PASCAL)
 	{	/* usual calltips: "retval tagname (arglist)" */
-		if (tag->atts.entry.var_type)
+		if (tag->var_type)
 		{
 			guint i;
 
-			g_string_append(str, tag->atts.entry.var_type);
-			for (i = 0; i < tag->atts.entry.pointerOrder; i++)
+			g_string_append(str, tag->var_type);
+			for (i = 0; i < tag->pointerOrder; i++)
 			{
 				g_string_append_c(str, '*');
 			}
 			g_string_append_c(str, ' ');
 		}
-		if (tag->atts.entry.scope)
+		if (tag->scope)
 		{
 			const gchar *cosep = symbols_get_context_separator(ft_id);
 
-			g_string_append(str, tag->atts.entry.scope);
+			g_string_append(str, tag->scope);
 			g_string_append(str, cosep);
 		}
 		g_string_append(str, tag->name);
 		g_string_append_c(str, ' ');
-		g_string_append(str, tag->atts.entry.arglist);
+		g_string_append(str, tag->arglist);
 	}
 	else
 	{	/* special case Pascal calltips: "tagname (arglist) : retval" */
 		g_string_append(str, tag->name);
 		g_string_append_c(str, ' ');
-		g_string_append(str, tag->atts.entry.arglist);
+		g_string_append(str, tag->arglist);
 
-		if (!EMPTY(tag->atts.entry.var_type))
+		if (!EMPTY(tag->var_type))
 		{
 			g_string_append(str, " : ");
-			g_string_append(str, tag->atts.entry.var_type);
+			g_string_append(str, tag->var_type);
 		}
 	}
 
@@ -1831,7 +1831,7 @@ static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id
 static gchar *find_calltip(const gchar *word, GeanyFiletype *ft)
 {
 	const GPtrArray *tags;
-	const gint arg_types = tm_tag_function_t | tm_tag_prototype_t |
+	const TMTagType arg_types = tm_tag_function_t | tm_tag_prototype_t |
 		tm_tag_method_t | tm_tag_macro_with_arg_t;
 	TMTagAttrType *attrs = NULL;
 	TMTag *tag;
@@ -1862,7 +1862,7 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft)
 	{
 		tag = TM_TAG(tags->pdata[i]);
 
-		if (! tag->atts.entry.arglist)
+		if (! tag->arglist)
 			tags->pdata[i] = NULL;
 	}
 	tm_tags_prune((GPtrArray *) tags);
@@ -1873,7 +1873,7 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft)
 		TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, tm_tag_attr_scope_t,
 			tm_tag_attr_arglist_t, 0};
 
-		tm_tags_sort((GPtrArray *) tags, sort_attr, TRUE);
+		tm_tags_sort((GPtrArray *) tags, sort_attr, TRUE, FALSE);
 	}
 
 	/* if the current word has changed since last time, start with the first tag match */


Modified: src/highlighting.c
19 lines changed, 1 insertions(+), 18 deletions(-)
===================================================================
@@ -433,23 +433,6 @@ void highlighting_free_styles(void)
 }
 
 
-static GString *get_global_typenames(gint lang)
-{
-	GString *s = NULL;
-
-	if (app->tm_workspace)
-	{
-		GPtrArray *tags_array = app->tm_workspace->global_tags;
-
-		if (tags_array)
-		{
-			s = symbols_find_tags_as_string(tags_array, TM_GLOBAL_TYPE_MASK, lang);
-		}
-	}
-	return s;
-}
-
-
 static gchar*
 get_keyfile_whitespace_chars(GKeyFile *config, GKeyFile *configh)
 {
@@ -823,7 +806,7 @@ static void merge_type_keywords(ScintillaObject *sci, guint ft_id, guint keyword
 	const gchar *user_words = style_sets[ft_id].keywords[keyword_idx];
 	GString *s;
 
-	s = get_global_typenames(filetypes[ft_id]->lang);
+	s = symbols_find_typenames_as_string(filetypes[ft_id]->lang, TRUE);
 	if (G_UNLIKELY(s == NULL))
 		s = g_string_sized_new(200);
 	else


Modified: src/main.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -1289,7 +1289,7 @@ static void do_main_quit(void)
 	filetypes_free_types();
 	log_finalize();
 
-	tm_workspace_free(TM_WORK_OBJECT(app->tm_workspace));
+	tm_workspace_free();
 	g_free(app->configdir);
 	g_free(app->datadir);
 	g_free(app->docdir);


Modified: src/plugindata.h
16 lines changed, 8 insertions(+), 8 deletions(-)
===================================================================
@@ -58,7 +58,7 @@ G_BEGIN_DECLS
  * @warning You should not test for values below 200 as previously
  * @c GEANY_API_VERSION was defined as an enum value, not a macro.
  */
-#define GEANY_API_VERSION 220
+#define GEANY_API_VERSION 221
 
 /* hack to have a different ABI when built with GTK3 because loading GTK2-linked plugins
  * with GTK3-linked Geany leads to crash */
@@ -72,7 +72,7 @@ G_BEGIN_DECLS
  * Changing this forces all plugins to be recompiled before Geany can load them. */
 /* This should usually stay the same if fields are only appended, assuming only pointers to
  * structs and not structs themselves are declared by plugins. */
-#define GEANY_ABI_VERSION (69 << GEANY_ABI_SHIFT)
+#define GEANY_ABI_VERSION (70 << GEANY_ABI_SHIFT)
 
 
 /** Defines a function to check the plugin is safe to load.
@@ -599,12 +599,12 @@ SearchFuncs;
 typedef struct TagManagerFuncs
 {
 	gchar*			(*tm_get_real_path) (const gchar *file_name);
-	TMWorkObject*	(*tm_source_file_new) (const char *file_name, gboolean update, const char *name);
-	gboolean		(*tm_workspace_add_object) (TMWorkObject *work_object);
-	gboolean		(*tm_source_file_update) (TMWorkObject *source_file, gboolean force,
-					 gboolean recurse, gboolean update_parent);
-	void			(*tm_work_object_free) (gpointer work_object);
-	gboolean		(*tm_workspace_remove_object) (TMWorkObject *w, gboolean do_free, gboolean update);
+	TMSourceFile*	(*tm_source_file_new) (const char *file_name, const char *name);
+	void			(*tm_source_file_free) (TMSourceFile *source_file);
+	void			(*tm_workspace_add_source_file) (TMSourceFile *source_file);
+	void			(*tm_workspace_remove_source_file) (TMSourceFile *source_file);
+	void			(*tm_workspace_add_source_files) (GPtrArray *source_files);
+	void			(*tm_workspace_remove_source_files) (GPtrArray *source_files);
 }
 TagManagerFuncs;
 


Modified: src/plugins.c
9 lines changed, 5 insertions(+), 4 deletions(-)
===================================================================
@@ -290,10 +290,11 @@ static KeybindingFuncs keybindings_funcs = {
 static TagManagerFuncs tagmanager_funcs = {
 	&tm_get_real_path,
 	&tm_source_file_new,
-	&tm_workspace_add_object,
-	&tm_source_file_update,
-	&tm_work_object_free,
-	&tm_workspace_remove_object
+	&tm_source_file_free,
+	&tm_workspace_add_source_file,
+	&tm_workspace_remove_source_file,
+	&tm_workspace_add_source_files,
+	&tm_workspace_remove_source_files
 };
 
 static SearchFuncs search_funcs = {


Modified: src/sidebar.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -904,7 +904,7 @@ static gboolean taglist_go_to_selection(GtkTreeSelection *selection, guint keyva
 		if (! tag)
 			return FALSE;
 
-		line = tag->atts.entry.line;
+		line = tag->line;
 		if (line > 0)
 		{
 			GeanyDocument *doc = document_get_current();


Modified: src/symbols.c
129 lines changed, 60 insertions(+), 69 deletions(-)
===================================================================
@@ -64,11 +64,6 @@
 #include <stdlib.h>
 
 
-const guint TM_GLOBAL_TYPE_MASK =
-	tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t |
-	tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t;
-
-
 static gchar **html_entities = NULL;
 
 typedef struct
@@ -248,7 +243,7 @@ static void html_tags_loaded(void)
 }
 
 
-GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gint lang)
+GString *symbols_find_typenames_as_string(gint lang, gboolean global)
 {
 	guint j;
 	TMTag *tag;
@@ -256,9 +251,10 @@ GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gin
 	GPtrArray *typedefs;
 	gint tag_lang;
 
-	g_return_val_if_fail(tags_array != NULL, NULL);
-
-	typedefs = tm_tags_extract(tags_array, tag_types);
+	if (global)
+		typedefs = tm_tags_extract(app->tm_workspace->global_tags, TM_GLOBAL_TYPE_MASK);
+	else
+		typedefs = app->tm_workspace->typename_array;
 
 	if ((typedefs) && (typedefs->len > 0))
 	{
@@ -266,9 +262,7 @@ GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gin
 		for (j = 0; j < typedefs->len; ++j)
 		{
 			tag = TM_TAG(typedefs->pdata[j]);
-			/* tag->atts.file.lang contains (for some reason) the line of the tag if
-			 * tag->atts.entry.file is not NULL */
-			tag_lang = (tag->atts.entry.file) ? tag->atts.entry.file->lang : tag->atts.file.lang;
+			tag_lang = tag->lang;
 
 			/* the check for tag_lang == lang is necessary to avoid wrong type colouring of
 			 * e.g. PHP classes in C++ files
@@ -282,7 +276,7 @@ GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gin
 			}
 		}
 	}
-	if (typedefs)
+	if (typedefs && global)
 		g_ptr_array_free(typedefs, TRUE);
 	return s;
 }
@@ -333,25 +327,25 @@ GString *symbols_get_macro_list(gint lang)
 	gint tag_lang;
 	TMTag *tag;
 
-	if (app->tm_workspace->work_objects == NULL)
+	if (app->tm_workspace->source_files == NULL)
 		return NULL;
 
 	ftags = g_ptr_array_sized_new(50);
 	words = g_string_sized_new(200);
 
-	for (j = 0; j < app->tm_workspace->work_objects->len; j++)
+	for (j = 0; j < app->tm_workspace->source_files->len; j++)
 	{
 		GPtrArray *tags;
 
-		tags = tm_tags_extract(TM_WORK_OBJECT(app->tm_workspace->work_objects->pdata[j])->tags_array,
+		tags = tm_tags_extract(TM_SOURCE_FILE(app->tm_workspace->source_files->pdata[j])->tags_array,
 			tm_tag_enum_t | tm_tag_variable_t | tm_tag_macro_t | tm_tag_macro_with_arg_t);
 		if (NULL != tags)
 		{
 			for (i = 0; ((i < tags->len) && (i < editor_prefs.autocompletion_max_entries)); ++i)
 			{
 				tag = TM_TAG(tags->pdata[i]);
-				tag_lang = (tag->atts.entry.file) ?
-					tag->atts.entry.file->lang : tag->atts.file.lang;
+				tag_lang = (tag->file) ?
+					tag->file->lang : tag->lang;
 
 				if (tag_lang == lang)
 					g_ptr_array_add(ftags, (gpointer) tags->pdata[i]);
@@ -367,7 +361,7 @@ GString *symbols_get_macro_list(gint lang)
 		return NULL;
 	}
 
-	tm_tags_sort(ftags, NULL, FALSE);
+	tm_tags_sort(ftags, NULL, FALSE, FALSE);
 	for (j = 0; j < ftags->len; j++)
 	{
 		if (j > 0)
@@ -395,24 +389,21 @@ symbols_find_tm_tag(const GPtrArray *tags, const gchar *tag_name)
 }
 
 
-static TMTag *find_work_object_tag(const TMWorkObject *workobj,
+static TMTag *find_source_file_tag(GPtrArray *tags_array,
 		const gchar *tag_name, guint type)
 {
 	GPtrArray *tags;
 	TMTag *tmtag;
 
-	if (G_LIKELY(workobj != NULL))
+	tags = tm_tags_extract(tags_array, type);
+	if (tags != NULL)
 	{
-		tags = tm_tags_extract(workobj->tags_array, type);
-		if (tags != NULL)
-		{
-			tmtag = symbols_find_tm_tag(tags, tag_name);
+		tmtag = symbols_find_tm_tag(tags, tag_name);
 
-			g_ptr_array_free(tags, TRUE);
+		g_ptr_array_free(tags, TRUE);
 
-			if (tmtag != NULL)
-				return tmtag;
-		}
+		if (tmtag != NULL)
+			return tmtag;
 	}
 	return NULL;	/* not found */
 }
@@ -421,19 +412,19 @@ static TMTag *find_work_object_tag(const TMWorkObject *workobj,
 static TMTag *find_workspace_tag(const gchar *tag_name, guint type)
 {
 	guint j;
-	const GPtrArray *work_objects = NULL;
+	const GPtrArray *source_files = NULL;
 
 	if (app->tm_workspace != NULL)
-		work_objects = app->tm_workspace->work_objects;
+		source_files = app->tm_workspace->source_files;
 
-	if (work_objects != NULL)
+	if (source_files != NULL)
 	{
-		for (j = 0; j < work_objects->len; j++)
+		for (j = 0; j < source_files->len; j++)
 		{
-			TMWorkObject *workobj = TM_WORK_OBJECT(work_objects->pdata[j]);
+			TMSourceFile *srcfile = source_files->pdata[j];
 			TMTag *tmtag;
 
-			tmtag = find_work_object_tag(workobj, tag_name, type);
+			tmtag = find_source_file_tag(srcfile->tags_array, tag_name, type);
 			if (tmtag != NULL)
 				return tmtag;
 		}
@@ -468,7 +459,7 @@ static gint compare_symbol(const TMTag *tag_a, const TMTag *tag_b)
 	ret = strcmp(tag_a->name, tag_b->name);
 	if (ret == 0)
 	{
-		return tag_a->atts.entry.line - tag_b->atts.entry.line;
+		return tag_a->line - tag_b->line;
 	}
 	return ret;
 }
@@ -484,21 +475,21 @@ static gint compare_symbol_lines(gconstpointer a, gconstpointer b)
 	if (a == NULL || b == NULL)
 		return 0;
 
-	ret = tag_a->atts.entry.line - tag_b->atts.entry.line;
+	ret = tag_a->line - tag_b->line;
 	if (ret == 0)
 	{
-		if (tag_a->atts.entry.scope == NULL)
-			return -(tag_a->atts.entry.scope != tag_b->atts.entry.scope);
-		if (tag_b->atts.entry.scope == NULL)
-			return tag_a->atts.entry.scope != tag_b->atts.entry.scope;
+		if (tag_a->scope == NULL)
+			return -(tag_a->scope != tag_b->scope);
+		if (tag_b->scope == NULL)
+			return tag_a->scope != tag_b->scope;
 		else
-			return strcmp(tag_a->atts.entry.scope, tag_b->atts.entry.scope);
+			return strcmp(tag_a->scope, tag_b->scope);
 	}
 	return ret;
 }
 
 
-static GList *get_tag_list(GeanyDocument *doc, guint tag_types)
+static GList *get_tag_list(GeanyDocument *doc, TMTagType tag_types)
 {
 	GList *tag_names = NULL;
 	TMTag *tag;
@@ -1037,7 +1028,7 @@ static void hide_empty_rows(GtkTreeStore *store)
 static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboolean found_parent)
 {
 	gchar *utf8_name;
-	const gchar *scope = tag->atts.entry.scope;
+	const gchar *scope = tag->scope;
 	static GString *buffer = NULL;	/* buffer will be small so we can keep it for reuse */
 	gboolean doc_is_utf8 = FALSE;
 
@@ -1078,7 +1069,7 @@ static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboole
 	if (! doc_is_utf8)
 		g_free(utf8_name);
 
-	g_string_append_printf(buffer, " [%lu]", tag->atts.entry.line);
+	g_string_append_printf(buffer, " [%lu]", tag->line);
 
 	return buffer->str;
 }
@@ -1108,7 +1099,7 @@ static gchar *get_symbol_tooltip(GeanyDocument *doc, const TMTag *tag)
 /* find the last word in "foo::bar::blah", e.g. "blah" */
 static const gchar *get_parent_name(const TMTag *tag, filetype_id ft_id)
 {
-	const gchar *scope = tag->atts.entry.scope;
+	const gchar *scope = tag->scope;
 	const gchar *separator = symbols_get_context_separator(ft_id);
 	const gchar *str, *ptr;
 
@@ -1226,9 +1217,9 @@ static gboolean tag_equal(gconstpointer v1, gconstpointer v2)
 	const TMTag *t2 = v2;
 
 	return (t1->type == t2->type && strcmp(t1->name, t2->name) == 0 &&
-			utils_str_equal(t1->atts.entry.scope, t2->atts.entry.scope) &&
+			utils_str_equal(t1->scope, t2->scope) &&
 			/* include arglist in match to support e.g. C++ overloading */
-			utils_str_equal(t1->atts.entry.arglist, t2->atts.entry.arglist));
+			utils_str_equal(t1->arglist, t2->arglist));
 }
 
 
@@ -1242,15 +1233,15 @@ static guint tag_hash(gconstpointer v)
 	h = (h << 5) + h + tag->type;
 	for (p = tag->name; *p != '\0'; p++)
 		h = (h << 5) + h + *p;
-	if (tag->atts.entry.scope)
+	if (tag->scope)
 	{
-		for (p = tag->atts.entry.scope; *p != '\0'; p++)
+		for (p = tag->scope; *p != '\0'; p++)
 			h = (h << 5) + h + *p;
 	}
 	/* for e.g. C++ overloading */
-	if (tag->atts.entry.arglist)
+	if (tag->arglist)
 	{
-		for (p = tag->atts.entry.arglist; *p != '\0'; p++)
+		for (p = tag->arglist; *p != '\0'; p++)
 			h = (h << 5) + h + *p;
 	}
 
@@ -1348,7 +1339,7 @@ static GList *tags_table_lookup(GHashTable *table, TMTag *tag)
 		glong delta;
 		data = node->data;
 
-#define TAG_DELTA(a, b) ABS((glong) TM_TAG(a)->atts.entry.line - (glong) TM_TAG(b)->atts.entry.line)
+#define TAG_DELTA(a, b) ABS((glong) TM_TAG(a)->line - (glong) TM_TAG(b)->line)
 
 		delta = TAG_DELTA(((GList *) node->data)->data, tag);
 		for (node = node->next; node; node = node->next)
@@ -1542,7 +1533,7 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags)
 						gtk_tree_model_get(GTK_TREE_MODEL(store), node->data,
 								SYMBOLS_COLUMN_TAG, &parent_tag, -1);
 
-						d = tag->atts.entry.line - parent_tag->atts.entry.line;
+						d = tag->line - parent_tag->line;
 						if (! parent_search || (d >= 0 && d < delta))
 						{
 							delta = d;
@@ -1616,7 +1607,7 @@ static gboolean tag_has_missing_parent(const TMTag *tag, GtkTreeStore *store,
 		GtkTreeIter *iter)
 {
 	/* if the tag has a parent tag, it should be at depth >= 2 */
-	return !EMPTY(tag->atts.entry.scope) &&
+	return !EMPTY(tag->scope) &&
 		gtk_tree_store_iter_depth(store, iter) == 1;
 }
 
@@ -1666,7 +1657,7 @@ static gint tree_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
 				if (tag_a && tag_b)
 					if (!sort_by_name ||
 						(utils_str_equal(tag_a->name, tag_b->name) &&
-							utils_str_equal(tag_a->atts.entry.scope, tag_b->atts.entry.scope)))
+							utils_str_equal(tag_a->scope, tag_b->scope)))
 						cmp = compare_symbol_lines(tag_a, tag_b);
 			}
 		}
@@ -1931,8 +1922,8 @@ static void load_user_tags(filetype_id ft_id)
 
 static gboolean goto_tag(const gchar *name, gboolean definition)
 {
-	const gint forward_types = tm_tag_prototype_t | tm_tag_externvar_t;
-	guint type;
+	const TMTagType forward_types = tm_tag_prototype_t | tm_tag_externvar_t;
+	TMTagType type;
 	TMTag *tmtag = NULL;
 	GeanyDocument *old_doc = document_get_current();
 
@@ -1941,7 +1932,7 @@ static gboolean goto_tag(const gchar *name, gboolean definition)
 
 	/* first look in the current document */
 	if (old_doc != NULL && old_doc->tm_file)
-		tmtag = find_work_object_tag(old_doc->tm_file, name, type);
+		tmtag = find_source_file_tag(old_doc->tm_file->tags_array, name, type);
 
 	/* if not found, look in the workspace */
 	if (tmtag == NULL)
@@ -1950,13 +1941,13 @@ static gboolean goto_tag(const gchar *name, gboolean definition)
 	if (tmtag != NULL)
 	{
 		GeanyDocument *new_doc = document_find_by_real_path(
-			tmtag->atts.entry.file->work_object.file_name);
+			tmtag->file->file_name);
 
 		if (new_doc)
 		{
 			/* If we are already on the tag line, swap definition/declaration */
 			if (new_doc == old_doc &&
-				tmtag->atts.entry.line == (guint)sci_get_current_line(old_doc->editor->sci) + 1)
+				tmtag->line == (guint)sci_get_current_line(old_doc->editor->sci) + 1)
 			{
 				if (goto_tag(name, !definition))
 					return TRUE;
@@ -1965,10 +1956,10 @@ static gboolean goto_tag(const gchar *name, gboolean definition)
 		else
 		{
 			/* not found in opened document, should open */
-			new_doc = document_open_file(tmtag->atts.entry.file->work_object.file_name, FALSE, NULL, NULL);
+			new_doc = document_open_file(tmtag->file->file_name, FALSE, NULL, NULL);
 		}
 
-		if (navqueue_goto_line(old_doc, new_doc, tmtag->atts.entry.line))
+		if (navqueue_goto_line(old_doc, new_doc, tmtag->line))
 			return TRUE;
 	}
 	return FALSE;
@@ -2122,7 +2113,7 @@ static gint get_fold_header_after(ScintillaObject *sci, gint line)
 }
 
 
-static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_types)
+static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, TMTagType tag_types)
 {
 	gint line;
 	gint parent;
@@ -2137,7 +2128,7 @@ static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_
 
 		if (tag)
 		{
-			gint tag_line = tag->atts.entry.line - 1;
+			gint tag_line = tag->line - 1;
 			gint last_child = line + 1;
 
 			/* if it may be a false positive because we're inside a fold level not inside anything
@@ -2152,8 +2143,8 @@ static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_
 
 			if (line <= last_child)
 			{
-				if (tag->atts.entry.scope)
-					*tagname = g_strconcat(tag->atts.entry.scope,
+				if (tag->scope)
+					*tagname = g_strconcat(tag->scope,
 							symbols_get_context_separator(doc->file_type->id), tag->name, NULL);
 				else
 					*tagname = g_strdup(tag->name);
@@ -2199,7 +2190,7 @@ static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_
 }
 
 
-static gint get_current_tag_name_cached(GeanyDocument *doc, const gchar **tagname, guint tag_types)
+static gint get_current_tag_name_cached(GeanyDocument *doc, const gchar **tagname, TMTagType tag_types)
 {
 	static gint tag_line = -1;
 	static gchar *cur_tag = NULL;
@@ -2245,7 +2236,7 @@ gint symbols_get_current_function(GeanyDocument *doc, const gchar **tagname)
 /* same as symbols_get_current_function() but finds class, namespaces and more */
 gint symbols_get_current_scope(GeanyDocument *doc, const gchar **tagname)
 {
-	guint tag_types = (tm_tag_function_t | tm_tag_method_t | tm_tag_class_t |
+	TMTagType tag_types = (tm_tag_function_t | tm_tag_method_t | tm_tag_class_t |
 			tm_tag_struct_t | tm_tag_enum_t | tm_tag_union_t);
 
 	/* Python parser reports imports as namespaces which confuses the scope detection */


Modified: src/symbols.h
4 lines changed, 1 insertions(+), 3 deletions(-)
===================================================================
@@ -34,8 +34,6 @@ const gchar *symbols_get_context_separator(gint ft_id);
 
 #ifdef GEANY_PRIVATE
 
-extern const guint TM_GLOBAL_TYPE_MASK;
-
 enum
 {
 	SYMBOLS_SORT_BY_NAME,
@@ -52,7 +50,7 @@ void symbols_reload_config_files(void);
 
 void symbols_global_tags_loaded(guint file_type_idx);
 
-GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gint lang);
+GString *symbols_find_typenames_as_string(gint lang, gboolean global);
 
 const GList *symbols_get_tag_list(GeanyDocument *doc, guint tag_types);
 


Modified: tagmanager/ctags/read.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -298,7 +298,7 @@ extern boolean fileOpen (const char *const fileName, const langType language)
  * This func is NOT THREAD SAFE.
  * The user should not tamper with the buffer while this func is executing.
  */
-extern boolean bufferOpen (unsigned char *buffer, int buffer_size, 
+extern boolean bufferOpen (unsigned char *buffer, size_t buffer_size,
 			   const char *const fileName, const langType language )
 {
     boolean opened = FALSE;


Modified: tagmanager/ctags/read.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -106,7 +106,7 @@ extern void fileUngetc (int c);
 extern const unsigned char *fileReadLine (void);
 extern char *readLine (vString *const vLine, MIO *const mio);
 extern char *readSourceLine (vString *const vLine, MIOPos location, long *const pSeekValue);
-extern boolean bufferOpen (unsigned char *buffer, int buffer_size,
+extern boolean bufferOpen (unsigned char *buffer, size_t buffer_size,
 			   const char *const fileName, const langType language );
 #define bufferClose fileClose
 


Modified: tagmanager/src/Makefile.am
5 lines changed, 0 insertions(+), 5 deletions(-)
===================================================================
@@ -17,20 +17,15 @@ tagmanager_include_HEADERS = \
 	tm_source_file.h \
 	tm_tag.h \
 	tm_tagmanager.h \
-	tm_work_object.h \
 	tm_workspace.h
 
 
 libtagmanager_a_SOURCES =\
 	tm_tagmanager.h \
 	tm_parser.h \
-	tm_file_entry.h \
-	tm_file_entry.c \
 	tm_source_file.h \
 	tm_source_file.c \
 	tm_tag.h \
 	tm_tag.c \
-	tm_work_object.c \
-	tm_work_object.h \
 	tm_workspace.h \
 	tm_workspace.c


Modified: tagmanager/src/makefile.win32
3 lines changed, 1 insertions(+), 2 deletions(-)
===================================================================
@@ -44,8 +44,7 @@ all: $(COMPLIB)
 clean:
 	-$(RM) deps.mak *.o $(COMPLIB)
 
-$(COMPLIB): tm_workspace.o tm_work_object.o tm_source_file.o tm_tag.o \
-tm_file_entry.o
+$(COMPLIB): tm_workspace.o tm_source_file.o tm_tag.o 
 	$(AR) rc $@ $^
 	$(RANLIB) $@
 


Modified: tagmanager/src/tm_file_entry.c
282 lines changed, 0 insertions(+), 282 deletions(-)
===================================================================
@@ -1,282 +0,0 @@
-/*
-*
-*   Copyright (c) 2001-2002, Biswapesh Chattopadhyay
-*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License.
-*
-*/
-
-#include "general.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <dirent.h>
-#ifdef HAVE_FCNTL_H
-# include <fcntl.h>
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_FNMATCH_H
-# include <fnmatch.h>
-#endif
-#include <glib/gstdio.h>
-
-#include "tm_work_object.h"
-#include "tm_file_entry.h"
-
-
-#define FILE_NEW(T)		((T) = g_slice_new0(TMFileEntry))
-#define FILE_FREE(T)	g_slice_free(TMFileEntry, (T))
-
-
-void tm_file_entry_print(TMFileEntry *entry, gpointer UNUSED user_data
-  , guint level)
-{
-	guint i;
-
-	g_return_if_fail(entry);
-	for (i=0; i < level; ++i)
-		fputc('\t', stderr);
-	fprintf(stderr, "%s\n", entry->name);
-}
-
-gint tm_file_entry_compare(TMFileEntry *e1, TMFileEntry *e2)
-{
-	g_return_val_if_fail(e1 && e2 && e1->name && e2->name, 0);
-#ifdef TM_DEBUG
-	g_message("Comparing %s and %s", e1->name, e2->name);
-#endif
-	return strcmp(e1->name, e2->name);
-}
-
-/* TTimo - modified to handle symlinks */
-static TMFileType tm_file_entry_type(const char *path)
-{
-	struct stat s;
-
-#ifndef G_OS_WIN32
-	if (0 != g_lstat(path, &s))
-		return tm_file_unknown_t;
-#endif
-	if (S_ISDIR(s.st_mode))
-		return tm_file_dir_t;
-#ifndef G_OS_WIN32
-	else if (S_ISLNK(s.st_mode))
-		return tm_file_link_t;
-#endif
-	else if (S_ISREG(s.st_mode))
-		return tm_file_regular_t;
-	else
-		return tm_file_unknown_t;
-}
-
-static gboolean apply_filter(const char *name, GList *match, GList *unmatch
-  , gboolean ignore_hidden)
-{
-	GList *tmp;
-	gboolean matched = (match == NULL);
-	g_return_val_if_fail(name, FALSE);
-	if (ignore_hidden && ('.' == name[0]))
-		return FALSE;
-	/* TTimo - ignore .svn directories */
-	if (!strcmp(name, ".svn"))
-		return FALSE;
-	for (tmp = match; tmp; tmp = g_list_next(tmp))
-	{
-		if (0 == fnmatch((char *) tmp->data, name, 0))
-		{
-			matched = TRUE;
-			break;
-		}
-	}
-	if (!matched)
-		return FALSE;
-	for (tmp = unmatch; tmp; tmp = g_list_next(tmp))
-	{
-		if (0 == fnmatch((char *) tmp->data, name, 0))
-		{
-			return FALSE;
-		}
-	}
-	return matched;
-}
-
-TMFileEntry *tm_file_entry_new(const char *path, TMFileEntry *parent
-  , gboolean recurse, GList *file_match, GList *file_unmatch
-  , GList *dir_match, GList *dir_unmatch, gboolean ignore_hidden_files
-  , gboolean ignore_hidden_dirs)
-{
-	TMFileEntry *entry;
-	/* GList *tmp; */
-	char *real_path;
-	DIR *dir;
-	struct dirent *dir_entry;
-	TMFileEntry *new_entry;
-	char *file_name;
-	struct stat s;
-	char *entries = NULL;
-
-	g_return_val_if_fail (path != NULL, NULL);
-
-	/* TTimo - don't follow symlinks */
-	if (tm_file_entry_type(path) == tm_file_link_t)
-		return NULL;
-	real_path = tm_get_real_path(path);
-	if (!real_path)
-		return NULL;
-	FILE_NEW(entry);
-	entry->type = tm_file_entry_type(real_path);
-	entry->parent = parent;
-	entry->path = real_path;
-	entry->name = strrchr(entry->path, '/');
-	if (entry->name)
-		++ (entry->name);
-	else
-		entry->name = entry->path;
-	switch(entry->type)
-	{
-		case tm_file_unknown_t:
-			g_free(real_path);
-			FILE_FREE(entry);
-			return NULL;
-		case tm_file_link_t:
-		case tm_file_regular_t:
-			if (parent && !apply_filter(entry->name, file_match, file_unmatch
-			  , ignore_hidden_files))
-			{
-				tm_file_entry_free(entry);
-				return NULL;
-			}
-			break;
-		case tm_file_dir_t:
-			if (parent && !(recurse && apply_filter(entry->name, dir_match
-			  , dir_unmatch, ignore_hidden_dirs)))
-			{
-				tm_file_entry_free(entry);
-				return NULL;
-			}
-			file_name = g_strdup_printf("%s/CVS/Entries", entry->path);
-			if (0 == g_stat(file_name, &s))
-			{
-				if (S_ISREG(s.st_mode))
-				{
-					int fd;
-					entries = g_new(char, s.st_size + 2);
-					if (0 > (fd = open(file_name, O_RDONLY)))
-					{
-						g_free(entries);
-						entries = NULL;
-					}
-					else
-					{
-						off_t n =0;
-						off_t total_read = 1;
-						while (0 < (n = read(fd, entries + total_read, s.st_size - total_read)))
-							total_read += n;
-						entries[s.st_size] = '\0';
-						entries[0] = '\n';
-						close(fd);
-						entry->version = g_strdup("D");
-					}
-				}
-			}
-			g_free(file_name);
-			if (NULL != (dir = opendir(entry->path)))
-			{
-				while (NULL != (dir_entry = readdir(dir)))
-				{
-					if ((0 == strcmp(dir_entry->d_name, "."))
-					  || (0 == strcmp(dir_entry->d_name, "..")))
-						continue;
-					file_name = g_strdup_printf("%s/%s", entry->path, dir_entry->d_name);
-					new_entry = tm_file_entry_new(file_name, entry, recurse
-					  , file_match, file_unmatch, dir_match, dir_unmatch
-			  		  , ignore_hidden_files, ignore_hidden_dirs);
-					g_free(file_name);
-					if (new_entry)
-					{
-						if (entries)
-						{
-							char *str = g_strconcat("\n/", new_entry->name, "/", NULL);
-							char *name_pos = strstr(entries, str);
-							if (NULL != name_pos)
-							{
-								int len = strlen(str);
-								char *version_pos = strchr(name_pos + len, '/');
-								if (NULL != version_pos)
-								{
-									*version_pos = '\0';
-									new_entry->version = g_strdup(name_pos + len);
-									*version_pos = '/';
-								}
-							}
-							g_free(str);
-						}
-						entry->children = g_slist_prepend(entry->children, new_entry);
-					}
-				}
-			}
-			closedir(dir);
-			entry->children = g_slist_sort(entry->children, (GCompareFunc) tm_file_entry_compare);
-			g_free(entries);
-			break;
-	}
-	return entry;
-}
-
-void tm_file_entry_free(gpointer entry)
-{
-	if (entry)
-	{
-		TMFileEntry *file_entry = TM_FILE_ENTRY(entry);
-		if (file_entry->children)
-		{
-			GSList *tmp;
-			for (tmp = file_entry->children; tmp; tmp = g_slist_next(tmp))
-				tm_file_entry_free(tmp->data);
-			g_slist_free(file_entry->children);
-		}
-		g_free(file_entry->version);
-		g_free(file_entry->path);
-		FILE_FREE(file_entry);
-	}
-}
-
-void tm_file_entry_foreach(TMFileEntry *entry, TMFileEntryFunc func
-  , gpointer user_data, guint level, gboolean reverse)
-{
-	g_return_if_fail (entry != NULL);
-	g_return_if_fail (func != NULL);
-
-	if ((reverse) && (entry->children))
-	{
-		GSList *tmp;
-		for (tmp = entry->children; tmp; tmp = g_slist_next(tmp))
-			tm_file_entry_foreach(TM_FILE_ENTRY(tmp->data), func
-			  , user_data, level + 1, TRUE);
-	}
-	func(entry, user_data, level);
-	if ((!reverse) && (entry->children))
-	{
-		GSList *tmp;
-		for (tmp = entry->children; tmp; tmp = g_slist_next(tmp))
-			tm_file_entry_foreach(TM_FILE_ENTRY(tmp->data), func
-			  , user_data, level + 1, FALSE);
-	}
-}
-
-GList *tm_file_entry_list(TMFileEntry *entry, GList *files)
-{
-	GSList *tmp;
-	files = g_list_prepend(files, g_strdup(entry->path));
-	for (tmp = entry->children; tmp; tmp = g_slist_next(tmp))
-	{
-		files = tm_file_entry_list((TMFileEntry *) tmp->data, files);
-	}
-	if (!files)
-		files = g_list_reverse(files);
-	return files;
-}


Modified: tagmanager/src/tm_file_entry.h
124 lines changed, 0 insertions(+), 124 deletions(-)
===================================================================
@@ -1,124 +0,0 @@
-/*
-*
-*   Copyright (c) 2001-2002, Biswapesh Chattopadhyay
-*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License.
-*
-*/
-
-#ifndef TM_FILE_ENTRY_H
-#define TM_FILE_ENTRY_H
-
-#include <glib.h>
-
-/* \file
-The TMFileEntry structure and associated functions can be used
-for file and directory traversal. The following example demonstrates
-the use of TMFileEntry.
-\include tm_file_tree_dump.c
-*/
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/* Enum defining file types */
-typedef enum
-{
-	tm_file_unknown_t, /* Unknown file type/file does not exist */
-	tm_file_regular_t, /* Is a regular file */
-	tm_file_dir_t, /* Is a directory */
-	tm_file_link_t /* Is a symbolic link */
-} TMFileType;
-
-/*
- This example demonstrates the use of TMFileEntry and associated functions
- for managing file hierarchies in a project.
-
- \example tm_file_tree_dump.c
-*/
-
-/* This structure stores the file tree */
-typedef struct _TMFileEntry
-{
-	TMFileType type; /* File type */
-	char *path; /* Full path to the file (incl. dir and name) */
-	char *name; /* Just the file name (path minus the directory) */
-	char *version; /* CVS version in case there is a CVS entry for this file */
-	struct _TMFileEntry *parent; /* The parent directory file entry */
-	GSList *children; /* List of children (for directory) */
-} TMFileEntry;
-
-/* Prototype for the function that gets called for each entry when
- tm_file_entry_foreach() is called.
-*/
-typedef void (*TMFileEntryFunc) (TMFileEntry *entry, gpointer user_data
-  , guint level);
-
-/* Convinience casting macro */
-#define TM_FILE_ENTRY(E) ((TMFileEntry *) (E))
-
-/* Function that compares two file entries on name and returns the
- difference
-*/
-gint tm_file_entry_compare(TMFileEntry *e1, TMFileEntry *e2);
-
-/* Function to create a new file entry structure.
-\param path Path to the file for which the entry is to be created.
-\param parent Should be NULL for the first call. Since the function calls
- itself recursively, this parameter is required to build the hierarchy.
-\param recurse Whether the entry is to be recursively scanned (for
- directories only)
-\param file_match List of file name patterns to match. If set to NULL,
- all files match. You can use wildcards like '*.c'. See the example program
- for usage.
-\param file_unmatch Opposite of file_match. All files matching any of the patterns
- supplied are ignored. If set to NULL, no file is ignored.
-\param dir_match List of directory name patterns to match. If set to NULL,
- all directories match. You can use wildcards like '\.*'.
-\param dir_unmatch Opposite of dir_match. All directories matching any of the
- patterns supplied are ignored. If set to NULL, no directory is ignored.
-\param ignore_hidden_files If set to TRUE, hidden files (starting with '.')
- are ignored.
-\param ignore_hidden_dirs If set to TRUE, hidden directories (starting with '.')
- are ignored.
-\return Populated TMFileEntry structure on success, NULL on failure.
-*/
-TMFileEntry *tm_file_entry_new(const char *path, TMFileEntry *parent
-  , gboolean recurse, GList *file_match, GList *file_unmatch
-  , GList *dir_match, GList *dir_unmatch, gboolean ignore_hidden_files
-  , gboolean ignore_hidden_dirs);
-
-/* Frees a TMFileEntry structure. Freeing is recursive, so all child
- entries are freed as well.
-\param entry The TMFileEntry structure to be freed.
-*/
-void tm_file_entry_free(gpointer entry);
-
-/* This will call the function func() for each file entry.
-\param entry The root file entry.
-\param func The function to be called.
-\param user_data Extra information to be passed to the function.
-\param level The recursion level. You should set this to 0 initially.
-\param reverse If set to TRUE, traversal is in reverse hierarchical order
-*/
-void tm_file_entry_foreach(TMFileEntry *entry, TMFileEntryFunc func
-  , gpointer user_data, guint level, gboolean reverse);
-
-/* This is a sample function to show the use of tm_file_entry_foreach().
-*/
-void tm_file_entry_print(TMFileEntry *entry, gpointer user_data, guint level);
-
-/* Creates a list of path names from a TMFileEntry structure.
-\param entry The TMFileEntry structure.
-\files Current file list. Should be NULL.
-*/
-GList *tm_file_entry_list(TMFileEntry *entry, GList *files);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* TM_FILE_ENTRY_H */


Modified: tagmanager/src/tm_source_file.c
449 lines changed, 256 insertions(+), 193 deletions(-)
===================================================================
@@ -18,37 +18,159 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <glib/gstdio.h>
+#ifdef G_OS_WIN32
+# define VC_EXTRALEAN
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h> /* for GetFullPathName */
+#endif
 
 #include "general.h"
 #include "entry.h"
 #include "parse.h"
 #include "read.h"
-#define LIBCTAGS_DEFINED
-#include "tm_work_object.h"
 
+#define LIBCTAGS_DEFINED
 #include "tm_source_file.h"
 #include "tm_tag.h"
 
 
-guint source_file_class_id = 0;
 static TMSourceFile *current_source_file = NULL;
 
-gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name
-  , gboolean update, const char* name)
+static int get_path_max(const char *path)
+{
+#ifdef PATH_MAX
+	return PATH_MAX;
+#else
+	int path_max = pathconf(path, _PC_PATH_MAX);
+	if (path_max <= 0)
+		path_max = 4096;
+	return path_max;
+#endif
+}
+
+
+#ifdef G_OS_WIN32
+/* realpath implementation for Windows found at http://bugzilla.gnome.org/show_bug.cgi?id=342926
+ * this one is better than e.g. liberty's lrealpath because this one uses Win32 API and works
+ * with special chars within the filename */
+static char *realpath (const char *pathname, char *resolved_path)
+{
+	int size;
+
+	if (resolved_path != NULL)
+	{
+		int path_max = get_path_max(pathname);
+		size = GetFullPathNameA (pathname, path_max, resolved_path, NULL);
+		if (size > path_max)
+			return NULL;
+		else
+			return resolved_path;
+	}
+	else
+	{
+		size = GetFullPathNameA (pathname, 0, NULL, NULL);
+		resolved_path = g_new0 (char, size);
+		GetFullPathNameA (pathname, size, resolved_path, NULL);
+		return resolved_path;
+	}
+}
+#endif
+
+/**
+ Given a file name, returns a newly allocated string containing the realpath()
+ of the file.
+ @param file_name The original file_name
+ @return A newly allocated string containing the real path to the file. NULL if none is available.
+*/
+gchar *tm_get_real_path(const gchar *file_name)
+{
+	if (file_name)
+	{
+		gsize len = get_path_max(file_name) + 1;
+		gchar *path = g_malloc0(len);
+
+		if (realpath(file_name, path))
+			return path;
+		else
+			g_free(path);
+	}
+	return NULL;
+}
+
+/*
+ This function is registered into the ctags parser when a file is parsed for
+ the first time. The function is then called by the ctags parser each time
+ it finds a new tag. You should not have to use this function.
+ @see tm_source_file_parse()
+*/
+static int tm_source_file_tags(const tagEntryInfo *tag)
+{
+	if (NULL == current_source_file)
+		return 0;
+	g_ptr_array_add(current_source_file->tags_array, 
+		tm_tag_new(current_source_file, tag));
+	return TRUE;
+}
+
+/* Set the argument list of tag identified by its name */
+static void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist)
+{
+	guint count;
+	TMTag **tags, *tag;
+
+	if (NULL == arglist ||
+		NULL == tag_name ||
+		NULL == current_source_file)
+	{
+		return;
+	}
+
+	tags = tm_tags_find(current_source_file->tags_array, tag_name, FALSE, FALSE,
+			&count);
+	if (tags != NULL && count == 1)
+	{
+		tag = tags[0];
+		g_free(tag->arglist);
+		tag->arglist = g_strdup(arglist);
+	}
+}
+
+/* Initializes a TMSourceFile structure from a file name. */
+static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name, 
+	const char* name)
 {
-	if (0 == source_file_class_id)
-		source_file_class_id = tm_work_object_register(tm_source_file_free
-		  , tm_source_file_update, NULL);
+	struct stat s;
+	int status;
 
 #ifdef TM_DEBUG
 	g_message("Source File init: %s", file_name);
 #endif
 
-	if (FALSE == tm_work_object_init(&(source_file->work_object),
-		  source_file_class_id, file_name, FALSE))
-		return FALSE;
+	if (file_name != NULL)
+	{
+		status = g_stat(file_name, &s);
+		if (0 != status)
+		{
+			/* g_warning("Unable to stat %s", file_name);*/
+			return FALSE;
+		}
+		if (!S_ISREG(s.st_mode))
+		{
+			g_warning("%s: Not a regular file", file_name);
+			return FALSE;
+		}
+		source_file->file_name = tm_get_real_path(file_name);
+		source_file->short_name = strrchr(source_file->file_name, '/');
+		if (source_file->short_name)
+			++ source_file->short_name;
+		else
+			source_file->short_name = source_file->file_name;
+	}
+
+	source_file->tags_array = g_ptr_array_new();
 
-	source_file->inactive = FALSE;
 	if (NULL == LanguageTable)
 	{
 		initializeParsing();
@@ -64,37 +186,46 @@ gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name
 	else
 		source_file->lang = getNamedLanguage(name);
 
-	if (update)
-		tm_source_file_update(TM_WORK_OBJECT(source_file), TRUE, FALSE, FALSE);
 	return TRUE;
 }
 
-TMWorkObject *tm_source_file_new(const char *file_name, gboolean update, const char *name)
+/** Initializes a TMSourceFile structure and returns a pointer to it. The
+ * TMSourceFile has to be added to TMWorkspace to start its parsing.
+ * @param file_name The file name.
+ * @param name Name of the used programming language, NULL for autodetection.
+ * @return The created unparsed TMSourceFile object.
+ * */
+TMSourceFile *tm_source_file_new(const char *file_name, const char *name)
 {
 	TMSourceFile *source_file = g_new(TMSourceFile, 1);
-	if (TRUE != tm_source_file_init(source_file, file_name, update, name))
+	if (TRUE != tm_source_file_init(source_file, file_name, name))
 	{
 		g_free(source_file);
 		return NULL;
 	}
-	return (TMWorkObject *) source_file;
+	return source_file;
 }
 
-void tm_source_file_destroy(TMSourceFile *source_file)
+/* Destroys the contents of the source file. Note that the tags are owned by the
+ source file and are also destroyed when the source file is destroyed. If pointers
+ to these tags are used elsewhere, then those tag arrays should be rebuilt.
+*/
+static void tm_source_file_destroy(TMSourceFile *source_file)
 {
 #ifdef TM_DEBUG
-	g_message("Destroying source file: %s", source_file->work_object.file_name);
+	g_message("Destroying source file: %s", source_file->file_name);
 #endif
 
-	if (NULL != TM_WORK_OBJECT(source_file)->tags_array)
-	{
-		tm_tags_array_free(TM_WORK_OBJECT(source_file)->tags_array, TRUE);
-		TM_WORK_OBJECT(source_file)->tags_array = NULL;
-	}
-	tm_work_object_destroy(&(source_file->work_object));
+	g_free(source_file->file_name);
+	tm_tags_array_free(source_file->tags_array, TRUE);
+	source_file->tags_array = NULL;
 }
 
-void tm_source_file_free(gpointer source_file)
+/** Frees a TMSourceFile structure, including all contents. Before calling this
+ function the TMSourceFile has to be removed from the TMWorkspace. 
+ @param source_file The source file to free.
+*/
+void tm_source_file_free(TMSourceFile *source_file)
 {
 	if (NULL != source_file)
 	{
@@ -103,79 +234,63 @@ void tm_source_file_free(gpointer source_file)
 	}
 }
 
-gboolean tm_source_file_parse(TMSourceFile *source_file)
+/* Parses the text-buffer or source file and regenarates the tags.
+ @param source_file The source file to parse
+ @param text_buf The text buffer to parse
+ @param buf_size The size of text_buf.
+ @param use_buffer Set FALSE to ignore the buffer and parse the file directly or
+ TRUE to parse the buffer and ignore the file content.
+ @return TRUE on success, FALSE on failure
+*/
+gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size,
+	gboolean use_buffer)
 {
 	const char *file_name;
-	gboolean status = TRUE;
-	int passCount = 0;
+	gboolean retry = TRUE;
+	gboolean parse_file = FALSE;
+	gboolean free_buf = FALSE;
 
-	if ((NULL == source_file) || (NULL == source_file->work_object.file_name))
+	if ((NULL == source_file) || (NULL == source_file->file_name))
 	{
 		g_warning("Attempt to parse NULL file");
 		return FALSE;
 	}
-
-	file_name = source_file->work_object.file_name;
-	if (NULL == LanguageTable)
+	
+	if (source_file->lang == LANG_IGNORE)
 	{
-		initializeParsing();
-		installLanguageMapDefaults();
-		if (NULL == TagEntryFunction)
-			TagEntryFunction = tm_source_file_tags;
-		if (NULL == TagEntrySetArglistFunction)
-			TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
+		tm_tags_array_free(source_file->tags_array, FALSE);
+		return FALSE;
 	}
-	current_source_file = source_file;
-
-	if (LANG_AUTO == source_file->lang)
-		source_file->lang = getFileLanguage (file_name);
-
-	if (source_file->lang < 0 || ! LanguageTable [source_file->lang]->enabled)
-		return status;
-
-	while ((TRUE == status) && (passCount < 3))
+	
+	file_name = source_file->file_name;
+	
+	if (!use_buffer)
 	{
-		if (source_file->work_object.tags_array)
-			tm_tags_array_free(source_file->work_object.tags_array, FALSE);
-		if (fileOpen (file_name, source_file->lang))
+		struct stat s;
+		
+		/* load file to memory and parse it from memory unless the file is too big */
+		if (g_stat(file_name, &s) != 0 || s.st_size > 10*1024*1024)
+			parse_file = TRUE;
+		else
 		{
-			if (LanguageTable [source_file->lang]->parser != NULL)
+			if (!g_file_get_contents(file_name, (gchar**)&text_buf, (gsize*)&buf_size, NULL))
 			{
-				LanguageTable [source_file->lang]->parser ();
-				fileClose ();
-				break;
+				g_warning("Unable to open %s", file_name);
+				return FALSE;
 			}
-			else if (LanguageTable [source_file->lang]->parser2 != NULL)
-				status = LanguageTable [source_file->lang]->parser2 (passCount);
-			fileClose ();
+			free_buf = TRUE;
 		}
-		else
-		{
-			g_warning("%s: Unable to open %s", G_STRFUNC, file_name);
-			return FALSE;
-		}
-		++ passCount;
-	}
-	return status;
-}
-
-gboolean tm_source_file_buffer_parse(TMSourceFile *source_file, guchar* text_buf, gint buf_size)
-{
-	const char *file_name;
-	gboolean status = TRUE;
-
-	if ((NULL == source_file) || (NULL == source_file->work_object.file_name))
-	{
-		g_warning("Attempt to parse NULL file");
-		return FALSE;
 	}
 
-	if ((NULL == text_buf) || (0 == buf_size))
+	if (!parse_file && (NULL == text_buf || 0 == buf_size))
 	{
-		g_warning("Attempt to parse a NULL text buffer");
+		/* Empty buffer, "parse" by setting empty tag array */
+		tm_tags_array_free(source_file->tags_array, FALSE);
+		if (free_buf)
+			g_free(text_buf);
+		return TRUE;
 	}
 
-	file_name = source_file->work_object.file_name;
 	if (NULL == LanguageTable)
 	{
 		initializeParsing();
@@ -202,21 +317,34 @@ gboolean tm_source_file_buffer_parse(TMSourceFile *source_file, guchar* text_buf
 	}
 	else
 	{
-		int passCount = 0;
-		while ((TRUE == status) && (passCount < 3))
+		guint passCount = 0;
+		while (retry && passCount < 3)
 		{
-			if (source_file->work_object.tags_array)
-				tm_tags_array_free(source_file->work_object.tags_array, FALSE);
-			if (bufferOpen (text_buf, buf_size, file_name, source_file->lang))
+			tm_tags_array_free(source_file->tags_array, FALSE);
+			if (parse_file && fileOpen (file_name, source_file->lang))
+			{
+				if (LanguageTable [source_file->lang]->parser != NULL)
+				{
+					LanguageTable [source_file->lang]->parser ();
+					fileClose ();
+					retry = FALSE;
+					break;
+				}
+				else if (LanguageTable [source_file->lang]->parser2 != NULL)
+					retry = LanguageTable [source_file->lang]->parser2 (passCount);
+				fileClose ();
+			}
+			else if (!parse_file && bufferOpen (text_buf, buf_size, file_name, source_file->lang))
 			{
 				if (LanguageTable [source_file->lang]->parser != NULL)
 				{
 					LanguageTable [source_file->lang]->parser ();
 					bufferClose ();
+					retry = FALSE;
 					break;
 				}
 				else if (LanguageTable [source_file->lang]->parser2 != NULL)
-					status = LanguageTable [source_file->lang]->parser2 (passCount);
+					retry = LanguageTable [source_file->lang]->parser2 (passCount);
 				bufferClose ();
 			}
 			else
@@ -226,103 +354,66 @@ gboolean tm_source_file_buffer_parse(TMSourceFile *source_file, guchar* text_buf
 			}
 			++ passCount;
 		}
-		return TRUE;
-	}
-	return status;
-}
-
-void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist)
-{
-	int count;
-	TMTag **tags, *tag;
-
-	if (NULL == arglist ||
-		NULL == tag_name ||
-		NULL == current_source_file ||
-		NULL == current_source_file->work_object.tags_array)
-	{
-		return;
-	}
-
-	tags = tm_tags_find(current_source_file->work_object.tags_array, tag_name, FALSE, FALSE,
-			&count);
-	if (tags != NULL && count == 1)
-	{
-		tag = tags[0];
-		g_free(tag->atts.entry.arglist);
-		tag->atts.entry.arglist = g_strdup(arglist);
 	}
+	
+	if (free_buf)
+		g_free(text_buf);
+	return !retry;
 }
 
-int tm_source_file_tags(const tagEntryInfo *tag)
-{
-	if (NULL == current_source_file)
-		return 0;
-	if (NULL == current_source_file->work_object.tags_array)
-		current_source_file->work_object.tags_array = g_ptr_array_new();
-	g_ptr_array_add(current_source_file->work_object.tags_array,
-	  tm_tag_new(current_source_file, tag));
-	return TRUE;
-}
-
-gboolean tm_source_file_update(TMWorkObject *source_file, gboolean force
-  , gboolean UNUSED recurse, gboolean update_parent)
+/* Gets the name associated with the language index.
+ @param lang The language index.
+ @return The language name, or NULL.
+*/
+const gchar *tm_source_file_get_lang_name(gint lang)
 {
-	if (force)
+	if (NULL == LanguageTable)
 	{
-		tm_source_file_parse(TM_SOURCE_FILE(source_file));
-		tm_tags_sort(source_file->tags_array, NULL, FALSE);
-		/* source_file->analyze_time = tm_get_file_timestamp(source_file->file_name); */
-		if ((source_file->parent) && update_parent)
-		{
-			tm_work_object_update(source_file->parent, TRUE, FALSE, TRUE);
-		}
-		return TRUE;
-	}
-	else {
-#ifdef	TM_DEBUG
-		g_message ("no parsing of %s has been done", source_file->file_name);
-#endif
-		return FALSE;
+		initializeParsing();
+		installLanguageMapDefaults();
+		if (NULL == TagEntryFunction)
+			TagEntryFunction = tm_source_file_tags;
+		if (NULL == TagEntrySetArglistFunction)
+			TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
 	}
+	return getLanguageName(lang);
 }
 
-
-gboolean tm_source_file_buffer_update(TMWorkObject *source_file, guchar* text_buf,
-			gint buf_size, gboolean update_parent)
+/* Gets the language index for \a name.
+ @param name The language name.
+ @return The language index, or -2.
+*/
+gint tm_source_file_get_named_lang(const gchar *name)
 {
-#ifdef TM_DEBUG
-	g_message("Buffer updating based on source file %s", source_file->file_name);
-#endif
-
-	tm_source_file_buffer_parse (TM_SOURCE_FILE(source_file), text_buf, buf_size);
-	tm_tags_sort(source_file->tags_array, NULL, FALSE);
-	/* source_file->analyze_time = time(NULL); */
-	if ((source_file->parent) && update_parent)
+	if (NULL == LanguageTable)
 	{
-#ifdef TM_DEBUG
-		g_message("Updating parent from buffer..");
-#endif
-		tm_work_object_update(source_file->parent, TRUE, FALSE, TRUE);
+		initializeParsing();
+		installLanguageMapDefaults();
+		if (NULL == TagEntryFunction)
+			TagEntryFunction = tm_source_file_tags;
+		if (NULL == TagEntrySetArglistFunction)
+			TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
 	}
-#ifdef TM_DEBUG
-	else
-		g_message("Skipping parent update because parent is %s and update_parent is %s"
-		  , source_file->parent?"NOT NULL":"NULL", update_parent?"TRUE":"FALSE");
-
-#endif
-	return TRUE;
+	return getNamedLanguage(name);
 }
 
-
-gboolean tm_source_file_write(TMWorkObject *source_file, FILE *fp, guint attrs)
+#if 0
+/*
+ Writes all tags of a source file (including the file tag itself) to the passed
+ file pointer.
+ @param source_file The source file to write.
+ @param fp The file pointer to write to.
+ @param attrs The attributes to write.
+ @return TRUE on success, FALSE on failure.
+*/
+static gboolean tm_source_file_write(TMSourceFile *source_file, FILE *fp, guint attrs)
 {
 	TMTag *tag;
 	guint i;
 
 	if (NULL != source_file)
 	{
-		if (NULL != (tag = tm_tag_new(TM_SOURCE_FILE(source_file), NULL)))
+		if (NULL != (tag = tm_tag_new(source_file, NULL)))
 		{
 			tm_tag_write(tag, fp, tm_tag_attr_max_t);
 			tm_tag_unref(tag);
@@ -339,32 +430,4 @@ gboolean tm_source_file_write(TMWorkObject *source_file, FILE *fp, guint attrs)
 	}
 	return TRUE;
 }
-
-const gchar *tm_source_file_get_lang_name(gint lang)
-{
-	if (NULL == LanguageTable)
-	{
-		initializeParsing();
-		installLanguageMapDefaults();
-		if (NULL == TagEntryFunction)
-			TagEntryFunction = tm_source_file_tags;
-		if (NULL == TagEntrySetArglistFunction)
-			TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
-	}
-	return getLanguageName(lang);
-}
-
-gint tm_source_file_get_named_lang(const gchar *name)
-{
-	if (NULL == LanguageTable)
-	{
-		initializeParsing();
-		installLanguageMapDefaults();
-		if (NULL == TagEntryFunction)
-			TagEntryFunction = tm_source_file_tags;
-		if (NULL == TagEntrySetArglistFunction)
-			TagEntrySetArglistFunction = tm_source_file_set_tag_arglist;
-	}
-	return getNamedLanguage(name);
-}
-
+#endif


Modified: tagmanager/src/tm_source_file.h
136 lines changed, 18 insertions(+), 118 deletions(-)
===================================================================
@@ -10,8 +10,8 @@
 #ifndef TM_SOURCE_FILE_H
 #define TM_SOURCE_FILE_H
 
-#include "tm_work_object.h"
-
+#include <stdio.h>
+#include <glib.h>
 
 #ifndef LIBCTAGS_DEFINED
 typedef int langType;
@@ -27,138 +27,38 @@ extern "C"
 #endif
 
 /* Casts a pointer to a pointer to a TMSourceFile structure */
-#define TM_SOURCE_FILE(work_object) ((TMSourceFile *) work_object)
+#define TM_SOURCE_FILE(source_file) ((TMSourceFile *) source_file)
+
+/* Evaluates to X is X is defined, else evaluates to Y */
+#define FALLBACK(X,Y) (X)?(X):(Y)
 
-/* Checks whether the object is a TMSourceFile */
-#define IS_TM_SOURCE_FILE(source_file) (((TMWorkObject *) (source_file))->type \
-			== source_file_class_id)
 
-/*!
- The TMSourceFile structure is derived from TMWorkObject and contains all it's
- attributes, plus an integer representing the language of the file.
+/**
+ The TMSourceFile structure represents the source file and its tags in the tag manager.
 */
 typedef struct
 {
-	TMWorkObject work_object; /*!< The base work object */
-	langType lang; /*!< Programming language used */
-	gboolean inactive; /*!< Whether this file should be scanned for tags */
+	langType lang; /**< Programming language used */
+	char *file_name; /**< Full file name (inc. path) */
+	char *short_name; /**< Just the name of the file (without the path) */
+	GPtrArray *tags_array; /**< Sorted tag array obtained by parsing the object */
 } TMSourceFile;
 
-/*! Initializes a TMSourceFile structure and returns a pointer to it. 
- * \param file_name The file name.
- * \param update Update the tag array of the file.
- * \param name Name of the used programming language, NULL for autodetection.
- * \return The created TMSourceFile object.
- * */
-TMWorkObject *tm_source_file_new(const char *file_name, gboolean update, const char *name);
-
-/*! Updates the source file by reparsing if the modification time is greater
- than the timestamp in the structure, or if force is TRUE. The tags array and
- the tags themselves are destroyed and re-created, hence any other tag arrays
- pointing to these tags should be rebuilt as well. All sorting information is
- also lost. The language parameter is automatically set the first time the file
- is parsed.
- \param source_file The source file to update.
- \param force Ignored. The source file is always updated.
- \param recurse This parameter is ignored for source files and is only there for consistency.
- \param update_parent If set to TRUE, sends an update signal to parent if required. You should
- always set this to TRUE if you are calling this function directly.
- \return TRUE if the file was parsed, FALSE otherwise.
- \sa tm_work_object_update(), tm_workspace_update()
-*/
-gboolean tm_source_file_update(TMWorkObject *source_file, gboolean force
-  , gboolean recurse, gboolean update_parent);
-
-
-#ifdef GEANY_PRIVATE
-
-/* Initializes a TMSourceFile structure from a file name. */
-gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name,
-							 gboolean update, const char *name);
-
-/* Destroys the contents of the source file. Note that the tags are owned by the
- source file and are also destroyed when the source file is destroyed. If pointers
- to these tags are used elsewhere, then those tag arrays should be rebuilt.
-*/
-void tm_source_file_destroy(TMSourceFile *source_file);
-
-/* Frees a TMSourceFile structure, including all contents */
-void tm_source_file_free(gpointer source_file);
-
-/* Updates the source file by reparsing the text-buffer passed as parameter.
- Ctags will use a parsing based on buffer instead of on files.
- You should call this function when you don't want a previous saving of the file
- you're editing. It's useful for a "real-time" updating of the tags.
- The tags array and the tags themselves are destroyed and re-created, hence any
- other tag arrays pointing to these tags should be rebuilt as well. All sorting
- information is also lost. The language parameter is automatically set the first
- time the file is parsed.
- \param source_file The source file to update with a buffer.
- \param text_buf A text buffer. The user should take care of allocate and free it after
- the use here.
- \param buf_size The size of text_buf.
- \param update_parent If set to TRUE, sends an update signal to parent if required. You should
- always set this to TRUE if you are calling this function directly.
- \return TRUE if the file was parsed, FALSE otherwise.
- \sa tm_work_object_update(), tm_workspace_update()
-*/
-gboolean tm_source_file_buffer_update(TMWorkObject *source_file, guchar* text_buf,
-			gint buf_size, gboolean update_parent);
+TMSourceFile *tm_source_file_new(const char *file_name, const char *name);
 
-/* Parses the source file and regenarates the tags.
- \param source_file The source file to parse
- \return TRUE on success, FALSE on failure
- \sa tm_source_file_update()
-*/
-gboolean tm_source_file_parse(TMSourceFile *source_file);
-
-/* Parses the text-buffer and regenarates the tags.
- \param source_file The source file to parse
- \param text_buf The text buffer to parse
- \param buf_size The size of text_buf.
- \return TRUE on success, FALSE on failure
- \sa tm_source_file_update()
-*/
-gboolean tm_source_file_buffer_parse(TMSourceFile *source_file, guchar* text_buf, gint buf_size);
+void tm_source_file_free(TMSourceFile *source_file);
 
-/*
- This function is registered into the ctags parser when a file is parsed for
- the first time. The function is then called by the ctags parser each time
- it finds a new tag. You should not have to use this function.
- \sa tm_source_file_parse()
-*/
-int tm_source_file_tags(const tagEntryInfo *tag);
+gchar *tm_get_real_path(const gchar *file_name);
 
-/*
- Writes all tags of a source file (including the file tag itself) to the passed
- file pointer.
- \param source_file The source file to write.
- \param fp The file pointer to write to.
- \param attrs The attributes to write.
- \return TRUE on success, FALSE on failure.
-*/
-gboolean tm_source_file_write(TMWorkObject *source_file, FILE *fp, guint attrs);
 
-/* Contains the id obtained by registering the TMSourceFile class as a child of
- TMWorkObject.
- \sa tm_work_object_register()
-*/
-extern guint source_file_class_id;
+#ifdef GEANY_PRIVATE
 
-/* Gets the name associated with the language index.
- \param lang The language index.
- \return The language name, or NULL.
-*/
 const gchar *tm_source_file_get_lang_name(gint lang);
 
-/* Gets the language index for \a name.
- \param name The language name.
- \return The language index, or -2.
-*/
 gint tm_source_file_get_named_lang(const gchar *name);
 
-/* Set the argument list of tag identified by its name */
-void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist);
+gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize buf_size,
+	gboolean use_buffer);
 
 #endif /* GEANY_PRIVATE */
 


Modified: tagmanager/src/tm_tag.c
804 lines changed, 517 insertions(+), 287 deletions(-)
===================================================================
@@ -78,6 +78,11 @@ static void log_tag_free(TMTag *tag)
 #endif /* DEBUG_TAG_REFS */
 
 
+const TMTagType TM_GLOBAL_TYPE_MASK =
+	tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t |
+	tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t;
+
+
 /* Note: To preserve binary compatibility, it is very important
 	that you only *append* to this list ! */
 enum
@@ -95,12 +100,15 @@ enum
 	TA_ACCESS,
 	TA_IMPL,
 	TA_LANG,
-	TA_INACTIVE,
+	TA_INACTIVE, /* Obsolete */
 	TA_POINTER
 };
 
-static guint *s_sort_attrs = NULL;
-static gboolean s_partial = FALSE;
+typedef struct
+{
+	guint *sort_attrs;
+	gboolean partial;
+} TMSortOptions;
 
 static const char *s_tag_type_names[] = {
 	"class", /* classes */
@@ -123,7 +131,7 @@ static const char *s_tag_type_names[] = {
 	"other" /* Other tag type (non C/C++/Java) */
 };
 
-static int s_tag_types[] = {
+static TMTagType s_tag_types[] = {
 	tm_tag_class_t,
 	tm_tag_enum_t,
 	tm_tag_enumerator_t,
@@ -144,6 +152,7 @@ static int s_tag_types[] = {
 	tm_tag_other_t
 };
 
+/* Gets the GType for a TMTag */
 GType tm_tag_get_type(void)
 {
 	static GType gtype = 0;
@@ -155,7 +164,7 @@ GType tm_tag_get_type(void)
 	return gtype;
 }
 
-static int get_tag_type(const char *tag_name)
+static TMTagType get_tag_type(const char *tag_name)
 {
 	unsigned int i;
 	int cmp;
@@ -208,56 +217,59 @@ static char get_tag_access(const char *access)
 	return TAG_ACCESS_UNKNOWN;
 }
 
-gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry)
+/*
+ Initializes a TMTag structure with information from a tagEntryInfo struct
+ used by the ctags parsers. Note that the TMTag structure must be malloc()ed
+ before calling this function. This function is called by tm_tag_new() - you
+ should not need to call this directly.
+ @param tag The TMTag structure to initialize
+ @param file Pointer to a TMSourceFile struct (it is assigned to the file member)
+ @param tag_entry Tag information gathered by the ctags parser
+ @return TRUE on success, FALSE on failure
+*/
+static gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry)
 {
 	tag->refcount = 1;
 	if (NULL == tag_entry)
-	{
-		/* This is a file tag */
-		if (NULL == file)
-			return FALSE;
-		else
-		{
-			tag->name = g_strdup(file->work_object.file_name);
-			tag->type = tm_tag_file_t;
-			/* tag->atts.file.timestamp = file->work_object.analyze_time; */
-			tag->atts.file.lang = file->lang;
-			tag->atts.file.inactive = FALSE;
-			return TRUE;
-		}
-	}
-	else
-	{
-		/* This is a normal tag entry */
-		if (NULL == tag_entry->name)
-			return FALSE;
-		tag->name = g_strdup(tag_entry->name);
-		tag->type = get_tag_type(tag_entry->kindName);
-		tag->atts.entry.local = tag_entry->isFileScope;
-		tag->atts.entry.pointerOrder = 0;	/* backward compatibility (use var_type instead) */
-		tag->atts.entry.line = tag_entry->lineNumber;
-		if (NULL != tag_entry->extensionFields.arglist)
-			tag->atts.entry.arglist = g_strdup(tag_entry->extensionFields.arglist);
-		if ((NULL != tag_entry->extensionFields.scope[1]) &&
-			(isalpha(tag_entry->extensionFields.scope[1][0]) ||
-			 tag_entry->extensionFields.scope[1][0] == '_' ||
-			 tag_entry->extensionFields.scope[1][0] == '$'))
-			tag->atts.entry.scope = g_strdup(tag_entry->extensionFields.scope[1]);
-		if (tag_entry->extensionFields.inheritance != NULL)
-			tag->atts.entry.inheritance = g_strdup(tag_entry->extensionFields.inheritance);
-		if (tag_entry->extensionFields.varType != NULL)
-			tag->atts.entry.var_type = g_strdup(tag_entry->extensionFields.varType);
-		if (tag_entry->extensionFields.access != NULL)
-			tag->atts.entry.access = get_tag_access(tag_entry->extensionFields.access);
-		if (tag_entry->extensionFields.implementation != NULL)
-			tag->atts.entry.impl = get_tag_impl(tag_entry->extensionFields.implementation);
-		if ((tm_tag_macro_t == tag->type) && (NULL != tag->atts.entry.arglist))
-			tag->type = tm_tag_macro_with_arg_t;
-		tag->atts.entry.file = file;
-		return TRUE;
-	}
+		return FALSE;
+		
+	/* This is a normal tag entry */
+	if (NULL == tag_entry->name)
+		return FALSE;
+	tag->name = g_strdup(tag_entry->name);
+	tag->type = get_tag_type(tag_entry->kindName);
+	tag->local = tag_entry->isFileScope;
+	tag->pointerOrder = 0;	/* backward compatibility (use var_type instead) */
+	tag->line = tag_entry->lineNumber;
+	if (NULL != tag_entry->extensionFields.arglist)
+		tag->arglist = g_strdup(tag_entry->extensionFields.arglist);
+	if ((NULL != tag_entry->extensionFields.scope[1]) &&
+		(isalpha(tag_entry->extensionFields.scope[1][0]) ||
+		 tag_entry->extensionFields.scope[1][0] == '_' ||
+		 tag_entry->extensionFields.scope[1][0] == '$'))
+		tag->scope = g_strdup(tag_entry->extensionFields.scope[1]);
+	if (tag_entry->extensionFields.inheritance != NULL)
+		tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance);
+	if (tag_entry->extensionFields.varType != NULL)
+		tag->var_type = g_strdup(tag_entry->extensionFields.varType);
+	if (tag_entry->extensionFields.access != NULL)
+		tag->access = get_tag_access(tag_entry->extensionFields.access);
+	if (tag_entry->extensionFields.implementation != NULL)
+		tag->impl = get_tag_impl(tag_entry->extensionFields.implementation);
+	if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist))
+		tag->type = tm_tag_macro_with_arg_t;
+	tag->file = file;
+	tag->lang = file->lang;
+	return TRUE;
 }
 
+/*
+ Creates a new tag structure from a tagEntryInfo pointer and a TMSOurceFile pointer
+ and returns a pointer to it.
+ @param file - Pointer to the TMSourceFile structure containing the tag
+ @param tag_entry Contains tag information generated by ctags
+ @return the new TMTag structure. This should be free()-ed using tm_tag_free()
+*/
 TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry)
 {
 	TMTag *tag;
@@ -271,7 +283,15 @@ TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry)
 	return tag;
 }
 
-gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
+/*
+ Initializes an already malloc()ed TMTag structure by reading a tag entry
+ line from a file. The structure should be allocated beforehand.
+ @param tag The TMTag structure to populate
+ @param file The TMSourceFile struct (assigned to the file member)
+ @param fp FILE pointer from where the tag line is read
+ @return TRUE on success, FALSE on FAILURE
+*/
+static gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
 {
 	guchar buf[BUFSIZ];
 	guchar *start, *end;
@@ -301,61 +321,40 @@ gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
 			switch (*start)
 			{
 				case TA_LINE:
-					tag->atts.entry.line = atol((gchar*)start + 1);
+					tag->line = atol((gchar*)start + 1);
 					break;
 				case TA_LOCAL:
-					tag->atts.entry.local = atoi((gchar*)start + 1);
+					tag->local = atoi((gchar*)start + 1);
 					break;
 				case TA_TYPE:
 					tag->type = (TMTagType) atoi((gchar*)start + 1);
 					break;
 				case TA_ARGLIST:
-					tag->atts.entry.arglist = g_strdup((gchar*)start + 1);
+					tag->arglist = g_strdup((gchar*)start + 1);
 					break;
 				case TA_SCOPE:
-					tag->atts.entry.scope = g_strdup((gchar*)start + 1);
+					tag->scope = g_strdup((gchar*)start + 1);
 					break;
 				case TA_POINTER:
-					tag->atts.entry.pointerOrder = atoi((gchar*)start + 1);
+					tag->pointerOrder = atoi((gchar*)start + 1);
 					break;
 				case TA_VARTYPE:
-					tag->atts.entry.var_type = g_strdup((gchar*)start + 1);
+					tag->var_type = g_strdup((gchar*)start + 1);
 					break;
 				case TA_INHERITS:
-					tag->atts.entry.inheritance = g_strdup((gchar*)start + 1);
+					tag->inheritance = g_strdup((gchar*)start + 1);
 					break;
-				case TA_TIME:
-					if (tm_tag_file_t != tag->type)
-					{
-						g_warning("Got time attribute for non-file tag %s", tag->name);
-						return FALSE;
-					}
-					else
-						tag->atts.file.timestamp = atol((gchar*)start + 1);
+				case TA_TIME:  /* Obsolete */
 					break;
-				case TA_LANG:
-					if (tm_tag_file_t != tag->type)
-					{
-						g_warning("Got lang attribute for non-file tag %s", tag->name);
-						return FALSE;
-					}
-					else
-						tag->atts.file.lang = atoi((gchar*)start + 1);
+				case TA_LANG:  /* Obsolete */
 					break;
-				case TA_INACTIVE:
-					if (tm_tag_file_t != tag->type)
-					{
-						g_warning("Got inactive attribute for non-file tag %s", tag->name);
-						return FALSE;
-					}
-					else
-						tag->atts.file.inactive = (gboolean) atoi((gchar*)start + 1);
+				case TA_INACTIVE:  /* Obsolete */
 					break;
 				case TA_ACCESS:
-					tag->atts.entry.access = *(start + 1);
+					tag->access = (char) *(start + 1);
 					break;
 				case TA_IMPL:
-					tag->atts.entry.impl = *(start + 1);
+					tag->impl = (char) *(start + 1);
 					break;
 				default:
 #ifdef GEANY_DEBUG
@@ -368,14 +367,13 @@ gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
 	}
 	if (NULL == tag->name)
 		return FALSE;
-	if (tm_tag_file_t != tag->type)
-		tag->atts.entry.file = file;
+	tag->file = file;
 	return TRUE;
 }
 
 /* alternative parser for Pascal and LaTeX global tags files with the following format
  * tagname|return value|arglist|description\n */
-gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
+static gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
 {
 	guchar buf[BUFSIZ];
 	guchar *start, *end;
@@ -404,8 +402,8 @@ gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
 
 			if (field_len >= 1) tag->name = g_strdup(fields[0]);
 			else tag->name = NULL;
-			if (field_len >= 2 && fields[1] != NULL) tag->atts.entry.var_type = g_strdup(fields[1]);
-			if (field_len >= 3 && fields[2] != NULL) tag->atts.entry.arglist = g_strdup(fields[2]);
+			if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]);
+			if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]);
 			tag->type = tm_tag_prototype_t;
 			g_strfreev(fields);
 		}
@@ -413,13 +411,15 @@ gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
 
 	if (NULL == tag->name)
 		return FALSE;
-	if (tm_tag_file_t != tag->type)
-		tag->atts.entry.file = file;
+	tag->file = file;
 	return TRUE;
 }
 
-/* Reads ctags format (http://ctags.sourceforge.net/FORMAT) */
-gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
+/*
+ Same as tm_tag_init_from_file(), but parsing CTags tag file format
+ (http://ctags.sourceforge.net/FORMAT) 
+*/
+static gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
 {
 	gchar buf[BUFSIZ];
 	gchar *p, *tab;
@@ -460,7 +460,7 @@ gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
 		}
 	}
 	else /* assume a line */
-		tag->atts.entry.line = atol(p);
+		tag->line = atol(p);
 	tab = strstr(p, ";\"");
 	/* read extension fields */
 	if (tab)
@@ -501,7 +501,7 @@ gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
 						case 'c': tag->type = tm_tag_class_t; break;
 						case 'd': tag->type = tm_tag_macro_t; break;
 						case 'e': tag->type = tm_tag_enumerator_t; break;
-						case 'F': tag->type = tm_tag_file_t; break;
+						case 'F': tag->type = tm_tag_other_t; break;  /* Obsolete */
 						case 'f': tag->type = tm_tag_function_t; break;
 						case 'g': tag->type = tm_tag_enum_t; break;
 						case 'I': tag->type = tm_tag_class_t; break;
@@ -527,39 +527,42 @@ gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
 			}
 			else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */
 			{
-				g_free(tag->atts.entry.inheritance);
-				tag->atts.entry.inheritance = g_strdup(value);
+				g_free(tag->inheritance);
+				tag->inheritance = g_strdup(value);
 			}
 			else if (0 == strcmp(key, "implementation")) /* implementation limit */
-				tag->atts.entry.impl = get_tag_impl(value);
+				tag->impl = get_tag_impl(value);
 			else if (0 == strcmp(key, "line")) /* line */
-				tag->atts.entry.line = atol(value);
+				tag->line = atol(value);
 			else if (0 == strcmp(key, "access")) /* access */
-				tag->atts.entry.access = get_tag_access(value);
+				tag->access = get_tag_access(value);
 			else if (0 == strcmp(key, "class") ||
 					 0 == strcmp(key, "enum") ||
 					 0 == strcmp(key, "function") ||
 					 0 == strcmp(key, "struct") ||
 					 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
 			{
-				g_free(tag->atts.entry.scope);
-				tag->atts.entry.scope = g_strdup(value);
+				g_free(tag->scope);
+				tag->scope = g_strdup(value);
 			}
 			else if (0 == strcmp(key, "file")) /* static (local) tag */
-				tag->atts.entry.local = TRUE;
+				tag->local = TRUE;
 			else if (0 == strcmp(key, "signature")) /* arglist */
 			{
-				g_free(tag->atts.entry.arglist);
-				tag->atts.entry.arglist = g_strdup(value);
+				g_free(tag->arglist);
+				tag->arglist = g_strdup(value);
 			}
 		}
 	}
 
-	if (tm_tag_file_t != tag->type)
-		tag->atts.entry.file = file;
+	tag->file = file;
 	return TRUE;
 }
 
+/*
+ Same as tm_tag_new() except that the tag attributes are read from file.
+ @param mode langType to use for the tag.
+*/
 TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format)
 {
 	TMTag *tag;
@@ -585,70 +588,67 @@ TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileForma
 		TAG_FREE(tag);
 		return NULL;
 	}
-	tag->atts.file.lang = mode;
+	tag->lang = mode;
 	return tag;
 }
 
-gboolean tm_tag_write(TMTag *tag, FILE *fp, guint attrs)
+/*
+ Writes tag information to the given FILE *.
+ @param tag The tag information to write.
+ @param file FILE pointer to which the tag information is written.
+ @param attrs Attributes to be written (bitmask).
+ @return TRUE on success, FALSE on failure.
+*/
+gboolean tm_tag_write(TMTag *tag, FILE *fp, TMTagAttrType attrs)
 {
 	fprintf(fp, "%s", tag->name);
 	if (attrs & tm_tag_attr_type_t)
 		fprintf(fp, "%c%d", TA_TYPE, tag->type);
-	if (tag->type == tm_tag_file_t)
-	{
-		if (attrs & tm_tag_attr_time_t)
-			fprintf(fp, "%c%ld", TA_TIME, tag->atts.file.timestamp);
-		if (attrs & tm_tag_attr_lang_t)
-			fprintf(fp, "%c%d", TA_LANG, tag->atts.file.lang);
-		if ((attrs & tm_tag_attr_inactive_t) && tag->atts.file.inactive)
-			fprintf(fp, "%c%d", TA_INACTIVE, tag->atts.file.inactive);
-	}
-	else
-	{
-		if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->atts.entry.arglist))
-			fprintf(fp, "%c%s", TA_ARGLIST, tag->atts.entry.arglist);
-		if (attrs & tm_tag_attr_line_t)
-			fprintf(fp, "%c%ld", TA_LINE, tag->atts.entry.line);
-		if (attrs & tm_tag_attr_local_t)
-			fprintf(fp, "%c%d", TA_LOCAL, tag->atts.entry.local);
-		if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->atts.entry.scope))
-			fprintf(fp, "%c%s", TA_SCOPE, tag->atts.entry.scope);
-		if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->atts.entry.inheritance))
-			fprintf(fp, "%c%s", TA_INHERITS, tag->atts.entry.inheritance);
-		if (attrs & tm_tag_attr_pointer_t)
-			fprintf(fp, "%c%d", TA_POINTER, tag->atts.entry.pointerOrder);
-		if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->atts.entry.var_type))
-			fprintf(fp, "%c%s", TA_VARTYPE, tag->atts.entry.var_type);
-		if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->atts.entry.access))
-			fprintf(fp, "%c%c", TA_ACCESS, tag->atts.entry.access);
-		if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->atts.entry.impl))
-			fprintf(fp, "%c%c", TA_IMPL, tag->atts.entry.impl);
-	}
+	if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist))
+		fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist);
+	if (attrs & tm_tag_attr_line_t)
+		fprintf(fp, "%c%ld", TA_LINE, tag->line);
+	if (attrs & tm_tag_attr_local_t)
+		fprintf(fp, "%c%d", TA_LOCAL, tag->local);
+	if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope))
+		fprintf(fp, "%c%s", TA_SCOPE, tag->scope);
+	if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance))
+		fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance);
+	if (attrs & tm_tag_attr_pointer_t)
+		fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder);
+	if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type))
+		fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type);
+	if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access))
+		fprintf(fp, "%c%c", TA_ACCESS, tag->access);
+	if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl))
+		fprintf(fp, "%c%c", TA_IMPL, tag->impl);
+
 	if (fprintf(fp, "\n"))
 		return TRUE;
 	else
 		return FALSE;
 }
 
+/*
+ Destroys a TMTag structure, i.e. frees all elements except the tag itself.
+ @param tag The TMTag structure to destroy
+ @see tm_tag_free()
+*/
 static void tm_tag_destroy(TMTag *tag)
 {
 	g_free(tag->name);
-	if (tm_tag_file_t != tag->type)
-	{
-		g_free(tag->atts.entry.arglist);
-		g_free(tag->atts.entry.scope);
-		g_free(tag->atts.entry.inheritance);
-		g_free(tag->atts.entry.var_type);
-	}
+	g_free(tag->arglist);
+	g_free(tag->scope);
+	g_free(tag->inheritance);
+	g_free(tag->var_type);
 }
 
-#if 0
-void tm_tag_free(gpointer tag)
-{
-	tm_tag_unref(tag);
-}
-#endif
 
+/*
+ Drops a reference from a TMTag. If the reference count reaches 0, this function
+ destroys all data in the tag and frees the tag structure as well.
+ @param tag Pointer to a TMTag structure
+*/
 void tm_tag_unref(TMTag *tag)
 {
 	/* be NULL-proof because tm_tag_free() was NULL-proof and we indent to be a
@@ -660,77 +660,86 @@ void tm_tag_unref(TMTag *tag)
 	}
 }
 
+/*
+ Adds a reference to a TMTag.
+ @param tag Pointer to a TMTag structure
+ @return the passed-in TMTag
+*/
 TMTag *tm_tag_ref(TMTag *tag)
 {
 	g_atomic_int_inc(&tag->refcount);
 	return tag;
 }
 
-int tm_tag_compare(const void *ptr1, const void *ptr2)
+/*
+ Inbuilt tag comparison function.
+*/
+static gint tm_tag_compare(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
 {
 	unsigned int *sort_attr;
 	int returnval = 0;
 	TMTag *t1 = *((TMTag **) ptr1);
 	TMTag *t2 = *((TMTag **) ptr2);
+	TMSortOptions *sort_options = user_data;
 
 	if ((NULL == t1) || (NULL == t2))
 	{
 		g_warning("Found NULL tag");
 		return t2 - t1;
 	}
-	if (NULL == s_sort_attrs)
+	if (NULL == sort_options->sort_attrs)
 	{
-		if (s_partial)
+		if (sort_options->partial)
 			return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
 		else
 			return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
 	}
 
-	for (sort_attr = s_sort_attrs; *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
+	for (sort_attr = sort_options->sort_attrs; returnval == 0 && *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
 	{
 		switch (*sort_attr)
 		{
 			case tm_tag_attr_name_t:
-				if (s_partial)
+				if (sort_options->partial)
 					returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
 				else
 					returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
-				if (0 != returnval)
-					return returnval;
-				break;
-			case tm_tag_attr_type_t:
-				if (0 != (returnval = (t1->type - t2->type)))
-					return returnval;
 				break;
 			case tm_tag_attr_file_t:
-				if (0 != (returnval = (t1->atts.entry.file - t2->atts.entry.file)))
-					return returnval;
+				returnval = t1->file - t2->file;
+				break;
+			case tm_tag_attr_line_t:
+				returnval = t1->line - t2->line;
+				break;
+			case tm_tag_attr_type_t:
+				returnval = t1->type - t2->type;
 				break;
 			case tm_tag_attr_scope_t:
-				if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.scope, ""), FALLBACK(t2->atts.entry.scope, ""))))
-					return returnval;
+				returnval = strcmp(FALLBACK(t1->scope, ""), FALLBACK(t2->scope, ""));
 				break;
 			case tm_tag_attr_arglist_t:
-				if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.arglist, ""), FALLBACK(t2->atts.entry.arglist, ""))))
+				returnval = strcmp(FALLBACK(t1->arglist, ""), FALLBACK(t2->arglist, ""));
+				if (returnval != 0)
 				{
-					int line_diff = (t1->atts.entry.line - t2->atts.entry.line);
+					int line_diff = (t1->line - t2->line);
 
-					return line_diff ? line_diff : returnval;
+					returnval = line_diff ? line_diff : returnval;
 				}
 				break;
 			case tm_tag_attr_vartype_t:
-				if (0 != (returnval = strcmp(FALLBACK(t1->atts.entry.var_type, ""), FALLBACK(t2->atts.entry.var_type, ""))))
-					return returnval;
-				break;
-			case tm_tag_attr_line_t:
-				if (0 != (returnval = (t1->atts.entry.line - t2->atts.entry.line)))
-					return returnval;
+				returnval = strcmp(FALLBACK(t1->var_type, ""), FALLBACK(t2->var_type, ""));
 				break;
 		}
 	}
 	return returnval;
 }
 
+/*
+ Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() since 
+ this function substitutes duplicate entries with NULL
+ @param tags_array Array of tags to dedup
+ @return TRUE on success, FALSE on failure
+*/
 gboolean tm_tags_prune(GPtrArray *tags_array)
 {
 	guint i, count;
@@ -743,18 +752,29 @@ gboolean tm_tags_prune(GPtrArray *tags_array)
 	return TRUE;
 }
 
-gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes)
+/*
+ Deduplicates an array on tags using the inbuilt comparison function based on
+ the attributes specified. Called by tm_tags_sort() when dedup is TRUE.
+ @param tags_array Array of tags to dedup.
+ @param sort_attributes Attributes the array is sorted on. They will be deduped
+ on the same criteria.
+ @return TRUE on success, FALSE on failure
+*/
+gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
 {
+	TMSortOptions sort_options;
 	guint i;
 
 	if ((!tags_array) || (!tags_array->len))
 		return TRUE;
-	s_sort_attrs = sort_attributes;
-	s_partial = FALSE;
+	sort_options.sort_attrs = sort_attributes;
+	sort_options.partial = FALSE;
 	for (i = 1; i < tags_array->len; ++i)
 	{
-		if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i])))
+		if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]), &sort_options))
 		{
+			if (unref_duplicates)
+				tm_tag_unref(tags_array->pdata[i-1]);
 			tags_array->pdata[i-1] = NULL;
 		}
 	}
@@ -762,91 +782,200 @@ gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes)
 	return TRUE;
 }
 
-gboolean tm_tags_custom_dedup(GPtrArray *tags_array, TMTagCompareFunc compare_func)
+/*
+ Sort an array of tags on the specified attribuites using the inbuilt comparison
+ function.
+ @param tags_array The array of tags to be sorted
+ @param sort_attributes Attributes to be sorted on (int array terminated by 0)
+ @param dedup Whether to deduplicate the sorted array
+ @return TRUE on success, FALSE on failure
+*/
+gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes, 
+	gboolean dedup, gboolean unref_duplicates)
 {
-	guint i;
-
+	TMSortOptions sort_options;
+	
 	if ((!tags_array) || (!tags_array->len))
 		return TRUE;
-	for (i = 1; i < tags_array->len; ++i)
+	sort_options.sort_attrs = sort_attributes;
+	sort_options.partial = FALSE;
+	g_ptr_array_sort_with_data(tags_array, tm_tag_compare, &sort_options);
+	if (dedup)
+		tm_tags_dedup(tags_array, sort_attributes, unref_duplicates);
+	return TRUE;
+}
+
+void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
+{
+	guint i;
+	GPtrArray *to_delete = g_ptr_array_sized_new(source_file->tags_array->len);
+	
+	for (i = 0; i < source_file->tags_array->len; i++)
 	{
-		if (0 == compare_func(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i])))
-			tags_array->pdata[i-1] = NULL;
+		guint j;
+		guint tag_count;
+		TMTag **found;
+		TMTag *tag = source_file->tags_array->pdata[i];
+		
+		found = tm_tags_find(tags_array, tag->name, FALSE, TRUE, &tag_count);
+		
+		for (j = 0; j < tag_count; j++)
+		{
+			if (*found != NULL && (*found)->file == source_file)
+			{
+				/* we cannot set the pointer to NULL now because the search wouldn't work */
+				g_ptr_array_add(to_delete, found);
+				/* no break - if there are multiple tags of the same name, we would 
+				 * always find the first instance and wouldn't remove others; duplicates
+				 * in the to_delete list aren't a problem */
+			}
+			found++;
+		}
 	}
+
+	for (i = 0; i < to_delete->len; i++)
+	{
+		TMTag **tag = to_delete->pdata[i];
+		*tag = NULL;
+	}
+	g_ptr_array_free(to_delete, TRUE);
+
 	tm_tags_prune(tags_array);
-	return TRUE;
 }
 
-/* Sorts newly-added tags and merges them in order with existing tags.
- * This is much faster than resorting the whole array.
- * Note: Having the caller append to the existing array should be faster
- * than creating a new array which would likely get resized more than once.
- * tags_array: array with new (perhaps unsorted) tags appended.
- * orig_len: number of existing tags. */
-gboolean tm_tags_merge(GPtrArray *tags_array, gsize orig_len,
-	TMTagAttrType *sort_attributes, gboolean dedup)
-{
-	gpointer *copy, *a, *b;
-	gsize copy_len, i;
+/* Optimized merge sort for merging sorted values from one array to another
+ * where one of the arrays is much smaller than the other.
+ * The merge complexity depends mostly on the size of the small array
+ * and is almost independent of the size of the big array.
+ * In addition, get rid of the duplicates (if both big_array and small_array are duplicate-free). */
+static GPtrArray *merge(GPtrArray *big_array, GPtrArray *small_array, 
+	TMSortOptions *sort_options, gboolean unref_duplicates) {
+	guint i1 = 0;  /* index to big_array */
+	guint i2 = 0;  /* index to small_array */
+	guint initial_step;
+	guint step;
+	GPtrArray *res_array = g_ptr_array_sized_new(big_array->len + small_array->len);
+#ifdef TM_DEBUG
+	guint cmpnum = 0;
+#endif
 
-	if ((!tags_array) || (!tags_array->len) || orig_len >= tags_array->len)
-		return TRUE;
-	if (!orig_len)
-		return tm_tags_sort(tags_array, sort_attributes, dedup);
-	copy_len = tags_array->len - orig_len;
-	copy = g_memdup(tags_array->pdata + orig_len, copy_len * sizeof(gpointer));
-	s_sort_attrs = sort_attributes;
-	s_partial = FALSE;
-	/* enforce copy sorted with same attributes for merge */
-	qsort(copy, copy_len, sizeof(gpointer), tm_tag_compare);
-	a = tags_array->pdata + orig_len - 1;
-	b = copy + copy_len - 1;
-	for (i = tags_array->len - 1;; i--)
+	/* swap the arrays if len(small) > len(big) */
+	if (small_array->len > big_array->len)
+	{
+		GPtrArray *tmp = small_array;
+		small_array = big_array;
+		big_array = tmp;
+	}
+	
+	/* on average, we are merging a value from small_array every 
+	 * len(big_array) / len(small_array) values - good approximation for fast jump
+	 * step size */
+	initial_step = (small_array->len > 0) ? big_array->len / small_array->len : 1;
+	initial_step = initial_step > 4 ? initial_step : 1;
+	step = initial_step;
+	
+	while (i1 < big_array->len && i2 < small_array->len)
 	{
-		gint cmp = tm_tag_compare(a, b);
+		gpointer val1;
+		gpointer val2 = small_array->pdata[i2];
 
-		tags_array->pdata[i] = (cmp >= 0) ? *a-- : *b--;
-		if (a < tags_array->pdata)
+		if (step > 4)  /* fast path start */
 		{
-			/* include remainder of copy as well as current value of b */
-			memcpy(tags_array->pdata, copy, ((b + 1) - copy) * sizeof(gpointer));
-			break;
+			guint j1 = (i1 + step < big_array->len) ? i1 + step : big_array->len - 1;
+			
+			val1 = big_array->pdata[j1];
+#ifdef TM_DEBUG
+			cmpnum++;
+#endif
+			/* if the value in big_array after making the big step is still smaller
+			 * than the value in small_array, we can copy all the values inbetween
+			 * into the result without making expensive string comparisons */
+			if (tm_tag_compare(&val1, &val2, sort_options) < 0)
+			{
+				while (i1 <= j1) 
+				{
+					val1 = big_array->pdata[i1];
+					/* we allocated enough space so we are sure we don't need to reallocate
+					 * the array - copy and increment the size directly so it can be inlined */
+					res_array->pdata[res_array->len++] = val1;
+					i1++;
+				}
+			}
+			else 
+			{
+				/* lower the step and try again */
+				step /= 2;
+			}
+		}  /* fast path end */
+		else
+		{
+			gint cmpval;
+			
+#ifdef TM_DEBUG
+			cmpnum++;
+#endif
+			val1 = big_array->pdata[i1];
+			cmpval = tm_tag_compare(&val1, &val2, sort_options);
+			if (cmpval < 0)
+			{
+				g_ptr_array_add(res_array, val1);
+				i1++;
+			}
+			else
+			{
+				g_ptr_array_add(res_array, val2);
+				i2++;
+				/* value from small_array gets merged - reset the step size */
+				step = initial_step;
+				if (cmpval == 0)
+				{
+					i1++;  /* remove the duplicate, keep just the newly merged value */
+					if (unref_duplicates)
+						tm_tag_unref(val1);
+				}
+			}
 		}
-		if (b < copy)
-			break; /* remaining elements of 'a' are in place already */
-		g_assert(i != 0);
 	}
-	s_sort_attrs = NULL;
-	g_free(copy);
-	if (dedup)
-		tm_tags_dedup(tags_array, sort_attributes);
-	return TRUE;
-}
 
-gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean dedup)
-{
-	if ((!tags_array) || (!tags_array->len))
-		return TRUE;
-	s_sort_attrs = sort_attributes;
-	s_partial = FALSE;
-	qsort(tags_array->pdata, tags_array->len, sizeof(gpointer), tm_tag_compare);
-	s_sort_attrs = NULL;
-	if (dedup)
-		tm_tags_dedup(tags_array, sort_attributes);
-	return TRUE;
+	/* end of one of the arrays reached - copy the rest from the other array */
+	while (i1 < big_array->len)
+		g_ptr_array_add(res_array, big_array->pdata[i1++]);
+	while (i2 < small_array->len)
+		g_ptr_array_add(res_array, small_array->pdata[i2++]);
+		
+#ifdef TM_DEBUG
+	printf("cmpnums: %u\n", cmpnum);
+	printf("total tags: %u\n", big_array->len);
+	printf("merged tags: %u\n\n", small_array->len);
+#endif
+
+	return res_array;
 }
 
-gboolean tm_tags_custom_sort(GPtrArray *tags_array, TMTagCompareFunc compare_func, gboolean dedup)
+GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array, 
+	TMTagAttrType *sort_attributes, gboolean unref_duplicates)
 {
-	if ((!tags_array) || (!tags_array->len))
-		return TRUE;
-	qsort(tags_array->pdata, tags_array->len, sizeof(gpointer), compare_func);
-	if (dedup)
-		tm_tags_custom_dedup(tags_array, compare_func);
-	return TRUE;
+	GPtrArray *res_array;
+	TMSortOptions sort_options;
+	
+	sort_options.sort_attrs = sort_attributes;
+	sort_options.partial = FALSE;
+	res_array = merge(big_array, small_array, &sort_options, unref_duplicates);
+	return res_array;
 }
 
-GPtrArray *tm_tags_extract(GPtrArray *tags_array, guint tag_types)
+/*
+ This function will extract the tags of the specified types from an array of tags.
+ The returned value is a GPtrArray which should be free-d with a call to
+ g_ptr_array_free(array, TRUE). However, do not free the tags themselves since they
+ are not duplicated.
+ @param tags_array The original array of tags
+ @param tag_types - The tag types to extract. Can be a bitmask. For example, passing
+ (tm_tag_typedef_t | tm_tag_struct_t) will extract all typedefs and structures from
+ the original array.
+ @return an array of tags (NULL on failure)
+*/
+GPtrArray *tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types)
 {
 	GPtrArray *new_tags;
 	guint i;
@@ -864,6 +993,11 @@ GPtrArray *tm_tags_extract(GPtrArray *tags_array, guint tag_types)
 	return new_tags;
 }
 
+/*
+ Completely frees an array of tags.
+ @param tags_array Array of tags to be freed.
+ @param free_array Whether the GptrArray is to be freed as well.
+*/
 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
 {
 	if (tags_array)
@@ -878,46 +1012,86 @@ void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
 	}
 }
 
-static TMTag **tags_search(const GPtrArray *tags_array, TMTag *tag, gboolean partial,
-		gboolean tags_array_sorted)
+/* copy/pasted bsearch() from libc extended with user_data for comparison function
+ * and using glib types */
+static gpointer binary_search(gpointer key, gpointer base, size_t nmemb, 
+	GCompareDataFunc compar, gpointer user_data)
+{
+	gsize l, u, idx;
+	gpointer p;
+	gint comparison;
+
+	l = 0;
+	u = nmemb;
@@ Diff output truncated at 100000 characters. @@

--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list