[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, ¤t_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