Branch: refs/heads/master Author: Jiří Techet techet@gmail.com Committer: Jiří Techet techet@gmail.com Date: Tue, 14 Jul 2015 07:44:33 UTC Commit: 5c18b3d1328b8d3dff923a2a02bc3cc6b41a333c https://github.com/geany/geany/commit/5c18b3d1328b8d3dff923a2a02bc3cc6b41a33...
Log Message: ----------- Improve scoped search
This patch contains a bit too many things which are however related.
It started by the part in editor.c (where we previously used only the first type we found to perform scoped search) by going through all the possible variable types until the scoped search returns some result (this is useful if variable foo is used once as int and once as struct and if the int is the first type found, we won't get the struct's members).
This didn't work. After an hour of debugging, it turned out that because tm_workspace_find_scope_members() calls internally tm_workspace_find() and this function returns static array, this invalidates the array returned by the tm_workspace_find() used previously to get all the possible variable types.
Since this is really dangerous and hard to notice, I tried to eliminate the static returns from both tm_workspace_find() and tm_workspace_find_scoped_members().
The tm_workspace_find_scoped_members() function is where I got stuck because as I started to understand what it's doing, I found many problems there. This patch does the following in this function:
1. Eliminates search_global and no_definitions parameters because we always search the whole workspace and this simplifies the slightly strange logic at the end of the function. 2. Returns members from global tags even when something found in workspace tags - previously global tags were skipped when something was found from workspace tags but I don't see a reason why. 3. Adds the lang parameter to restrict tags by language (we do this with normal search and the same should be done here). 4. Previously when searching for types with members the function returned NULL when more than one such type was found (there should have been >=1 instead of ==1 at line 906). This patch improves the logic a bit and if multiple types are found, it tries to use the one which is other than typedef because it probably has some members (the typedef can resolve to e.g. int). 5. Previously the function prevented only direct typedef loops like
typedef A B; typedef B A;
but a loop like A->B->C->A would lead to an infinite cycle. This patch restricts the number how many times the typedef can be resolved by using for loop with limited number of repetitions and giving up when nothing useful is resolved. 6. Finally the patch tries to simplify the function a bit, make it easier to read and adds some comments to make it clearer what the function does.
Modified Paths: -------------- src/editor.c tagmanager/src/tm_workspace.c tagmanager/src/tm_workspace.h
Modified: src/editor.c 60 lines changed, 41 insertions(+), 19 deletions(-) =================================================================== @@ -703,9 +703,10 @@ static void autocomplete_scope(GeanyEditor *editor) gint pos = sci_get_current_position(editor->sci); gchar typed = sci_get_char_at(sci, pos - 1); gchar *name; - const GPtrArray *tags = NULL; + GPtrArray *tags; const TMTag *tag; GeanyFiletype *ft = editor->document->file_type; + guint i;
if (ft->id == GEANY_FILETYPES_C || ft->id == GEANY_FILETYPES_CPP) { @@ -728,20 +729,27 @@ static void autocomplete_scope(GeanyEditor *editor)
tags = tm_workspace_find(name, NULL, tm_tag_max_t, NULL, FALSE, ft->lang); g_free(name); - if (!tags || tags->len == 0) - return;
- tag = g_ptr_array_index(tags, 0); - name = tag->var_type; - if (name) + foreach_ptr_array(tag, i, tags) { - TMSourceFile *obj = editor->document->tm_file; - - tags = tm_workspace_find_scope_members(obj ? obj->tags_array : NULL, - name, TRUE, FALSE); - if (tags) - show_tags_list(editor, tags, 0); + if (tag->var_type) + { + TMSourceFile *sf = editor->document->tm_file; + GPtrArray *member_tags; + gboolean found; + + member_tags = tm_workspace_find_scope_members(sf ? sf->tags_array : NULL, + tag->var_type, + sf ? sf->lang : -1); + found = member_tags && member_tags->len > 0; + if (found) + show_tags_list(editor, member_tags, 0); + g_ptr_array_free(member_tags, TRUE); + if (found) + break; + } } + g_ptr_array_free(tags, TRUE); }
@@ -1838,7 +1846,7 @@ static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id
static gchar *find_calltip(const gchar *word, GeanyFiletype *ft) { - const GPtrArray *tags; + 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; @@ -1851,18 +1859,25 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft) /* use all types in case language uses wrong tag type e.g. python "members" instead of "methods" */ tags = tm_workspace_find(word, NULL, tm_tag_max_t, attrs, FALSE, 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("this", tag->name, arg_types, attrs, FALSE, ft->lang); if (tags->len == 0) + { + g_ptr_array_free(tags, TRUE); return NULL; + } }
/* remove tags with no argument list */ @@ -1875,7 +1890,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, @@ -1912,6 +1930,9 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft) break; } } + + g_ptr_array_free(tags, TRUE); + if (str) { gchar *result = str->str; @@ -2034,20 +2055,21 @@ 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, NULL, tm_tag_max_t, attrs, TRUE, doc->file_type->lang); - if (tags) - { + 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; }
Modified: tagmanager/src/tm_workspace.c 230 lines changed, 100 insertions(+), 130 deletions(-) =================================================================== @@ -672,6 +672,20 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i }
+static gboolean langs_compatible(langType lang, langType other) +{ + 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; + + return FALSE; +} + + static guint fill_find_tags_array(GPtrArray *dst, const GPtrArray *src, const char *name, const char *scope, TMTagType type, gboolean partial, langType lang) { @@ -685,17 +699,8 @@ static guint fill_find_tags_array(GPtrArray *dst, const GPtrArray *src, matches = tm_tags_find(src, name, partial, TRUE, &tagCount); for (tagIter = 0; tagIter < tagCount; ++tagIter) { - gint tag_lang = (*matches)->lang; - gint tag_lang_alt = tag_lang; - - /* Accept CPP tags for C lang and vice versa */ - if (tag_lang == TM_PARSER_C) - tag_lang_alt = TM_PARSER_CPP; - else if (tag_lang == TM_PARSER_CPP) - tag_lang_alt = TM_PARSER_C; - if ((type & (*matches)->type) && - (lang == -1 || tag_lang == lang || tag_lang_alt == lang) && + langs_compatible(lang, (*matches)->lang) && (!scope || g_strcmp0((*matches)->scope, scope) == 0)) g_ptr_array_add(dst, *matches);
@@ -714,17 +719,12 @@ static guint fill_find_tags_array(GPtrArray *dst, const GPtrArray *src, @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(const char *name, const char *scope, TMTagType type, +GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type, TMTagAttrType *attrs, gboolean partial, langType lang) { - static GPtrArray *tags = NULL; - - if (tags) - g_ptr_array_set_size(tags, 0); - else - tags = g_ptr_array_new(); + GPtrArray *tags = g_ptr_array_new();
fill_find_tags_array(tags, theWorkspace->tags_array, name, scope, type, partial, lang); fill_find_tags_array(tags, theWorkspace->global_tags, name, scope, type, partial, lang); @@ -738,8 +738,7 @@ const GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagTyp
static int find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, - const langType langJava, const char *name, - const char *filename, gboolean no_definitions) + const char *name, langType lang) { GPtrArray *local = g_ptr_array_new (); unsigned int i; @@ -748,13 +747,8 @@ find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, for (i = 0; (i < all->len); ++i) { tag = TM_TAG (all->pdata[i]); - if (no_definitions && filename && tag->file && - 0 != strcmp (filename, - tag->file->short_name)) - { - continue; - } - if (tag && tag->scope && tag->scope[0] != '\0') + if (tag && tag->scope && tag->scope[0] != '\0' && + langs_compatible(tag->lang, lang)) { if (0 == strncmp (name, tag->scope, len)) { @@ -795,7 +789,7 @@ find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, } } if (tag->file - && tag->file->lang == langJava) + && tag->file->lang == TM_PARSER_JAVA) { scope = strrchr (tag->scope, '.'); if (scope) @@ -853,142 +847,118 @@ find_scope_members_tags (const GPtrArray * all, GPtrArray * tags,
/* Returns all matching members tags found in given struct/union/class name. - @param name Name of the struct/union/class. @param file_tags A GPtrArray of edited file TMTag pointers (for search speedup, can be NULL). + @param name Name of the struct/union/class. @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) +GPtrArray * +tm_workspace_find_scope_members (const GPtrArray * file_tags, const char *name, langType lang) { - 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"); */ + gboolean has_members = FALSE; + GPtrArray *tags; + gchar *type_name; + guint i;
- g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL); + g_return_val_if_fail(name && *name, NULL);
- if (!tags) - tags = g_ptr_array_new (); + type_name = g_strdup(name);
- while (1) + /* First 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 only result of this + * part is the updated type_name and boolean has_members. + * 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++) { - const GPtrArray *tags2; - guint got = 0; + guint j; + TMTag *tag = NULL; + GPtrArray *type_tags; 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);
+ /* try to get the type from file_tags first */ + type_tags = g_ptr_array_new(); if (file_tags) - { - g_ptr_array_set_size (tags, 0); - got = fill_find_tags_array (tags, file_tags, - new_name, NULL, types, FALSE, -1); - } - if (got) - { - tags2 = tags; - } - else + fill_find_tags_array(type_tags, file_tags, type_name, NULL, types, FALSE, lang); + + /* file not specified or type definition not in the file */ + if (type_tags->len == 0) { TMTagAttrType attrs[] = { tm_tag_attr_name_t, tm_tag_attr_type_t, tm_tag_attr_none_t }; - tags2 = tm_workspace_find (new_name, NULL, types, attrs, FALSE, -1); + + g_ptr_array_free(type_tags, TRUE); + type_tags = tm_workspace_find(type_name, NULL, types, attrs, FALSE, lang); }
- if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0]))) + if (type_tags) { - if (tag->type == tm_tag_typedef_t && tag->var_type - && tag->var_type[0] != '\0') + for (j = 0; j < type_tags->len; j++) { - char *tmp_name; - tmp_name = tag->var_type; - if (strcmp(tmp_name, new_name) == 0) { - new_name = NULL; - } - else { - new_name = tmp_name; - } - continue; + tag = TM_TAG(type_tags->pdata[j]); + /* prefer non-typedef tags because we can be sure they contain members */ + if (tag->type != tm_tag_typedef_t) + break; } - filename = (tag->file ? - tag->file->short_name : NULL); - if (tag->scope && tag->scope[0] != '\0') + } + + g_ptr_array_free(type_tags, TRUE); + + if (!tag) /* not a type that can contain members */ + break; + + /* intermediate typedef - resolve to the real type */ + if (tag->type == tm_tag_typedef_t && tag->var_type && tag->var_type[0] != '\0') + { + g_free(type_name); + type_name = g_strdup(tag->var_type); + continue; + } + else /* real type with members */ + { + has_members = TRUE; + if (tag->scope && *(tag->scope)) { - del = 1; - if (tag->file && - tag->file->lang == langJava) - { - new_name = g_strdup_printf ("%s.%s", - tag->scope, - new_name); - } + gchar *tmp_name = type_name; + + if (tag->file && tag->file->lang == TM_PARSER_JAVA) + type_name = g_strdup_printf("%s.%s", tag->scope, type_name); else - { - new_name = g_strdup_printf ("%s::%s", - tag->scope, - new_name); - } + type_name = g_strdup_printf("%s::%s", tag->scope, type_name); + g_free(tmp_name); } break; } - else - { - return NULL; - } }
- g_ptr_array_set_size (tags, 0); + tags = g_ptr_array_new();
- if (no_definitions && tag && tag->file) - { - local = tm_tags_extract (tag->file->tags_array, - (tm_tag_function_t | tm_tag_prototype_t | - tm_tag_member_t | tm_tag_field_t | - tm_tag_method_t | tm_tag_enumerator_t)); - } - else - { - local = tm_tags_extract (theWorkspace->tags_array, - (tm_tag_function_t | tm_tag_prototype_t | - tm_tag_member_t | tm_tag_field_t | - tm_tag_method_t | tm_tag_enumerator_t)); - } - if (local) + if (has_members) { - 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) + GPtrArray *extracted; + TMTagType member_types = (tm_tag_function_t | tm_tag_prototype_t | + tm_tag_member_t | tm_tag_field_t | + tm_tag_method_t | tm_tag_enumerator_t); + + /* Now get the tags from tags_array and global_tags corresponding to type_name */ + extracted = tm_tags_extract(theWorkspace->tags_array, member_types); + if (extracted) { - find_scope_members_tags (global, tags, langJava, new_name, - filename, no_definitions); - g_ptr_array_free (global, TRUE); + find_scope_members_tags(extracted, tags, type_name, lang); + g_ptr_array_free(extracted, TRUE); + } + extracted = tm_tags_extract(theWorkspace->global_tags, member_types); + if (extracted) + { + find_scope_members_tags(extracted, tags, type_name, lang); + g_ptr_array_free(extracted, TRUE); } } - if (del) - { - g_free (new_name); - } + + g_free(type_name);
return tags; }
Modified: tagmanager/src/tm_workspace.h 8 lines changed, 3 insertions(+), 5 deletions(-) =================================================================== @@ -54,13 +54,11 @@ 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, const char *scope, TMTagType type, +GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type, TMTagAttrType *attrs, gboolean partial, langType lang);
-const GPtrArray *tm_workspace_find_scope_members(const GPtrArray *file_tags, - const char *scope_name, - gboolean find_global, - gboolean no_definitions); +GPtrArray *tm_workspace_find_scope_members(const GPtrArray *file_tags, + const char *scope_name, langType lang);
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).