[geany/geany] a775da: Merge pull request #862 from techee/tm_workspace_find_cleanup3

Colomban Wendling git-noreply at xxxxx
Thu Feb 11 14:30:09 UTC 2016


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Thu, 11 Feb 2016 14:30:09 UTC
Commit:      a775da0714b8c7d22e7726ff274058658e6d4858
             https://github.com/geany/geany/commit/a775da0714b8c7d22e7726ff274058658e6d4858

Log Message:
-----------
Merge pull request #862 from techee/tm_workspace_find_cleanup3

Rewrite scope completion v3.

Closes #488 and #505.


Modified Paths:
--------------
    src/editor.c
    src/symbols.c
    tagmanager/src/tm_source_file.c
    tagmanager/src/tm_tag.c
    tagmanager/src/tm_tag.h
    tagmanager/src/tm_workspace.c
    tagmanager/src/tm_workspace.h

Modified: src/editor.c
172 lines changed, 130 insertions(+), 42 deletions(-)
===================================================================
@@ -75,6 +75,7 @@ static GHashTable *snippet_hash = NULL;
 static GQueue *snippet_offsets = NULL;
 static gint snippet_cursor_insert_pos;
 static GtkAccelGroup *snippet_accel_group = NULL;
+static gboolean autocomplete_scope_shown = FALSE;
 
 static const gchar geany_cursor_marker[] = "__GEANY_CURSOR_MARKER__";
 
@@ -657,7 +658,9 @@ static gboolean match_last_chars(ScintillaObject *sci, gint pos, const gchar *st
 	gchar *buf;
 
 	g_return_val_if_fail(len < 100, FALSE);
-	g_return_val_if_fail((gint)len <= pos, FALSE);
+
+	if ((gint)len > pos)
+		return FALSE;
 
 	buf = g_alloca(len + 1);
 	sci_get_text_range(sci, pos - len, pos, buf);
@@ -697,51 +700,108 @@ static void request_reshowing_calltip(SCNotification *nt)
 }
 
 
-static void autocomplete_scope(GeanyEditor *editor)
+static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize rootlen)
 {
 	ScintillaObject *sci = editor->sci;
 	gint pos = sci_get_current_position(editor->sci);
 	gchar typed = sci_get_char_at(sci, pos - 1);
+	gchar brace_char;
 	gchar *name;
-	const GPtrArray *tags = NULL;
-	const TMTag *tag;
 	GeanyFiletype *ft = editor->document->file_type;
+	GPtrArray *tags;
+	gboolean function = FALSE;
+	gboolean member;
+	gboolean ret = FALSE;
+	const gchar *current_scope;
+	const gchar *context_sep = tm_tag_context_separator(ft->lang);
 
-	if (ft->id == GEANY_FILETYPES_C || ft->id == GEANY_FILETYPES_CPP)
+	if (autocomplete_scope_shown)
 	{
-		if (pos >= 2 && (match_last_chars(sci, pos, "->") || match_last_chars(sci, pos, "::")))
+		/* move at the operator position */
+		pos -= rootlen;
+
+		/* allow for a space between word and operator */
+		while (pos > 0 && isspace(sci_get_char_at(sci, pos - 1)))
 			pos--;
-		else if (ft->id == GEANY_FILETYPES_CPP && pos >= 3 && match_last_chars(sci, pos, "->*"))
-			pos-=2;
-		else if (typed != '.')
-			return;
+
+		if (pos > 0)
+			typed = sci_get_char_at(sci, pos - 1);
 	}
-	else if (typed != '.')
-		return;
+
+	/* make sure to keep in sync with similar checks below */
+	if (typed == '.')
+		pos -= 1;
+	else if (match_last_chars(sci, pos, context_sep))
+		pos -= strlen(context_sep);
+	else if ((ft->id == GEANY_FILETYPES_C || ft->id == GEANY_FILETYPES_CPP) &&
+			match_last_chars(sci, pos, "->"))
+		pos -= 2;
+	else if (ft->id == GEANY_FILETYPES_CPP && match_last_chars(sci, pos, "->*"))
+		pos -= 3;
+	else
+		return FALSE;
 
 	/* allow for a space between word and operator */
-	if (isspace(sci_get_char_at(sci, pos - 2)))
+	while (pos > 0 && isspace(sci_get_char_at(sci, pos - 1)))
 		pos--;
-	name = editor_get_word_at_pos(editor, pos - 1, NULL);
-	if (!name)
-		return;
 
-	tags = tm_workspace_find(name, tm_tag_max_t, NULL, FALSE, ft->lang);
-	g_free(name);
-	if (!tags || tags->len == 0)
-		return;
+	/* if function or array index, skip to matching brace */
+	brace_char = sci_get_char_at(sci, pos - 1);
+	if (pos > 0 && (brace_char == ')' || brace_char == ']'))
+	{
+		gint brace_pos = sci_find_matching_brace(sci, pos - 1);
 
-	tag = g_ptr_array_index(tags, 0);
-	name = tag->var_type;
-	if (name)
+		if (brace_pos != -1)
+		{
+			pos = brace_pos;
+			function = brace_char == ')';
+		}
+
+		/* allow for a space between opening brace and name */
+		while (pos > 0 && isspace(sci_get_char_at(sci, pos - 1)))
+			pos--;
+	}
+
+	name = editor_get_word_at_pos(editor, pos, NULL);
+	if (!name)
+		return FALSE;
+
+	/* check if invoked on member */
+	pos -= strlen(name);
+	while (pos > 0 && isspace(sci_get_char_at(sci, pos - 1)))
+		pos--;
+	/* make sure to keep in sync with similar checks above */
+	member = match_last_chars(sci, pos, ".") || match_last_chars(sci, pos, context_sep) ||
+			 match_last_chars(sci, pos, "->") || match_last_chars(sci, pos, "->*");
+
+	if (symbols_get_current_scope(editor->document, &current_scope) == -1)
+		current_scope = "";
+	tags = tm_workspace_find_scope_members(editor->document->tm_file, name, function,
+				member, current_scope);
+	if (tags)
 	{
-		TMSourceFile *obj = editor->document->tm_file;
+		GPtrArray *filtered = g_ptr_array_new();
+		TMTag *tag;
+		guint i;
+
+		foreach_ptr_array(tag, i, tags)
+		{
+			if (g_str_has_prefix(tag->name, root))
+				g_ptr_array_add(filtered, tag);
+		}
 
-		tags = tm_workspace_find_scope_members(obj ? obj->tags_array : NULL,
-			name, TRUE, FALSE);
-		if (tags)
-			show_tags_list(editor, tags, 0);
+		if (filtered->len > 0)
+		{
+			show_tags_list(editor, filtered, rootlen);
+			ret = TRUE;
+		}
+
+		g_ptr_array_free(tags, TRUE);
+		g_ptr_array_free(filtered, TRUE);
 	}
+
+	g_free(name);
+	return ret;
 }
 
 
