diff --git a/doc/geany.txt b/doc/geany.txt index 4c2b006a78..2ac4915b4e 100644 --- a/doc/geany.txt +++ b/doc/geany.txt @@ -937,6 +937,10 @@ preferences`_, default 4) or when the *Complete word* keybinding is pressed (configurable, see `Editor keybindings`_, default Ctrl-Space). +For some languages the autocompletion list is ordered by heuristics to +attempt to show names that are more likely to be what the user wants +close to the top of the list. + When the defined keybinding is typed and the *Autocomplete all words in document* preference (in `Editor Completions preferences`_) is selected then the autocompletion list will show all matching words @@ -986,9 +990,11 @@ When you type ``foo.`` it will show an autocompletion list with 'i' and 'c' symbols. It only works for languages that set parent scope names for e.g. struct -members. Currently this means C-like languages. The C parser only -parses global scopes, so this won't work for structs or objects declared -in local scope. +members. Most languages only parse global definitions and so scope +autocompletion will not work for names declared in local scope +(e.g. inside functions). A few languages parse both local and global +symbols (e.g. C/C++ parsers) and for these parsers scope autocompletion +works also for local variables. User-definable snippets diff --git a/src/editor.c b/src/editor.c index dd2fd84c2f..04a6f9b4dc 100644 --- a/src/editor.c +++ b/src/editor.c @@ -601,6 +601,7 @@ static void show_autocomplete(ScintillaObject *sci, gsize rootlen, GString *word } /* store whether a calltip is showing, so we can reshow it after autocompletion */ calltip.set = (gboolean) SSM(sci, SCI_CALLTIPACTIVE, 0, 0); + SSM(sci, SCI_AUTOCSETORDER, SC_ORDER_CUSTOM, 0); SSM(sci, SCI_AUTOCSHOW, rootlen, (sptr_t) words->str); } @@ -698,6 +699,7 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize { ScintillaObject *sci = editor->sci; gint pos = sci_get_current_position(editor->sci); + gint line = sci_get_current_line(editor->sci) + 1; gchar typed = sci_get_char_at(sci, pos - 1); gchar brace_char; gchar *name; @@ -764,7 +766,7 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize 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, scope_sep_typed); + member, current_scope, line, scope_sep_typed); if (tags) { GPtrArray *filtered = g_ptr_array_new(); @@ -2019,12 +2021,19 @@ gboolean editor_show_calltip(GeanyEditor *editor, gint pos) static gboolean autocomplete_tags(GeanyEditor *editor, GeanyFiletype *ft, const gchar *root, gsize rootlen) { + GeanyDocument *doc = editor->document; + const gchar *current_scope = NULL; + guint current_line; GPtrArray *tags; gboolean found; - g_return_val_if_fail(editor, FALSE); + g_return_val_if_fail(editor && doc, FALSE); + + symbols_get_current_function(doc, ¤t_scope); + current_line = sci_get_current_line(editor->sci) + 1; - tags = tm_workspace_find_prefix(root, ft->lang, editor_prefs.autocompletion_max_entries); + tags = tm_workspace_find_prefix(root, doc->tm_file, current_line, current_scope, + editor_prefs.autocompletion_max_entries); found = tags->len > 0; if (found) show_tags_list(editor, tags, rootlen); diff --git a/src/symbols.c b/src/symbols.c index c70d976049..d211b505a1 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -1133,7 +1133,7 @@ gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode) g_return_val_if_fail(DOC_VALID(doc), FALSE); - tags = get_tag_list(doc, tm_tag_max_t); + tags = get_tag_list(doc, ~tm_tag_local_var_t); if (tags == NULL) return FALSE; @@ -1597,13 +1597,23 @@ static TMTag *find_best_goto_tag(GeanyDocument *doc, GPtrArray *tags) static GPtrArray *filter_tags(GPtrArray *tags, TMTag *current_tag, gboolean definition) { + GeanyDocument *doc = document_get_current(); + guint current_line = sci_get_current_line(doc->editor->sci) + 1; const TMTagType forward_types = tm_tag_prototype_t | tm_tag_externvar_t; TMTag *tmtag, *last_tag = NULL; + const gchar *current_scope = NULL; GPtrArray *filtered_tags = g_ptr_array_new(); guint i; + symbols_get_current_function(doc, ¤t_scope); + foreach_ptr_array(tmtag, i, tags) { + /* don't show local variables outside current function or other + * irrelevant tags - same as in the autocomplete case */ + if (!tm_workspace_is_autocomplete_tag(tmtag, doc->tm_file, current_line, current_scope)) + continue; + if ((definition && !(tmtag->type & forward_types)) || (!definition && (tmtag->type & forward_types))) { diff --git a/src/tagmanager/tm_parser.c b/src/tagmanager/tm_parser.c index a305674e2d..6e122e1b21 100644 --- a/src/tagmanager/tm_parser.c +++ b/src/tagmanager/tm_parser.c @@ -70,8 +70,8 @@ static GHashTable *subparser_map = NULL; {'v', tm_tag_variable_t}, /* variable */ \ {'x', tm_tag_externvar_t}, /* externvar */ \ {'h', tm_tag_undef_t}, /* header */ \ - {'l', tm_tag_undef_t}, /* local */ \ - {'z', tm_tag_undef_t}, /* parameter */ \ + {'l', tm_tag_local_var_t}, /* local */ \ + {'z', tm_tag_local_var_t}, /* parameter */ \ {'L', tm_tag_undef_t}, /* label */ \ {'D', tm_tag_undef_t}, /* macroparam */ @@ -89,7 +89,7 @@ static TMParserMapGroup group_C[] = { {_("Structs"), TM_ICON_STRUCT, tm_tag_union_t | tm_tag_struct_t}, {_("Typedefs / Enums"), TM_ICON_STRUCT, tm_tag_typedef_t | tm_tag_enum_t}, {_("Macros"), TM_ICON_MACRO, tm_tag_macro_t | tm_tag_macro_with_arg_t}, - {_("Variables"), TM_ICON_VAR, tm_tag_variable_t | tm_tag_enumerator_t}, + {_("Variables"), TM_ICON_VAR, tm_tag_variable_t | tm_tag_enumerator_t | tm_tag_local_var_t}, {_("Extern Variables"), TM_ICON_VAR, tm_tag_externvar_t}, {_("Other"), TM_ICON_OTHER, tm_tag_other_t}, }; diff --git a/src/tagmanager/tm_parser.h b/src/tagmanager/tm_parser.h index e6ed5fe680..0a44f67c23 100644 --- a/src/tagmanager/tm_parser.h +++ b/src/tagmanager/tm_parser.h @@ -40,7 +40,7 @@ typedef enum tm_tag_externvar_t = 32768, /**< Extern or forward declaration */ tm_tag_macro_t = 65536, /**< Macro (without arguments) */ tm_tag_macro_with_arg_t = 131072, /**< Parameterized macro */ - tm_tag_file_t = 262144, /**< File (Pseudo tag) - obsolete */ + tm_tag_local_var_t = 262144, /**< Local variable (inside function) */ tm_tag_other_t = 524288, /**< Other (non C/C++/Java tag) */ tm_tag_max_t = 1048575 /**< Maximum value of TMTagType */ } TMTagType; diff --git a/src/tagmanager/tm_workspace.c b/src/tagmanager/tm_workspace.c index 0f34477503..3511e8ea5b 100644 --- a/src/tagmanager/tm_workspace.c +++ b/src/tagmanager/tm_workspace.c @@ -580,6 +580,7 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i TMSourceFile *source_file; GList *includes_files; gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp"); + GPtrArray *filtered_tags; if (!temp_file) return FALSE; @@ -624,7 +625,9 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i } tm_tags_sort(source_file->tags_array, global_tags_sort_attrs, TRUE, FALSE); - ret = tm_source_file_write_tags_file(tags_file, source_file->tags_array); + filtered_tags = tm_tags_extract(source_file->tags_array, ~tm_tag_local_var_t); + ret = tm_source_file_write_tags_file(tags_file, filtered_tags); + g_ptr_array_free(filtered_tags, TRUE); tm_source_file_free(source_file); cleanup: @@ -681,33 +684,150 @@ GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type } -static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src, - const char *name, TMParserType lang, guint max_num) +gboolean tm_workspace_is_autocomplete_tag(TMTag *tag, + TMSourceFile *current_file, + guint current_line, + const gchar *current_scope) { - TMTag **tag, *last = NULL; - guint i, count, num; + TMParserType lang = current_file ? current_file->lang : TM_PARSER_NONE; + + /* ignore local variables from other files/functions or after current line */ + gboolean valid = !(tag->type & tm_tag_local_var_t) || + (current_file == tag->file && + current_line >= tag->line && + g_strcmp0(current_scope, tag->scope) == 0); + return valid && !tm_tag_is_anon(tag) && tm_parser_langs_compatible(lang, tag->lang); +} - if (!src || !dst || !name || !*name) + +typedef struct +{ + TMSourceFile *file; + guint line; + const gchar *scope; +} CopyInfo; + +static gboolean is_any_tag(TMTag *tag, CopyInfo *info) +{ + return TRUE; +} + +static gboolean is_local_tag(TMTag *tag, CopyInfo *info) +{ + return tag->type & tm_tag_local_var_t; +} + +static gboolean is_non_local_tag(TMTag *tag, CopyInfo *info) +{ + return !is_local_tag(tag, info); +} + +/* non-local tag not from current file */ +static gboolean is_workspace_tag(TMTag *tag, CopyInfo *info) +{ + return tag->file != info->file && + is_non_local_tag(tag, info); +} + + +static guint copy_tags(GPtrArray *dst, TMTag **src, guint src_len, GHashTable *name_table, + gint num, gboolean (*predicate) (TMTag *, CopyInfo *), CopyInfo *info) +{ + guint i; + + g_return_val_if_fail(src && dst, 0); + + for (i = 0; i < src_len && num > 0; i++) + { + TMTag *tag = *src; + if (predicate(tag, info) && + tm_workspace_is_autocomplete_tag(tag, info->file, info->line, info->scope) && + !g_hash_table_contains(name_table, tag->name)) + { + g_ptr_array_add(dst, tag); + g_hash_table_add(name_table, tag->name); + num--; + } + src++; + } +} + + +static void fill_find_tags_array_prefix(GPtrArray *dst, const char *name, + CopyInfo *info, guint max_num) +{ + TMTag **found; + guint count; + GHashTable *name_table; + + if (!dst || !name || !*name) return; - num = 0; - tag = tm_tags_find(src, name, TRUE, &count); - for (i = 0; i < count && num < max_num; ++i) + name_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); + + if (info->file) { - if (tm_parser_langs_compatible(lang, (*tag)->lang) && - !tm_tag_is_anon(*tag) && - (!last || g_strcmp0(last->name, (*tag)->name) != 0)) + found = tm_tags_find(info->file->tags_array, name, TRUE, &count); + if (found) { - g_ptr_array_add(dst, *tag); - last = *tag; - num++; + copy_tags(dst, found, count, name_table, max_num - dst->len, is_local_tag, info); + if (dst->len < max_num) + copy_tags(dst, found, count, name_table, max_num - dst->len, is_non_local_tag, info); } - tag++; } + if (dst->len < max_num) + { + found = tm_tags_find(theWorkspace->tags_array, name, TRUE, &count); + if (found) + copy_tags(dst, found, count, name_table, max_num - dst->len, is_workspace_tag, info); + } + if (dst->len < max_num) + { + found = tm_tags_find(theWorkspace->global_tags, name, TRUE, &count); + if (found) + copy_tags(dst, found, count, name_table, max_num - dst->len, is_any_tag, info); + } + + g_hash_table_unref(name_table); +} + + +typedef struct +{ + TMSourceFile *file; + gboolean sort_by_name; +} SortInfo; + + +static gint sort_found_tags(gconstpointer a, gconstpointer b, gpointer user_data) +{ + SortInfo *info = user_data; + const TMTag *t1 = *((TMTag **) a); + const TMTag *t2 = *((TMTag **) b); + + /* sort local vars first (with highest line number first), followed + * by tags from current file, followed by workspace tags, followed by + * global tags */ + if (t1->type & tm_tag_local_var_t && t2->type & tm_tag_local_var_t) + return info->sort_by_name ? g_strcmp0(t1->name, t2->name) : t2->line - t1->line; + else if (t1->type & tm_tag_local_var_t) + return -1; + else if (t2->type & tm_tag_local_var_t) + return 1; + else if (t1->file == info->file && t2->file != info->file) + return -1; + else if (t2->file == info->file && t1->file != info->file) + return 1; + else if (t1->file && !t2->file) + return -1; + else if (t2->file && !t1->file) + return 1; + return g_strcmp0(t1->name, t2->name); } -/* Returns tags with the specified prefix sorted by name. If there are several +/* Returns tags with the specified prefix sorted by name, ignoring local + variables from other files/functions or after current line. 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 tm_parsers.h) of the tags to be found, @@ -715,22 +835,105 @@ static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src, @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, TMParserType lang, guint max_num) +GPtrArray *tm_workspace_find_prefix(const char *prefix, + TMSourceFile *current_file, + guint current_line, + const gchar *current_scope, + guint max_num) { TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 }; GPtrArray *tags = g_ptr_array_new(); + SortInfo sort_info; + CopyInfo copy_info; - 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); + copy_info.file = current_file; + copy_info.line = current_line; + copy_info.scope = current_scope; + fill_find_tags_array_prefix(tags, prefix, ©_info, max_num); - tm_tags_sort(tags, attrs, TRUE, FALSE); - if (tags->len > max_num) - tags->len = max_num; + /* sort based on how "close" the tag is to current line with local + * variables first */ + sort_info.file = current_file; + sort_info.sort_by_name = TRUE; + g_ptr_array_sort_with_data(tags, sort_found_tags, &sort_info); return tags; } +static gboolean replace_with_char(gchar *haystack, const gchar *needle, char replacement) +{ + gchar *pos = strstr(haystack, needle); + if (pos) + { + while (*needle) + { + *pos = replacement; + needle++; + pos++; + } + return TRUE; + } + return FALSE; +} + + +static gboolean replace_parens_with_char(gchar *haystack, gchar paren_begin, gchar paren_end, char replacement) +{ + gchar needle[2] = {paren_begin, '\0'}; + gchar *pos = strstr(haystack, needle); + gint nesting = 0; + + if (pos) + { + while (*pos) + { + if (*pos == paren_begin) + nesting++; + else if (*pos == paren_end) + nesting--; + *pos = replacement; + if (nesting == 0) + break; + pos++; + } + return TRUE; + } + return FALSE; +} + + +static gchar *strip_type(const gchar *scoped_name, TMParserType lang, gboolean remove_scope) +{ + if (scoped_name != NULL) + { + const gchar *sep = tm_parser_scope_separator(lang); + gchar *name = g_strdup(scoped_name); + gchar *scope_suffix; + + /* remove pointers, parens and keywords appearing in types */ + g_strdelimit(name, "*^&", ' '); + while (replace_parens_with_char(name, '[', ']', ' ')) {} + while (replace_parens_with_char(name, '<', '>', ' ')) {} + while (replace_with_char(name, "const ", ' ')) {} + while (replace_with_char(name, " const", ' ')) {} + while (replace_with_char(name, " struct", ' ')) {} + /* remove everything before final scope separator */ + if (remove_scope && (scope_suffix = g_strrstr(name, sep))) + { + scope_suffix += strlen(sep); + scope_suffix = g_strdup(scope_suffix); + g_free(name); + name = scope_suffix; + } + g_strstrip(name); + + return name; + } + 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 @@ -739,13 +942,18 @@ GPtrArray *tm_workspace_find_prefix(const char *prefix, TMParserType lang, guint * 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) +find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespace, guint depth) { TMTagType member_types = tm_tag_max_t & ~(TM_TYPE_WITH_MEMBERS | tm_tag_typedef_t); - GPtrArray *tags = g_ptr_array_new(); + GPtrArray *tags; gchar *scope; guint i; + if (depth == 10) + return NULL; /* simple inheritence cycle avoidance */ + + tags = g_ptr_array_new(); + if (namespace) member_types = tm_tag_max_t; @@ -768,6 +976,47 @@ find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespa } } + /* add members from parent classes */ + if (!namespace && (type_tag->type & (tm_tag_class_t | tm_tag_struct_t)) && + type_tag->inheritance && *type_tag->inheritance) + { + gchar *stripped = strip_type(type_tag->inheritance, type_tag->lang, FALSE); + gchar **split_strv = g_strsplit(stripped, ",", -1); /* parent classes */ + const gchar *parent; + + g_free(stripped); + + for (i = 0; parent = split_strv[i]; i++) + { + GPtrArray *parent_tags; + + stripped = strip_type(parent, type_tag->lang, TRUE); + parent_tags = tm_workspace_find(stripped, NULL, tm_tag_class_t | tm_tag_struct_t, + NULL, type_tag->lang); + + if (parent_tags->len > 0) + { + TMTag *parent_tag = parent_tags->pdata[0]; + GPtrArray *parent_members = find_scope_members_tags( + parent_tag->file ? parent_tag->file->tags_array : all, parent_tag, + FALSE, depth + 1); + + if (parent_members) + { + guint j; + for (j = 0; j < parent_members->len; j++) + g_ptr_array_add (tags, parent_members->pdata[j]); + g_ptr_array_free(parent_members, TRUE); + } + } + + g_ptr_array_free(parent_tags, TRUE); + g_free(stripped); + } + + g_strfreev(split_strv); + } + g_free(scope); if (tags->len == 0) @@ -776,26 +1025,13 @@ find_scope_members_tags (const GPtrArray *all, TMTag *type_tag, gboolean namespa return NULL; } - return tags; -} - - -static gchar *strip_type(const gchar *scoped_name, TMParserType lang) -{ - if (scoped_name != NULL) + if (depth == 0) { - /* remove scope prefix */ - const gchar *sep = tm_parser_scope_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; + TMTagAttrType sort_attrs[] = {tm_tag_attr_name_t, 0}; + tm_tags_sort(tags, sort_attrs, TRUE, FALSE); } - return NULL; + + return tags; } @@ -857,7 +1093,7 @@ find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile if (tag->var_type && tag->var_type[0] != '\0') { g_free(type_name); - type_name = strip_type(tag->var_type, tag->lang); + type_name = strip_type(tag->var_type, tag->lang, TRUE); file = tag->file; continue; } @@ -866,7 +1102,7 @@ find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile else /* real type with members */ { /* 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); + res = find_scope_members_tags(tag->file ? tag->file->tags_array : tags_array, tag, namespace, 0); break; } } @@ -877,8 +1113,8 @@ find_scope_members (const GPtrArray *tags_array, const gchar *name, TMSourceFile } -/* 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, +/* Checks whether a member tag is directly accessible from method */ +static gboolean member_accessible(const GPtrArray *tags, const gchar *method_scope, TMTag *member_tag, TMParserType lang) { const gchar *sep = tm_parser_scope_separator(lang); @@ -891,35 +1127,31 @@ static gboolean member_at_method_scope(const GPtrArray *tags, const gchar *metho len = g_strv_length(comps); if (len > 1) { - 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) + gchar *cls = comps[len - 2]; + + if (*cls) { - const GPtrArray *src = member_tag->file ? member_tag->file->tags_array : tags; - GPtrArray *cls_tags = g_ptr_array_new(); + /* find method's class members */ + GPtrArray *cls_tags = find_scope_members(tags, cls, NULL, lang, FALSE); - /* 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); - } + if (cls_tags) + { + guint i; - g_free(cls_scope); - g_free(member_scope); + /* check if one of the class members is member_tag */ + for (i = 0; i < cls_tags->len; i++) + { + TMTag *t = cls_tags->pdata[i]; + + if (t == member_tag) + { + ret = TRUE; + break; + } + } + g_ptr_array_free(cls_tags, TRUE); + } + } } g_strfreev(comps); @@ -949,7 +1181,7 @@ find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, 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); + tag, TRUE, 0); } else if (tag->var_type) /* variable: scope search */ { @@ -959,9 +1191,9 @@ find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, T * 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)) + member_accessible(searched_array, current_scope, tag, lang)) { - gchar *tag_type = strip_type(tag->var_type, tag->lang); + gchar *tag_type = strip_type(tag->var_type, tag->lang, TRUE); member_tags = find_scope_members(searched_array, tag_type, tag->file, lang, FALSE); g_free(tag_type); @@ -973,7 +1205,7 @@ find_scope_members_all(const GPtrArray *tags, const GPtrArray *searched_array, T } -static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array, TMParserType lang) +static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrArray *searched_array) { GPtrArray *member_tags = NULL; guint i; @@ -982,7 +1214,7 @@ static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrAr { TMTag *tag = TM_TAG(tags->pdata[i]); - member_tags = find_scope_members_tags(searched_array, tag, TRUE); + member_tags = find_scope_members_tags(searched_array, tag, TRUE, 0); } return member_tags; @@ -996,11 +1228,13 @@ static GPtrArray *find_namespace_members_all(const GPtrArray *tags, const GPtrAr @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 + @param current_line The current line in the editor @param search_namespace Whether to search the contents of namespace (e.g. after MyNamespace::) @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, gboolean search_namespace) + gboolean function, gboolean member, const gchar *current_scope, guint current_line, + gboolean search_namespace) { TMParserType lang = source_file ? source_file->lang : TM_PARSER_NONE; GPtrArray *tags, *member_tags = NULL; @@ -1014,21 +1248,37 @@ tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name, { tags = tm_workspace_find(name, NULL, tm_tag_namespace_t, NULL, lang); - member_tags = find_namespace_members_all(tags, theWorkspace->tags_array, lang); + member_tags = find_namespace_members_all(tags, theWorkspace->tags_array); if (!member_tags) - member_tags = find_namespace_members_all(tags, theWorkspace->global_tags, lang); + member_tags = find_namespace_members_all(tags, theWorkspace->global_tags); g_ptr_array_free(tags, TRUE); } if (!member_tags) { + SortInfo info; + guint i; + if (function) tag_type = function_types; /* tags corresponding to the variable/type name */ tags = tm_workspace_find(name, NULL, tag_type, NULL, lang); + /* remove invalid local tags and sort tags so "nearest" tags are first */ + for (i = 0; i < tags->len; i++) + { + TMTag *tag = tags->pdata[i]; + if (!tm_workspace_is_autocomplete_tag(tag, source_file, current_line, current_scope)) + tags->pdata[i] = NULL; + } + tm_tags_prune(tags); + + info.file = source_file; + info.sort_by_name = FALSE; + g_ptr_array_sort_with_data(tags, sort_found_tags, &info); + /* 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. */ @@ -1069,59 +1319,3 @@ void tm_workspace_dump(void) } } #endif /* TM_DEBUG */ - - -#if 0 - -/* Returns a list of parent classes for the given class name - @param name Name of the class - @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */ -static const GPtrArray *tm_workspace_get_parents(const gchar *name) -{ - static TMTagAttrType type[] = { tm_tag_attr_name_t, tm_tag_attr_none_t }; - static GPtrArray *parents = NULL; - const GPtrArray *matches; - guint i = 0; - guint j; - gchar **klasses; - gchar **klass; - TMTag *tag; - - g_return_val_if_fail(name && isalpha(*name),NULL); - - if (NULL == parents) - parents = g_ptr_array_new(); - else - g_ptr_array_set_size(parents, 0); - 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]); - while (i < parents->len) - { - tag = TM_TAG(parents->pdata[i]); - if ((NULL != tag->inheritance) && (isalpha(tag->inheritance[0]))) - { - klasses = g_strsplit(tag->inheritance, ",", 10); - for (klass = klasses; (NULL != *klass); ++ klass) - { - for (j=0; j < parents->len; ++j) - { - if (0 == strcmp(*klass, TM_TAG(parents->pdata[j])->name)) - break; - } - if (parents->len == j) - { - 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]); - } - } - g_strfreev(klasses); - } - ++ i; - } - return parents; -} - -#endif diff --git a/src/tagmanager/tm_workspace.h b/src/tagmanager/tm_workspace.h index df586911eb..98e857558f 100644 --- a/src/tagmanager/tm_workspace.h +++ b/src/tagmanager/tm_workspace.h @@ -56,10 +56,12 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type, TMTagAttrType *attrs, TMParserType lang); -GPtrArray *tm_workspace_find_prefix(const char *prefix, TMParserType lang, guint max_num); +GPtrArray *tm_workspace_find_prefix(const char *prefix, + TMSourceFile *current_file, guint current_line, const gchar *current_scope, + guint max_num); GPtrArray *tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name, - gboolean function, gboolean member, const gchar *current_scope, gboolean search_namespace); + gboolean function, gboolean member, const gchar *current_scope, guint current_line, gboolean search_namespace); void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file); @@ -69,6 +71,8 @@ void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* t void tm_workspace_free(void); +gboolean tm_workspace_is_autocomplete_tag(TMTag *tag, TMSourceFile *current_file, + guint current_line, const gchar *current_scope); #ifdef TM_DEBUG void tm_workspace_dump(void);