From 91ccf4842a1f86559d419db5582683be2e1f1938 Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Mon, 22 Aug 2016 16:56:50 +0200 Subject: [PATCH 1/4] Add support for scoped calltips Try and select the correct calltip based on scope information on the left of the symbol triggering the calltip, similar to scope completion. Closes #1177. --- src/editor.c | 74 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/src/editor.c b/src/editor.c index 3808d3fa3b..05b123ea95 100644 --- a/src/editor.c +++ b/src/editor.c @@ -700,11 +700,10 @@ static void request_reshowing_calltip(SCNotification *nt) } -static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize rootlen) +static GPtrArray *get_scoped_tags(GeanyEditor *editor, gint pos) { ScintillaObject *sci = editor->sci; - gint pos = sci_get_current_position(editor->sci); - gchar typed = sci_get_char_at(sci, pos - 1); + gchar typed = pos > 0 ? sci_get_char_at(sci, pos - 1) : 0; gchar brace_char; gchar *name; GeanyFiletype *ft = editor->document->file_type; @@ -712,23 +711,9 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize gboolean function = FALSE; gboolean member; gboolean scope_sep_typed = FALSE; - gboolean ret = FALSE; const gchar *current_scope; const gchar *context_sep = tm_tag_context_separator(ft->lang); - if (autocomplete_scope_shown) - { - /* 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--; - - if (pos > 0) - typed = sci_get_char_at(sci, pos - 1); - } - /* make sure to keep in sync with similar checks below */ if (match_last_chars(sci, pos, context_sep)) { @@ -743,7 +728,7 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize else if (ft->id == GEANY_FILETYPES_CPP && match_last_chars(sci, pos, "->*")) pos -= 3; else - return FALSE; + return NULL; /* allow for a space between word and operator */ while (pos > 0 && isspace(sci_get_char_at(sci, pos - 1))) @@ -768,7 +753,7 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize name = editor_get_word_at_pos(editor, pos, NULL); if (!name) - return FALSE; + return NULL; /* check if invoked on member */ pos -= strlen(name); @@ -782,6 +767,30 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize current_scope = ""; tags = tm_workspace_find_scope_members(editor->document->tm_file, name, function, member, current_scope, scope_sep_typed); + + g_free(name); + return tags; +} + + +static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize rootlen) +{ + ScintillaObject *sci = editor->sci; + gint pos = sci_get_current_position(editor->sci); + GPtrArray *tags; + gboolean ret = FALSE; + + if (autocomplete_scope_shown) + { + /* 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--; + } + + tags = get_scoped_tags(editor, pos); if (tags) { GPtrArray *filtered = g_ptr_array_new(); @@ -804,7 +813,6 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize g_ptr_array_free(filtered, TRUE); } - g_free(name); return ret; } @@ -1895,8 +1903,9 @@ static gboolean append_calltip(GString *str, const TMTag *tag, GeanyFiletypeID f } -static gchar *find_calltip(const gchar *word, GeanyFiletype *ft) +static gchar *find_calltip(gint pos, const gchar *word, GeanyEditor *editor) { + GeanyFiletype *ft = editor->document->file_type; GPtrArray *tags; const TMTagType arg_types = tm_tag_function_t | tm_tag_prototype_t | tm_tag_method_t | tm_tag_macro_with_arg_t; @@ -1906,8 +1915,25 @@ 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, NULL, tm_tag_max_t, NULL, ft->lang); + tags = get_scoped_tags(editor, pos - strlen(word)); + if (tags) + { + GPtrArray *filtered = g_ptr_array_new(); + + foreach_ptr_array(tag, i, tags) + { + if (strcmp(tag->name, word) == 0) + g_ptr_array_add(filtered, tag); + } + + g_ptr_array_free(tags, TRUE); + tags = filtered; + } + else + { + /* 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, NULL, ft->lang); + } if (tags->len == 0) { g_ptr_array_free(tags, TRUE); @@ -2046,7 +2072,7 @@ gboolean editor_show_calltip(GeanyEditor *editor, gint pos) if (word[0] == '\0') return FALSE; - str = find_calltip(word, editor->document->file_type); + str = find_calltip(pos, word, editor); if (str) { g_free(calltip.text); /* free the old calltip */ From eeac1ca61c2df8aa8418187b6b14ef86816baee3 Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Tue, 23 Aug 2016 00:20:10 +0200 Subject: [PATCH 2/4] FXIME: Attempt at using the current (implicit) scope for symbol scope search Try to resolve symbols for the current scope (if available) when performing scope completion or searching for calltips. This is based on the assumption that for a symbol to be available in a scope, it has to either be in a current or parent scope, or be part of an explicit member. This implementation is naive and done quickly, it's likely to have some incorrect stuff in it. Also, integration in scope completion is likely wrong. --- src/editor.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/src/editor.c b/src/editor.c index 05b123ea95..ac39d065d2 100644 --- a/src/editor.c +++ b/src/editor.c @@ -773,6 +773,79 @@ static GPtrArray *get_scoped_tags(GeanyEditor *editor, gint pos) } +static GPtrArray *get_tags_for_current_scope(GeanyEditor *editor, const gchar *name, gboolean prefix) +{ + GPtrArray *found_tags = NULL; + const gchar *current_scope; + + if (symbols_get_current_scope(editor->document, ¤t_scope) != -1) + { + const GeanyFiletype *const ft = editor->document->file_type; + const gchar *const context_sep = tm_tag_context_separator(ft->lang); + gchar *const scope = g_strdup(current_scope); + GPtrArray *tags; + TMTag *tag; + guint i; + + found_tags = g_ptr_array_new(); + while (TRUE) + { + gchar *const sep_pos = g_strrstr(scope, context_sep); + const gchar *const pass_name = sep_pos ? (sep_pos + strlen(context_sep)) : scope; + const gchar *const pass_scope = sep_pos ? scope : ""; + + if (sep_pos) + *sep_pos = 0; + + tags = tm_workspace_find_scope_members(editor->document->tm_file, pass_name, + FALSE, FALSE, pass_scope, TRUE); + if (tags) + { + foreach_ptr_array(tag, i, tags) + { + if ((prefix && g_str_has_prefix(tag->name, name)) || + (! prefix && strcmp(tag->name, name) == 0)) + { + g_ptr_array_add(found_tags, tag); + } + } + + g_ptr_array_free(tags, TRUE); + } + + if (! sep_pos) + break; + } + + g_free(scope); + + /* root tags */ + if (prefix) + tags = tm_workspace_find_prefix(name, ft->lang, 0xffff); + else + tags = tm_workspace_find(name, NULL, tm_tag_max_t, NULL, ft->lang); + if (tags->len > 0) + { + foreach_ptr_array(tag, i, tags) + { + /* we want only top-level here */ + if (EMPTY(tag->scope)) + g_ptr_array_add(found_tags, tag); + } + } + g_ptr_array_free(tags, TRUE); + + if (found_tags->len == 0) + { + g_ptr_array_free(found_tags, TRUE); + found_tags = NULL; + } + } + + return found_tags; +} + + static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize rootlen) { ScintillaObject *sci = editor->sci; @@ -791,6 +864,11 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize } tags = get_scoped_tags(editor, pos); + if (! tags && rootlen >= (gsize) editor_prefs.symbolcompletion_min_chars) + { + /* FIXME: well, this basically replaces autocomplete_tags(), probably not wanted? */ + tags = get_tags_for_current_scope(editor, root, TRUE); + } if (tags) { GPtrArray *filtered = g_ptr_array_new(); @@ -1931,10 +2009,11 @@ static gchar *find_calltip(gint pos, const gchar *word, GeanyEditor *editor) } else { - /* 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, NULL, ft->lang); + tags = get_tags_for_current_scope(editor, word, FALSE); } - if (tags->len == 0) + if (! tags) + return NULL; + else if (tags->len == 0) { g_ptr_array_free(tags, TRUE); return NULL; From cd5c383530fe93ae946915887e2567fe51031d01 Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Tue, 23 Aug 2016 14:32:05 +0200 Subject: [PATCH 3/4] Don't dedup tags used for calltips in order to support overloading --- src/editor.c | 3 +++ src/tagmanager/tm_workspace.c | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/editor.c b/src/editor.c index ac39d065d2..6bec26ca94 100644 --- a/src/editor.c +++ b/src/editor.c @@ -871,6 +871,7 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize } if (tags) { + TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0}; GPtrArray *filtered = g_ptr_array_new(); TMTag *tag; guint i; @@ -881,6 +882,8 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize g_ptr_array_add(filtered, tag); } + tm_tags_dedup(filtered, sort_attr, FALSE); + if (filtered->len > 0) { show_tags_list(editor, filtered, rootlen); diff --git a/src/tagmanager/tm_workspace.c b/src/tagmanager/tm_workspace.c index ea65a72174..ad01417980 100644 --- a/src/tagmanager/tm_workspace.c +++ b/src/tagmanager/tm_workspace.c @@ -1036,7 +1036,6 @@ tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name, 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 (search_namespace) { @@ -1073,9 +1072,6 @@ tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name, g_ptr_array_free(tags, TRUE); } - if (member_tags) - tm_tags_dedup(member_tags, sort_attr, FALSE); - return member_tags; } From 342e636b9d46e30b05bdfe68ec4c3b5ce9695db9 Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Tue, 23 Aug 2016 14:32:49 +0200 Subject: [PATCH 4/4] Make sure to sort get_tags_for_current_scope() result This might not be ideal as the display order could be "closest candidate" instead of "by name", but it's required for deduplication. --- src/editor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/editor.c b/src/editor.c index 6bec26ca94..56abf49c98 100644 --- a/src/editor.c +++ b/src/editor.c @@ -840,6 +840,12 @@ static GPtrArray *get_tags_for_current_scope(GeanyEditor *editor, const gchar *n g_ptr_array_free(found_tags, TRUE); found_tags = NULL; } + else if (prefix) + { + TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0}; + + tm_tags_sort(found_tags, sort_attr, FALSE, FALSE); + } } return found_tags;