@@ -1100,6 +1160,7 @@ static gboolean on_editor_notify(G_GNUC_UNUSED GObject *object, GeanyEditor *edi
 		case SCN_AUTOCCANCELLED:
 			/* now that autocomplete is finishing or was cancelled, reshow calltips
 			 * if they were showing */
+			autocomplete_scope_shown = FALSE;
 			request_reshowing_calltip(nt);
 			break;
 		case SCN_NEEDSHOWN:
@@ -1832,10 +1893,9 @@ static gboolean append_calltip(GString *str, const TMTag *tag, GeanyFiletypeID f
 
 static gchar *find_calltip(const gchar *word, GeanyFiletype *ft)
 {
-	const GPtrArray *tags;
+	GPtrArray *tags;
 	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;
 	GString *str = NULL;
 	guint i;
@@ -1843,20 +1903,26 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft)
 	g_return_val_if_fail(ft && word && *word, NULL);
 
 	/* use all types in case language uses wrong tag type e.g. python "members" instead of "methods" */
-	tags = tm_workspace_find(word, tm_tag_max_t, attrs, FALSE, ft->lang);
+	tags = tm_workspace_find(word, NULL, tm_tag_max_t, NULL, ft->lang);
 	if (tags->len == 0)
+	{
+		g_ptr_array_free(tags, TRUE);
 		return NULL;
+	}
 
 	tag = TM_TAG(tags->pdata[0]);
 
 	if (ft->id == GEANY_FILETYPES_D &&
 		(tag->type == tm_tag_class_t || tag->type == tm_tag_struct_t))
 	{
+		g_ptr_array_free(tags, TRUE);
 		/* user typed e.g. 'new Classname(' so lookup D constructor Classname::this() */
-		tags = tm_workspace_find_scoped("this", tag->name,
-			arg_types, attrs, FALSE, ft->lang, TRUE);
+		tags = tm_workspace_find("this", tag->name, arg_types, NULL, ft->lang);
 		if (tags->len == 0)
+		{
+			g_ptr_array_free(tags, TRUE);
 			return NULL;
+		}
 	}
 
 	/* remove tags with no argument list */
@@ -1869,7 +1935,10 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft)
 	}
 	tm_tags_prune((GPtrArray *) tags);
 	if (tags->len == 0)
+	{
+		g_ptr_array_free(tags, TRUE);
 		return NULL;
+	}
 	else
 	{	/* remove duplicate calltips */
 		TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, tm_tag_attr_scope_t,
@@ -1906,6 +1975,9 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft)
 			break;
 		}
 	}
+
+	g_ptr_array_free(tags, TRUE);
+
 	if (str)
 	{
 		gchar *result = str->str;
@@ -2027,21 +2099,21 @@ autocomplete_html(ScintillaObject *sci, const gchar *root, gsize rootlen)
 static gboolean
 autocomplete_tags(GeanyEditor *editor, const gchar *root, gsize rootlen)
 {
-	TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
-	const GPtrArray *tags;
+	GPtrArray *tags;
 	GeanyDocument *doc;
+	gboolean found;
 
 	g_return_val_if_fail(editor, FALSE);
 
 	doc = editor->document;
 
-	tags = tm_workspace_find(root, tm_tag_max_t, attrs, TRUE, doc->file_type->lang);
-	if (tags)
-	{
+	tags = tm_workspace_find_prefix(root, doc->file_type->lang, editor_prefs.autocompletion_max_entries);
+	found = tags->len > 0;
+	if (found)
 		show_tags_list(editor, tags, rootlen);
-		return tags->len > 0;
-	}
-	return FALSE;
+	g_ptr_array_free(tags, TRUE);
+
+	return found;
 }
 
 
@@ -2201,7 +2273,6 @@ gboolean editor_start_auto_complete(GeanyEditor *editor, gint pos, gboolean forc
 	if (!force && !highlighting_is_code_style(lexer, style))
 		return FALSE;
 
-	autocomplete_scope(editor);
 	ret = autocomplete_check_html(editor, style, pos);
 
 	if (ft->id == GEANY_FILETYPES_LATEX)
@@ -2215,6 +2286,23 @@ gboolean editor_start_auto_complete(GeanyEditor *editor, gint pos, gboolean forc
 	root = cword;
 	rootlen = strlen(root);
 
+	if (ret || force)
+	{
+		if (autocomplete_scope_shown)
+		{
+			autocomplete_scope_shown = FALSE;
+			if (!ret)
+				sci_send_command(sci, SCI_AUTOCCANCEL);
+		}
+	}
+	else
+	{
+		ret = autocomplete_scope(editor, root, rootlen);
+		if (!ret && autocomplete_scope_shown)
+			sci_send_command(sci, SCI_AUTOCCANCEL);
+		autocomplete_scope_shown = ret;
+	}
+
 	if (!ret && rootlen > 0)
 	{
 		if (ft->id == GEANY_FILETYPES_PHP && style == SCE_HPHP_DEFAULT &&


Modified: src/symbols.c
30 lines changed, 2 insertions(+), 28 deletions(-)
===================================================================
@@ -281,7 +281,7 @@ GString *symbols_find_typenames_as_string(gint lang, gboolean global)
 	gint tag_lang;
 
 	if (global)
-		typedefs = tm_tags_extract(app->tm_workspace->global_tags, TM_GLOBAL_TYPE_MASK);
+		typedefs = app->tm_workspace->global_typename_array;
 	else
 		typedefs = app->tm_workspace->typename_array;
 
@@ -305,8 +305,6 @@ GString *symbols_find_typenames_as_string(gint lang, gboolean global)
 			}
 		}
 	}
-	if (typedefs && global)
-		g_ptr_array_free(typedefs, TRUE);
 	return s;
 }
 
@@ -324,31 +322,7 @@ GString *symbols_find_typenames_as_string(gint lang, gboolean global)
 GEANY_API_SYMBOL
 const gchar *symbols_get_context_separator(gint ft_id)
 {
-	switch (ft_id)
-	{
-		case GEANY_FILETYPES_C:	/* for C++ .h headers or C structs */
-		case GEANY_FILETYPES_CPP:
-		case GEANY_FILETYPES_GLSL:	/* for structs */
-		/*case GEANY_FILETYPES_RUBY:*/ /* not sure what to use atm*/
-		case GEANY_FILETYPES_PHP:
-		case GEANY_FILETYPES_POWERSHELL:
-		case GEANY_FILETYPES_RUST:
-		case GEANY_FILETYPES_ZEPHIR:
-			return "::";
-
-		/* avoid confusion with other possible separators in group/section name */
-		case GEANY_FILETYPES_CONF:
-		case GEANY_FILETYPES_REST:
-			return ":::";
-
-		/* no context separator */
-		case GEANY_FILETYPES_ASCIIDOC:
-		case GEANY_FILETYPES_TXT2TAGS:
-			return "\x03";
-
-		default:
-			return ".";
-	}
+	return tm_tag_context_separator(filetypes[ft_id]->lang);
 }
 
 


Modified: tagmanager/src/tm_source_file.c
18 lines changed, 10 insertions(+), 8 deletions(-)
===================================================================
@@ -126,8 +126,7 @@ static int tm_source_file_tags(const tagEntryInfo *tag)
 /* 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;
+	guint i;
 
 	if (NULL == arglist ||
 		NULL == tag_name ||
@@ -136,13 +135,16 @@ static void tm_source_file_set_tag_arglist(const char *tag_name, const char *arg
 		return;
 	}
 
-	tags = tm_tags_find(current_source_file->tags_array, tag_name, FALSE, FALSE,
-			&count);
-	if (tags != NULL && count == 1)
+	/* going in reverse order because the tag was added recently */
+	for (i = current_source_file->tags_array->len; i > 0; i--)
 	{
-		tag = tags[0];
-		g_free(tag->arglist);
-		tag->arglist = g_strdup(arglist);
+		TMTag *tag = (TMTag *) current_source_file->tags_array->pdata[i - 1];
+		if (g_strcmp0(tag->name, tag_name) == 0)
+		{
+			g_free(tag->arglist);
+			tag->arglist = g_strdup(arglist);
+			break;
+		}
 	}
 }
 


Modified: tagmanager/src/tm_tag.c
154 lines changed, 89 insertions(+), 65 deletions(-)
===================================================================
@@ -17,6 +17,7 @@
 #include "read.h"
 #define LIBCTAGS_DEFINED
 #include "tm_tag.h"
+#include "tm_parser.h"
 
 
 #define TAG_NEW(T)	((T) = g_slice_new0(TMTag))
@@ -108,6 +109,8 @@ typedef struct
 {
 	guint *sort_attrs;
 	gboolean partial;
+	const GPtrArray *tags_array;
+	gboolean first;
 } TMSortOptions;
 
 static const char *s_tag_type_names[] = {
@@ -860,7 +863,7 @@ void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
 			TMTag **found;
 			TMTag *tag = source_file->tags_array->pdata[i];
 
-			found = tm_tags_find(tags_array, tag->name, FALSE, TRUE, &tag_count);
+			found = tm_tags_find(tags_array, tag->name, FALSE, &tag_count);
 
 			for (j = 0; j < tag_count; j++)
 			{
@@ -1081,85 +1084,74 @@ static gpointer binary_search(gpointer key, gpointer base, size_t nmemb,
 	return NULL;
 }
 
-static TMTag **tags_search(const GPtrArray *tags_array, TMTag *tag,
-		gboolean tags_array_sorted, TMSortOptions *sort_options)
+static gint tag_search_cmp(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
 {
-	if (tags_array_sorted)
-	{	/* fast binary search on sorted tags array */
-		return (TMTag **) binary_search(&tag, tags_array->pdata, tags_array->len, 
-			tm_tag_compare, sort_options);
-	}
-	else
-	{	/* the slow way: linear search (to make it a bit faster, search reverse assuming
-		 * that the tag to search was added recently) */
-		guint i;
-		TMTag **t;
-		for (i = tags_array->len; i > 0; i--)
+	gint res = tm_tag_compare(ptr1, ptr2, user_data);
+
+	if (res == 0)
+	{
+		TMSortOptions *sort_options = user_data;
+		const GPtrArray *tags_array = sort_options->tags_array;
+		TMTag **tag = (TMTag **) ptr2;
+
+		/* if previous/next (depending on sort options) tag equal, we haven't
+		 * found the first/last tag in a sequence of equal tags yet */
+		if (sort_options->first && ptr2 != &tags_array->pdata[0]) {
+			if (tm_tag_compare(ptr1, tag - 1, user_data) == 0)
+				return -1;
+		}
+		else if (!sort_options->first && ptr2 != &tags_array->pdata[tags_array->len-1])
 		{
-			t = (TMTag **) &tags_array->pdata[i - 1];
-			if (0 == tm_tag_compare(&tag, t, sort_options))
-				return t;
+			if (tm_tag_compare(ptr1, tag + 1, user_data) == 0)
+				return 1;
 		}
 	}
-	return NULL;
+	return res;
 }
 
 /*
  Returns a pointer to the position of the first matching tag in a (sorted) tags array.
- The passed array of tags should be already sorted by name for optimal performance. If
- \c tags_array_sorted is set to FALSE, it may be unsorted but the lookup will be slower.
- @param tags_array Tag array (may be sorted on name)
+ The passed array of tags must be already sorted by name (searched with binary search).
+ @param tags_array Tag array (sorted on name)
  @param name Name of the tag to locate.
  @param partial If TRUE, matches the first part of the name instead of doing exact match.
- @param tags_array_sorted If TRUE, the passed \c tags_array is sorted by name so it can be
- searched with binary search. Otherwise it is searched linear which is obviously slower.
  @param tagCount Return location of the matched tags.
 */
 TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
-		gboolean partial, gboolean tags_array_sorted, guint * tagCount)
+		gboolean partial, guint *tagCount)
 {
-	static TMTag *tag = NULL;
-	TMTag **result;
-	guint tagMatches=0;
+	TMTag *tag, **first;
 	TMSortOptions sort_options;
 
 	*tagCount = 0;
-	if ((!tags_array) || (!tags_array->len))
+	if (!tags_array || !tags_array->len)
 		return NULL;
 
-	if (NULL == tag)
-		tag = g_new0(TMTag, 1);
+	tag = g_new0(TMTag, 1);
 	tag->name = (char *) name;
+
 	sort_options.sort_attrs = NULL;
 	sort_options.partial = partial;
+	sort_options.tags_array = tags_array;
+	sort_options.first = TRUE;
+	first = (TMTag **)binary_search(&tag, tags_array->pdata, tags_array->len,
+			tag_search_cmp, &sort_options);
 
-	result = tags_search(tags_array, tag, tags_array_sorted, &sort_options);
-	/* There can be matches on both sides of result */
-	if (result)
+	if (first)
 	{
-		TMTag **last = (TMTag **) &tags_array->pdata[tags_array->len - 1];
-		TMTag **adv;
-
-		/* First look for any matches after result */
-		adv = result;
-		adv++;
-		for (; adv <= last && *adv; ++ adv)
-		{
-			if (0 != tm_tag_compare(&tag, adv, &sort_options))
-				break;
-			++tagMatches;
-		}
-		/* Now look for matches from result and below */
-		for (; result >= (TMTag **) tags_array->pdata; -- result)
-		{
-			if (0 != tm_tag_compare(&tag, (TMTag **) result, &sort_options))
-				break;
-			++tagMatches;
-		}
-		*tagCount=tagMatches;
-		++ result;	/* Correct address for the last successful match */
+		TMTag **last;
+		unsigned first_pos;
+
+		sort_options.first = FALSE;
+		first_pos = first - (TMTag **)tags_array->pdata;
+		/* search between the first element and end */
+		last = (TMTag **)binary_search(&tag, first, tags_array->len - first_pos,
+				tag_search_cmp, &sort_options);
+		*tagCount = last - first + 1;
 	}
-	return (TMTag **) result;
+
+	g_free(tag);
+	return (TMTag **) first;
 }
 
 /* Returns TMTag which "own" given line
@@ -1190,17 +1182,48 @@ tm_get_current_tag (GPtrArray * file_tags, const gulong line, const TMTagType ta
 	return matching_tag;
 }
 
-#if 0
-/* Returns TMTag to function or method which "own" given line
- @param line Current line in edited file.
- @param file_tags A GPtrArray of edited file TMTag pointers.
- @return TMTag pointers to owner function. */
-static const TMTag *
-tm_get_current_function (GPtrArray * file_tags, const gulong line)
+const gchar *tm_tag_context_separator(langType lang)
 {
-	return tm_get_current_tag (file_tags, line, tm_tag_function_t | tm_tag_method_t);
+	switch (lang)
+	{
+		case TM_PARSER_C:	/* for C++ .h headers or C structs */
+		case TM_PARSER_CPP:
+		case TM_PARSER_GLSL:	/* for structs */
+		/*case GEANY_FILETYPES_RUBY:*/ /* not sure what to use atm*/
+		case TM_PARSER_PHP:
+		case TM_PARSER_POWERSHELL:
+		case TM_PARSER_RUST:
+		case TM_PARSER_ZEPHIR:
+			return "::";
+
+		/* avoid confusion with other possible separators in group/section name */
+		case TM_PARSER_CONF:
+		case TM_PARSER_REST:
+			return ":::";
+
+		/* no context separator */
+		case TM_PARSER_ASCIIDOC:
+		case TM_PARSER_TXT2TAGS:
+			return "\x03";
+
+		default:
+			return ".";
+	}
+}
+
+gboolean tm_tag_is_anon(const TMTag *tag)
+{
+	guint i;
+	char dummy;
+
+	if (tag->lang == TM_PARSER_C || tag->lang == TM_PARSER_CPP)
+		return sscanf(tag->name, "anon_%*[a-z]_%u%c", &i, &dummy) == 1;
+	else if (tag->lang == TM_PARSER_FORTRAN || tag->lang == TM_PARSER_F77)
+		return sscanf(tag->name, "Structure#%u%c", &i, &dummy) == 1 ||
+			sscanf(tag->name, "Interface#%u%c", &i, &dummy) == 1 ||
+			sscanf(tag->name, "Enum#%u%c", &i, &dummy) == 1;
+	return FALSE;
 }
-#endif
 
 
 #ifdef TM_DEBUG /* various debugging functions */
@@ -1343,11 +1366,12 @@ void tm_tags_array_print(GPtrArray *tags, FILE *fp)
 */
 gint tm_tag_scope_depth(const TMTag *t)
 {
+	const gchar *context_sep = tm_tag_context_separator(t->lang);
 	gint depth;
 	char *s;
 	if(!(t && t->scope))
 		return 0;
-	for (s = t->scope, depth = 0; s; s = strstr(s, "::"))
+	for (s = t->scope, depth = 0; s; s = strstr(s, context_sep))
 	{
 		++ depth;
 		++ s;


Modified: tagmanager/src/tm_tag.h
6 lines changed, 5 insertions(+), 1 deletions(-)
===================================================================
@@ -179,7 +179,7 @@ gboolean tm_tags_prune(GPtrArray *tags_array);
 gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates);
 
 TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
-		gboolean partial, gboolean tags_array_sorted, guint * tagCount);
+		gboolean partial, guint * tagCount);
 
 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all);
 
@@ -191,6 +191,10 @@ TMTag *tm_tag_ref(TMTag *tag);
 
 gboolean tm_tags_equal(const TMTag *a, const TMTag *b);
 
+const gchar *tm_tag_context_separator(langType lang);
+
+gboolean tm_tag_is_anon(const TMTag *tag);
+
 #ifdef TM_DEBUG /* various debugging functions */
 
 const char *tm_tag_type_name(const TMTag *tag);


Modified: tagmanager/src/tm_workspace.c
898 lines changed, 336 insertions(+), 562 deletions(-)
===================================================================
@@ -32,6 +32,7 @@
 
 #include "tm_workspace.h"
 #include "tm_tag.h"
+#include "tm_parser.h"
 
 
 /* when changing, always keep the three sort criteria below in sync */
@@ -55,6 +56,9 @@ static TMTagAttrType global_tags_sort_attrs[] =
 	tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
 };
 
+static TMTagType TM_TYPE_WITH_MEMBERS =
+	tm_tag_class_t | tm_tag_struct_t | tm_tag_union_t |
+	tm_tag_enum_t | tm_tag_interface_t;
 
 static TMWorkspace *theWorkspace = NULL;
 
@@ -67,6 +71,7 @@ static gboolean tm_create_workspace(void)
 	theWorkspace->global_tags = g_ptr_array_new();
 	theWorkspace->source_files = g_ptr_array_new();
 	theWorkspace->typename_array = g_ptr_array_new();
+	theWorkspace->global_typename_array = g_ptr_array_new();
 	return TRUE;
 }
 
@@ -88,6 +93,7 @@ void tm_workspace_free(void)
 	tm_tags_array_free(theWorkspace->global_tags, TRUE);
 	g_ptr_array_free(theWorkspace->tags_array, TRUE);
 	g_ptr_array_free(theWorkspace->typename_array, TRUE);
+	g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
 	g_free(theWorkspace);
 	theWorkspace = NULL;
 }
@@ -116,6 +122,16 @@ static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_arra
 }
 
 
+static void merge_extracted_tags(GPtrArray **dest, GPtrArray *src, TMTagType tag_types)
+{
+	GPtrArray *arr;
+
+	arr = tm_tags_extract(src, tag_types);
+	tm_workspace_merge_tags(dest, arr);
+	g_ptr_array_free(arr, TRUE);
+}
+
+
 static void update_source_file(TMSourceFile *source_file, guchar* text_buf,
 	gsize buf_size, gboolean use_buffer, gboolean update_workspace)
 {
@@ -134,16 +150,12 @@ static void update_source_file(TMSourceFile *source_file, guchar* text_buf,
 	tm_tags_sort(source_file->tags_array, file_tags_sort_attrs, FALSE, TRUE);
 	if (update_workspace)
 	{
-		GPtrArray *sf_typedefs;
-
 #ifdef TM_DEBUG
 		g_message("Updating workspace from source file");
 #endif
 		tm_workspace_merge_tags(&theWorkspace->tags_array, source_file->tags_array);
-		
-		sf_typedefs = tm_tags_extract(source_file->tags_array, TM_GLOBAL_TYPE_MASK);
-		tm_workspace_merge_tags(&theWorkspace->typename_array, sf_typedefs);
-		g_ptr_array_free(sf_typedefs, TRUE);
+
+		merge_extracted_tags(&(theWorkspace->typename_array), source_file->tags_array, TM_GLOBAL_TYPE_MASK);
 	}
 #ifdef TM_DEBUG
 	else
@@ -385,6 +397,9 @@ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode)
 	g_ptr_array_free(file_tags, TRUE);
 	theWorkspace->global_tags = new_tags;
 
+	g_ptr_array_free(theWorkspace->global_typename_array, TRUE);
+	theWorkspace->global_typename_array = tm_tags_extract(new_tags, TM_GLOBAL_TYPE_MASK);
+
 	return TRUE;
 }
 
@@ -671,667 +686,426 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i
 }
 
 
-/* Returns all matching tags found in the workspace.
- @param name The name of the tag to find.
- @param type The tag types to return (TMTagType). Can be a bitmask.
- @param attrs The attributes to sort and dedup on (0 terminated integer array).
- @param partial Whether partial match is allowed.
- @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
-             -1 for all
- @return Array of matching tags. Do not free() it since it is a static member.
-*/
-const GPtrArray *tm_workspace_find(const char *name, TMTagType type, TMTagAttrType *attrs,
-	gboolean partial, langType lang)
+static gboolean langs_compatible(langType lang, langType other)
 {
-	static GPtrArray *tags = NULL;
-	TMTag **matches[2];
-	size_t len;
-	guint tagCount[2]={0,0}, tagIter;
-
-	if (!name)
-		return NULL;
-	len = strlen(name);
-	if (!len)
-		return NULL;
-	if (tags)
-		g_ptr_array_set_size(tags, 0);
-	else
-		tags = g_ptr_array_new();
-
-	matches[0] = tm_tags_find(theWorkspace->tags_array, name, partial, TRUE,
-					&tagCount[0]);
-	matches[1] = tm_tags_find(theWorkspace->global_tags, name, partial, TRUE, &tagCount[1]);
-
-	/* file tags */
-	if (matches[0] && *matches[0])
-	{
-		for (tagIter=0;tagIter<tagCount[0];++tagIter)
-		{
-			gint tag_lang = (*matches[0])->lang;
-
-			if ((type & (*matches[0])->type) && (lang == -1 || tag_lang == lang))
-				g_ptr_array_add(tags, *matches[0]);
-			if (partial)
-			{
-				if (0 != strncmp((*matches[0])->name, name, len))
-					break;
-			}
-			else
-			{
-				if (0 != strcmp((*matches[0])->name, name))
-					break;
-			}
-			++ matches[0];
-		}
-	}
-
-	/* global tags */
-	if (matches[1] && *matches[1])
-	{
-		for (tagIter=0;tagIter<tagCount[1];++tagIter)
-		{
-			gint tag_lang = (*matches[1])->lang;
-			gint tag_lang_alt = 0;
-
-			/* tag_lang_alt is used to load C global tags only once for C and C++
-			 * lang = 1 is C++, lang = 0 is C
-			 * if we have lang 0, than accept also lang 1 for C++ */
-			if (tag_lang == 0)	/* C or C++ */
-				tag_lang_alt = 1;
-			else
-				tag_lang_alt = tag_lang; /* otherwise just ignore it */
-
-			if ((type & (*matches[1])->type) && (lang == -1 ||
-				tag_lang == lang || tag_lang_alt == lang))
-				g_ptr_array_add(tags, *matches[1]);
-
-			if (partial)
-			{
-				if (0 != strncmp((*matches[1])->name, name, len))
-					break;
-			}
-			else
-			{
-				if (0 != strcmp((*matches[1])->name, name))
-					break;
-			}
-			++ matches[1];
-		}
-	}
+	if (lang == other || lang == -1 || other == -1)
+		return TRUE;
+	/* Accept CPP tags for C lang and vice versa */
+	else if (lang == TM_PARSER_C && other == TM_PARSER_CPP)
+		return TRUE;
+	else if (lang == TM_PARSER_CPP && other == TM_PARSER_C)
+		return TRUE;
 
-	if (attrs)
-		tm_tags_sort(tags, attrs, TRUE, FALSE);
-	return tags;
-}
-
-
-static gboolean match_langs(gint lang, const TMTag *tag)
-{
-	if (tag->file)
-	{	/* workspace tag */
-		if (lang == tag->file->lang)
-			return TRUE;
-	}
-	else
-	{	/* global tag */
-		if (lang == tag->lang)
-			return TRUE;
-	}
 	return FALSE;
 }
 
 
-/* scope can be NULL.
- * lang can be -1 */
-static guint
-fill_find_tags_array (GPtrArray *dst, const GPtrArray *src,
-					  const char *name, const char *scope, TMTagType type, gboolean partial,
-					  gint lang, gboolean first)
+static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
+	const char *name, const char *scope, TMTagType type, langType lang)
 {
-	TMTag **match;
-	guint tagIter, count;
+	TMTag **tag;
+	guint i, num;
 
-	if ((!src) || (!dst) || (!name) || (!*name))
-		return 0;
+	if (!src || !dst || !name || !*name)
+		return;
 
-	match = tm_tags_find (src, name, partial, TRUE, &count);
-	if (count && match && *match)
+	tag = tm_tags_find(src, name, FALSE, &num);
+	for (i = 0; i < num; ++i)
 	{
-		for (tagIter = 0; tagIter < count; ++tagIter)
+		if ((type & (*tag)->type) &&
+			langs_compatible(lang, (*tag)->lang) &&
+			(!scope || g_strcmp0((*tag)->scope, scope) == 0))
 		{
-			if (! scope || (match[tagIter]->scope &&
-				0 == strcmp(match[tagIter]->scope, scope)))
-			{
-				if (type & match[tagIter]->type)
-				if (lang == -1 || match_langs(lang, match[tagIter]))
-				{
-					g_ptr_array_add (dst, match[tagIter]);
-					if (first)
-						break;
-				}
-			}
+			g_ptr_array_add(dst, *tag);
 		}
+		tag++;
 	}
-	return dst->len;
 }
 
 
-/* Returns all matching tags found in the workspace. Adapted from tm_workspace_find, Anjuta 2.02
+/* Returns all matching tags found in the workspace.
  @param name The name of the tag to find.
  @param scope The scope name of the tag to find, or NULL.
  @param type The tag types to return (TMTagType). Can be a bitmask.
  @param attrs The attributes to sort and dedup on (0 terminated integer array).
- @param partial Whether partial match is allowed.
  @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
              -1 for all
- @return Array of matching tags. Do not free() it since it is a static member.
+ @return Array of matching tags.
 */
-const GPtrArray *
-tm_workspace_find_scoped (const char *name, const char *scope, TMTagType type,
-		TMTagAttrType *attrs, gboolean partial, langType lang, gboolean global_search)
+GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
+	TMTagAttrType *attrs, langType lang)
 {
-	static GPtrArray *tags = NULL;
+	GPtrArray *tags = g_ptr_array_new();
 
-	if (tags)
-		g_ptr_array_set_size (tags, 0);
-	else
-		tags = g_ptr_array_new ();
+	fill_find_tags_array(tags, theWorkspace->tags_array, name, scope, type, lang);
+	fill_find_tags_array(tags, theWorkspace->global_tags, name, scope, type, lang);
 
-	fill_find_tags_array (tags, theWorkspace->tags_array,
-						  name, scope, type, partial, lang, FALSE);
-	if (global_search)
-	{
-		/* for a scoped tag, I think we always want the same language */
-		fill_find_tags_array (tags, theWorkspace->global_tags,
-							  name, scope, type, partial, lang, FALSE);
-	}
 	if (attrs)
-		tm_tags_sort (tags, attrs, TRUE, FALSE);
+		tm_tags_sort(tags, attrs, TRUE, FALSE);
+
 	return tags;
 }
 
 
-static int
-find_scope_members_tags (const GPtrArray * all, GPtrArray * tags,
-						 const langType langJava, const char *name,
-						 const char *filename, gboolean no_definitions)
+static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src,
+	const char *name, langType lang, guint max_num)
 {
-	GPtrArray *local = g_ptr_array_new ();
-	unsigned int i;
-	TMTag *tag;
-	size_t len = strlen (name);
-	for (i = 0; (i < all->len); ++i)
+	TMTag **tag, *last = NULL;
+	guint i, count, num;
+
+	if (!src || !dst || !name || !*name)
+		return;
+
+	num = 0;
+	tag = tm_tags_find(src, name, TRUE, &count);
+	for (i = 0; i < count && num < max_num; ++i)
 	{
-		tag = TM_TAG (all->pdata[i]);
-		if (no_definitions && filename && tag->file &&
-			0 != strcmp (filename,
-						 tag->file->short_name))
-		{
-			continue;
-		}
-		if (tag && tag->scope && tag->scope[0] != '\0')
+		if (langs_compatible(lang, (*tag)->lang) &&
+			!tm_tag_is_anon(*tag) &&
+			(!last || g_strcmp0(last->name, (*tag)->name) != 0))
 		{
-			if (0 == strncmp (name, tag->scope, len))
-			{
-				g_ptr_array_add (local, tag);
-			}
+			g_ptr_array_add(dst, *tag);
+			last = *tag;
+			num++;
 		}
+		tag++;
 	}
-	if (local->len > 0)
-	{
-		unsigned int j;
-		TMTag *tag2;
-		char backup = 0;
-		char *s_backup = NULL;
-		char *var_type = NULL;
-		char *scope;
-		for (i = 0; (i < local->len); ++i)
-		{
-			tag = TM_TAG (local->pdata[i]);
-			scope = tag->scope;
-			if (scope && 0 == strcmp (name, scope))
-			{
-				g_ptr_array_add (tags, tag);
-				continue;
-			}
-			s_backup = NULL;
-			j = 0;				/* someone could write better code :P */
-			while (scope)
-			{
-				if (s_backup)
-				{
-					backup = s_backup[0];
-					s_backup[0] = '\0';
-					if (0 == strcmp (name, tag->scope))
-					{
-						j = local->len;
-						s_backup[0] = backup;
-						break;
-					}
-				}
-				if (tag->file
-					&& tag->file->lang == langJava)
-				{
-					scope = strrchr (tag->scope, '.');
-					if (scope)
-						var_type = scope + 1;
-				}
-				else
-				{
-					scope = strrchr (tag->scope, ':');
-					if (scope)
-					{
-						var_type = scope + 1;
-						scope--;
-					}
-				}
-				if (s_backup)
-				{
-					s_backup[0] = backup;
-				}
-				if (scope)
-				{
-					if (s_backup)
-					{
-						backup = s_backup[0];
-						s_backup[0] = '\0';
-					}
-					for (j = 0; (j < local->len); ++j)
-					{
-						if (i == j)
-							continue;
-						tag2 = TM_TAG (local->pdata[j]);
-						if (tag2->var_type &&
-							0 == strcmp (var_type, tag2->var_type))
-						{
-							break;
-						}
-					}
-					if (s_backup)
-						s_backup[0] = backup;
-				}
-				if (j < local->len)
-				{
-					break;
-				}
-				s_backup = scope;
-			}
-			if (j == local->len)
-			{
-				g_ptr_array_add (tags, tag);
-			}
-		}
-	}
-	g_ptr_array_free (local, TRUE);
-	return (int) tags->len;
 }
 
 
-/* Returns all matching members tags found in given struct/union/class name.
- @param name Name of the struct/union/class.
- @param file_tags A GPtrArray of edited file TMTag pointers (for search speedup, can be NULL).
- @return A GPtrArray of TMTag pointers to struct/union/class members */
-const GPtrArray *
-tm_workspace_find_scope_members (const GPtrArray * file_tags, const char *name,
-								 gboolean search_global, gboolean no_definitions)
+/* Returns tags with the specified prefix sorted by name. If there are several
+ tags with the same name, only one of them appears in the resulting array.
+ @param prefix The prefix of the tag to find.
+ @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
+             -1 for all.
+ @param max_num The maximum number of tags to return.
+ @return Array of matching tags sorted by their name.
+*/
+GPtrArray *tm_workspace_find_prefix(const char *prefix, langType lang, guint max_num)
 {
-	static GPtrArray *tags = NULL;
-	GPtrArray *local = NULL;
-	char *new_name = (char *) name;
-	char *filename = NULL;
-	int found = 0, del = 0;
-	static langType langJava = -1;
-	TMTag *tag = NULL;
-
-	/* FIXME */
-	/* langJava = getNamedLanguage ("Java"); */
+	TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
+	GPtrArray *tags = g_ptr_array_new();
 
-	g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL);
+	fill_find_tags_array_prefix(tags, theWorkspace->tags_array, prefix, lang, max_num);
+	fill_find_tags_array_prefix(tags, theWorkspace->global_tags, prefix, lang, max_num);
 
-	if (!tags)
-		tags = g_ptr_array_new ();
+	tm_tags_sort(tags, attrs, TRUE, FALSE);
+	if (tags->len > max_num)
+		tags->len = max_num;
 
-	while (1)
-	{
-		const GPtrArray *tags2;
-		guint got = 0;
-		TMTagType types = (tm_tag_class_t | tm_tag_namespace_t |
-						   tm_tag_struct_t | tm_tag_typedef_t |
-						   tm_tag_union_t | tm_tag_enum_t);
+	return tags;
+}
 
-		if (file_tags)
-		{
-			g_ptr_array_set_size (tags, 0);
-			got = fill_find_tags_array (tags, file_tags,
-										  new_name, NULL, types, FALSE, -1, FALSE);
-		}
-		if (got)
-		{
-			tags2 = tags;
-		}
-		else
-		{
-			TMTagAttrType attrs[] = {
-				tm_tag_attr_name_t, tm_tag_attr_type_t,
-				tm_tag_attr_none_t
-			};
-			tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1);
-		}
 
-		if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0])))
-		{
-			if (tag->type == tm_tag_typedef_t && tag->var_type
-				&& tag->var_type[0] != '\0')
-			{
-				char *tmp_name;
-				tmp_name = tag->var_type;
-				if (strcmp(tmp_name, new_name) == 0) {
-					new_name = NULL;
-				}
-				else {
-					new_name = tmp_name;
-				}
-				continue;
-			}
-			filename = (tag->file ?
-						tag->file->short_name : NULL);
-			if (tag->scope && tag->scope[0] != '\0')
-			{
-				del = 1;
-				if (tag->file &&
-					tag->file->lang == langJava)
-				{
-					new_name = g_strdup_printf ("%s.%s",
-												tag->scope,
-												new_name);
-				}
-				else
-				{
-					new_name = g_strdup_printf ("%s::%s",
-												tag->scope,
-												new_name);
-				}
-			}
-			break;
-		}
-		else
-		{
-			return NULL;
-		}
-	}
+/* Gets all members of type_tag; search them inside the all array.
+ * The namespace parameter determines whether we are performing the "namespace"
+ * search (user has typed something like "A::" where A is a type) or "scope" search
+ * (user has typed "a." where a is a global struct-like variable). With the
+ * namespace search we return all direct descendants of any type while with the
+ * scope search we return only those which can be invoked on a variable (member,
+ * method, etc.). */
+static GPtrArray *
+find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace)
+{
+	TMTagType member_types = tm_tag_max_t & ~(TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t);
+	GPtrArray *tags = g_ptr_array_new();
+	gchar *scope;
+	guint i;
 
-	g_ptr_array_set_size (tags, 0);
+	if (namespace)
+		member_types = tm_tag_max_t;
 
-	if (no_definitions && tag && tag->file)
-	{
-		local = tm_tags_extract (tag->file->tags_array,
-								 (tm_tag_function_t | tm_tag_prototype_t |
-								  tm_tag_member_t | tm_tag_field_t |
-								  tm_tag_method_t | tm_tag_enumerator_t));
-	}
+	if (type_tag->scope && *(type_tag->scope))
+		scope = g_strconcat(type_tag->scope, tm_tag_context_separator(type_tag->lang), type_tag->name, NULL);
 	else
+		scope = g_strdup(type_tag->name);
+
+	for (i = 0; i < all->len; ++i)
 	{
-		local = tm_tags_extract (theWorkspace->tags_array,
-								 (tm_tag_function_t | tm_tag_prototype_t |
-								  tm_tag_member_t | tm_tag_field_t |
-								  tm_tag_method_t | tm_tag_enumerator_t));
-	}
-	if (local)
-	{
-		found = find_scope_members_tags (local, tags, langJava, new_name,
-										 filename, no_definitions);
-		g_ptr_array_free (local, TRUE);
-	}
-	if (!found && search_global)
-	{
-		GPtrArray *global = tm_tags_extract (theWorkspace->global_tags,
-											 (tm_tag_member_t |
-											  tm_tag_prototype_t |
-											  tm_tag_field_t |
-											  tm_tag_method_t |
-											  tm_tag_function_t |
-											  tm_tag_enumerator_t
-											  |tm_tag_struct_t | tm_tag_typedef_t |
-											  tm_tag_union_t | tm_tag_enum_t));
-		if (global)
+		TMTag *tag = TM_TAG (all->pdata[i]);
+
+		if (tag && (tag->type & member_types) &&
+			tag->scope && tag->scope[0] != '\0' &&
+			langs_compatible(tag->lang, type_tag->lang) &&
+			strcmp(scope, tag->scope) == 0 &&
+			(!namespace || !tm_tag_is_anon(tag)))
 		{
-			find_scope_members_tags (global, tags, langJava, new_name,
-									 filename, no_definitions);
-			g_ptr_array_free (global, TRUE);
+			g_ptr_array_add (tags, tag);
 		}
 	}
-	if (del)
+
+	g_free(scope);
+
+	if (tags->len == 0)
 	{
-		g_free (new_name);
+		g_ptr_array_free(tags, TRUE);
+		return NULL;
 	}
 
 	return tags;
 }
 
 
-#ifdef TM_DEBUG
-
-/* Dumps the workspace tree - useful for debugging */
-void tm_workspace_dump(void)
+static gchar *strip_type(const gchar *scoped_name, langType lang)
 {
-	guint i;
-
-#ifdef TM_DEBUG
-	g_message("Dumping TagManager workspace tree..");
-#endif
-	for (i=0; i < theWorkspace->source_files->len; ++i)
+	if (scoped_name != NULL)
 	{
-		TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
-		fprintf(stderr, "%s", source_file->file_name);
+		/* remove scope prefix */
+		const gchar *sep = tm_tag_context_separator(lang);
+		const gchar *base = g_strrstr(scoped_name, sep);
+		gchar *name = base ? g_strdup(base + strlen(sep)) : g_strdup(scoped_name);
+
+		/* remove pointers */
+		g_strdelimit(name, "*^", ' ');
+		g_strstrip(name);
+
+		return name;
 	}
+	return NULL;
 }
-#endif /* TM_DEBUG */
-
 
-#if 0
 
-static int
-find_namespace_members_tags (const GPtrArray * all, GPtrArray * tags,
-						 	const langType langJava, const char *name,
-						 	const char *filename)
+/* Gets all members of the type with the given name; search them inside tags_array */
+static GPtrArray *
+find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile *file,
+	langType lang, gboolean namespace)
 {
-	GPtrArray *local = g_ptr_array_new ();
-	unsigned int i;
-	TMTag *tag;
-	size_t len = strlen (name);
+	GPtrArray *res = NULL;
+	gchar *type_name;
+	guint i;
 
-	g_return_val_if_fail (all != NULL, 0);
+	g_return_val_if_fail(name && *name, NULL);
 
-	for (i = 0; (i < all->len); ++i)
-	{
-		tag = TM_TAG (all->pdata[i]);
-		if (filename && tag->file &&
-			0 != strcmp (filename,
-						 tag->file->short_name))
-		{
-			continue;
-		}
-
-		if (tag && tag->scope && tag->scope[0] != '\0')
-		{
-			if (0 == strncmp (name, tag->scope, len))
-			{
-				g_ptr_array_add (local, tag);
-			}
-		}
-	}
+	type_name = g_strdup(name);
 
-	if (local->len > 0)
+	/* Check if type_name is a type that can possibly contain members.
+	 * Try to resolve intermediate typedefs to get the real type name. Also
+	 * add scope information to the name if applicable.
+	 * The loop below loops only when resolving typedefs - avoid possibly infinite
+	 * loop when typedefs create a cycle by adding some limits. */
+	for (i = 0; i < 5; i++)
 	{
-		char *scope;
-		for (i = 0; (i < local->len); ++i)
-		{
-			tag = TM_TAG (local->pdata[i]);
-			scope = tag->scope;
-
-			/* if we wanna complete something like
-			 * namespace1::
-			 * we'll just return the tags that have "namespace1"
-			 * as their scope. So we won't return classes/members/namespaces
-			 * under, for example, namespace2, where namespace1::namespace2
-			 */
-			if (scope && 0 == strcmp (name, scope))
-			{
-				g_ptr_array_add (tags, tag);
-			}
-		}
-	}
+		guint j;
+		GPtrArray *type_tags;
+		TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
+		TMTag *tag = NULL;
 
-	g_ptr_array_free (local, TRUE);
-	return (int) tags->len;
-}
+		if (!namespace)
+			types &= ~tm_tag_enum_t;
 
-static const GPtrArray *
-tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *name,
-								 gboolean search_global)
-{
-	static GPtrArray *tags = NULL;
-	GPtrArray *local = NULL;
-	char *new_name = (char *) name;
-	char *filename = NULL;
-	int found = 0, del = 0;
-	static langType langJava = -1;
-	TMTag *tag = NULL;
+		type_tags = g_ptr_array_new();
+		fill_find_tags_array(type_tags, tags_array, type_name, NULL, types, lang);
 
-	g_return_val_if_fail (name && name[0] != '\0', NULL);
+		for (j = 0; j < type_tags->len; j++)
+		{
+			TMTag *test_tag = TM_TAG(type_tags->pdata[j]);
 
-	if (!tags)
-		tags = g_ptr_array_new ();
+			/* anonymous type defined in a different file than the variable -
+			 * this isn't the type we are looking for */
+			if (tm_tag_is_anon(test_tag) && (file != test_tag->file || test_tag->file == NULL))
+				continue;
 
-	while (1)
-	{
-		const GPtrArray *tags2;
-		guint got = 0;
-		TMTagType types = (tm_tag_class_t
-						   tm_tag_struct_t | tm_tag_typedef_t |
-						   tm_tag_union_t | tm_tag_enum_t);
+			tag = test_tag;
 
-		if (file_tags)
-		{
-			g_ptr_array_set_size (tags, 0);
-			got = fill_find_tags_array (tags, file_tags,
-										  new_name, NULL, types, FALSE, -1, FALSE);
+			/* prefer non-typedef tags because we can be sure they contain members */
+			if (test_tag->type != tm_tag_typedef_t)
+				break;
 		}
 
+		g_ptr_array_free(type_tags, TRUE);
 
-		if (got)
-		{
-			tags2 = tags;
-		}
-		else
-		{
-			TMTagAttrType attrs[] = {
-				tm_tag_attr_name_t, tm_tag_attr_type_t,
-				tm_tag_attr_none_t
-			};
-			tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1);
-		}
+		if (!tag) /* not a type that can contain members */
+			break;
 
-		if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0])))
+		/* intermediate typedef - resolve to the real type */
+		if (tag->type == tm_tag_typedef_t)
 		{
-			if (tag->type == tm_tag_typedef_t && tag->var_type
-				&& tag->var_type[0] != '\0')
+			if (tag->var_type && tag->var_type[0] != '\0')
 			{
-				new_name = tag->var_type;
+				g_free(type_name);
+				type_name = strip_type(tag->var_type, tag->lang);
+				file = tag->file;
 				continue;
 			}
-			filename = (tag->file ?
-						tag->file->short_name : NULL);
-			if (tag->scope && tag->scope[0] != '\0')
-			{
-				del = 1;
-				if (tag->file &&
-					tag->file->lang == langJava)
-				{
-					new_name = g_strdup_printf ("%s.%s",
-												tag->scope,
-												new_name);
-				}
-				else
-				{
-					new_name = g_strdup_printf ("%s::%s",
-												tag->scope,
-												new_name);
-				}
-			}
 			break;
 		}
-		else
+		else /* real type with members */
 		{
-			return NULL;
+			/* use the same file as the composite type if file information available */
+			res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace);
+			break;
 		}
 	}
 
-	g_ptr_array_set_size (tags, 0);
+	g_free(type_name);
 
-	if (tag && tag->file)
-	{
-		local = tm_tags_extract (tag->file->tags_array,
-								 (tm_tag_function_t |
-								  tm_tag_field_t | tm_tag_enumerator_t |
-								  tm_tag_namespace_t | tm_tag_class_t ));
-	}
-	else
-	{
-		local = tm_tags_extract (theWorkspace->tags_array,
-								 (tm_tag_function_t | tm_tag_prototype_t |
-								  tm_tag_member_t |
-								  tm_tag_field_t | tm_tag_enumerator_t |
-								  tm_tag_namespace_t | tm_tag_class_t ));
-	}
+	return res;
+}
 
-	if (local)
+
+/* Checks whether a member tag is directly accessible from method with method_scope */
+static gboolean member_at_method_scope(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag,
+	langType lang)
+{
+	const gchar *sep = tm_tag_context_separator(lang);
+	gboolean ret = FALSE;
+	gchar **comps;
+	guint len;
+
+	/* method scope is in the form ...::class_name::method_name */
+	comps = g_strsplit (method_scope, sep, 0);
+	len = g_strv_length(comps);
+	if (len > 1)
 	{
-		found = find_namespace_members_tags (local, tags,
-										 langJava, new_name, filename);
-		g_ptr_array_free (local, TRUE);
+		gchar *method, *member_scope, *cls, *cls_scope;
+
+		/* get method/member scope */
+		method = comps[len - 1];
+		comps[len - 1] = NULL;
+		member_scope = g_strjoinv(sep, comps);
+		comps[len - 1] = method;
+
+		/* get class scope */
+		cls = comps[len - 2];
+		comps[len - 2] = NULL;
+		cls_scope = g_strjoinv(sep, comps);
+		comps[len - 2] = cls;
+		cls_scope = strlen(cls_scope) > 0 ? cls_scope : NULL;
+
+		/* check whether member inside the class */
+		if (g_strcmp0(member_tag->scope, member_scope) == 0)
+		{
+			const GPtrArray *src = member_tag->file ? member_tag->file->tags_array : tags;
+			GPtrArray *cls_tags = g_ptr_array_new();
+
+			/* check whether the class exists */
+			fill_find_tags_array(cls_tags, src, cls, cls_scope, TM_TYPE_WITH_MEMBERS | tm_tag_namespace_t, lang);
+			ret = cls_tags->len > 0;
+			g_ptr_array_free(cls_tags, TRUE);
+		}
+
+		g_free(cls_scope);
+		g_free(member_scope);
 	}
 
+	g_strfreev(comps);
+	return ret;
+}
+
+
+/* For an array of variable/type tags, find members inside the types */
+static GPtrArray *
+find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, langType lang,
+	gboolean member, const gchar *current_scope)
+{
+	GPtrArray *member_tags = NULL;
+	guint i;
 
-	if (!found && search_global)
+	/* there may be several variables/types with the same name - try each of them until
+	 * we find something */
+	for (i = 0; i < tags->len && !member_tags; i++)
 	{
-		GPtrArray *global = tm_tags_extract (theWorkspace->global_tags,
-											 (tm_tag_member_t |
-											  tm_tag_prototype_t |
-											  tm_tag_field_t |
-											  tm_tag_method_t |
-											  tm_tag_function_t |
-											  tm_tag_enumerator_t |
-											  tm_tag_namespace_t |
-											  tm_tag_class_t ));
-
-		if (global)
+		TMTag *tag = TM_TAG(tags->pdata[i]);
+		TMTagType member_types = tm_tag_member_t | tm_tag_field_t | tm_tag_method_t;
+		TMTagType types = TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t;
+
+		if (tag->type & types)  /* type: namespace search */
+		{
+			if (tag->type & tm_tag_typedef_t)
+				member_tags = find_scope_members(searched_array, tag->name, tag->file, lang, TRUE);
+			else
+				member_tags = find_scope_members_tags(tag->file ? tag->file->tags_array : searched_array,
+					tag, TRUE);
+		}
+		else if (tag->var_type)  /* variable: scope search */
 		{
-			find_namespace_members_tags (global, tags, langJava,
-									 new_name, filename);
-/*/
-			DEBUG_PRINT ("returning these");
-  		    gint i;
-			for (i=0; i < tags->len; i++) {
-				TMTag *cur_tag;
-
-				cur_tag = (TMTag*)g_ptr_array_index (tags, i);
-				tm_tag_print (cur_tag, stdout );
+			/* The question now is whether we should use member tags (such as
+			 * tm_tag_field_t, tm_tag_member_t) or not. We want them if member==TRUE
+			 * (which means user has typed something like foo.bar.) or if we are
+			 * inside a method where foo is a class member, we want scope completion
+			 * for foo. */
+			if (!(tag->type & member_types) || member ||
+				member_at_method_scope(tags, current_scope, tag, lang))
+			{
+				gchar *tag_type = strip_type(tag->var_type, tag->lang);
+
+				member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE);
+				g_free(tag_type);
 			}
-/*/
-			g_ptr_array_free (global, TRUE);
 		}
 	}
 
+	return member_tags;
+}
+
+
+/* Returns all member tags of a struct/union/class if the provided name is a variable
+ of such a type or the name of the type.
+ @param source_file TMSourceFile of the edited source file or NULL if not available
+ @param name Name of the variable/type whose members are searched
+ @param function TRUE if the name is a name of a function
+ @param member TRUE if invoked on class/struct member (e.g. after the last dot in foo.bar.)
+ @param current_scope The current scope in the editor
+ @return A GPtrArray of TMTag pointers to struct/union/class members or NULL when not found */
+GPtrArray *
+tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
+	gboolean function, gboolean member, const gchar *current_scope)
+{
+	langType lang = source_file ? source_file->lang : -1;
+	GPtrArray *tags, *member_tags = NULL;
+	TMTagType function_types = tm_tag_function_t | tm_tag_method_t |
+		tm_tag_macro_with_arg_t | tm_tag_prototype_t;
+	TMTagType tag_type = tm_tag_max_t &
+		~(function_types | tm_tag_enumerator_t | tm_tag_namespace_t | tm_tag_package_t);
+	TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0};
+
+	if (function)
+		tag_type = function_types;
+
+	/* tags corresponding to the variable/type name */
+	tags = tm_workspace_find(name, NULL, tag_type, NULL, lang);
+
+	/* Start searching inside the source file, continue with workspace tags and
+	 * end with global tags. This way we find the "closest" tag to the current
+	 * file in case there are more of them. */
+	if (source_file)
+		member_tags = find_scope_members_all(tags, source_file->tags_array,
+											 lang, member, current_scope);
+	if (!member_tags)
+		member_tags = find_scope_members_all(tags, theWorkspace->tags_array, lang,
+											 member, current_scope);
+	if (!member_tags)
+		member_tags = find_scope_members_all(tags, theWorkspace->global_tags, lang,
+											 member, current_scope);
+
+	g_ptr_array_free(tags, TRUE);
+
+	tm_tags_dedup(member_tags, sort_attr, FALSE);
+
+	return member_tags;
+}
+
+
+#ifdef TM_DEBUG
+
+/* Dumps the workspace tree - useful for debugging */
+void tm_workspace_dump(void)
+{
+	guint i;
 
-	if (del)
+#ifdef TM_DEBUG
+	g_message("Dumping TagManager workspace tree..");
+#endif
+	for (i=0; i < theWorkspace->source_files->len; ++i)
 	{
-		g_free (new_name);
+		TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
+		fprintf(stderr, "%s", source_file->file_name);
 	}
-
-	return tags;
 }
+#endif /* TM_DEBUG */
+
 
+#if 0
 
 /* Returns a list of parent classes for the given class name
  @param name Name of the class
@@ -1353,7 +1127,7 @@ static const GPtrArray *tm_workspace_get_parents(const gchar *name)
 		parents = g_ptr_array_new();
 	else
 		g_ptr_array_set_size(parents, 0);
-	matches = tm_workspace_find(name, tm_tag_class_t, type, FALSE, -1);
+	matches = tm_workspace_find(name, NULL, tm_tag_class_t, type, -1);
 	if ((NULL == matches) || (0 == matches->len))
 		return NULL;
 	g_ptr_array_add(parents, matches->pdata[0]);
@@ -1372,7 +1146,7 @@ static const GPtrArray *tm_workspace_get_parents(const gchar *name)
 				}
 				if (parents->len == j)
 				{
-					matches = tm_workspace_find(*klass, tm_tag_class_t, type, FALSE, -1);
+					matches = tm_workspace_find(*klass, NULL, tm_tag_class_t, type, -1);
 					if ((NULL != matches) && (0 < matches->len))
 						g_ptr_array_add(parents, matches->pdata[0]);
 				}


Modified: tagmanager/src/tm_workspace.h
16 lines changed, 7 insertions(+), 9 deletions(-)
===================================================================
@@ -33,6 +33,7 @@ typedef struct
 	GPtrArray *tags_array; /**< Sorted tags from all source files 
 		(just pointers to source file tags, the tag objects are owned by the source files) */
 	GPtrArray *typename_array; /* Typename tags for syntax highlighting (pointers owned by source files) */
+	GPtrArray *global_typename_array; /* Like above for global tags */
 } TMWorkspace;
 
 
@@ -54,17 +55,14 @@ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode);
 gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes,
 	int includes_count, const char *tags_file, int lang);
 
-const GPtrArray *tm_workspace_find(const char *name, TMTagType type, TMTagAttrType *attrs,
-	gboolean partial, langType lang);
+GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type,
+	TMTagAttrType *attrs, langType lang);
 
-const GPtrArray *
-tm_workspace_find_scoped (const char *name, const char *scope, TMTagType type,
-	TMTagAttrType *attrs, gboolean partial, langType lang, gboolean global_search);
+GPtrArray *tm_workspace_find_prefix(const char *prefix, langType lang, guint max_num);
+
+GPtrArray *tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
+	gboolean function, gboolean member, const gchar *current_scope);
 
-const GPtrArray *tm_workspace_find_scope_members(const GPtrArray *file_tags,
-                                                 const char *scope_name,
-                                                 gboolean find_global,
-                                                 gboolean no_definitions);
 
 void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file);
 



--------------
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