New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Scoped calltips #1188
base: master
Are you sure you want to change the base?
WIP: Scoped calltips #1188
Changes from all commits
91ccf48
eeac1ca
cd5c383
342e636
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -700,35 +700,20 @@ 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; | ||
GPtrArray *tags; | ||
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,8 +767,117 @@ 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 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; | ||
} | ||
else if (prefix) | ||
{ | ||
TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0}; | ||
|
||
tm_tags_sort(found_tags, sort_attr, FALSE, FALSE); | ||
} | ||
} | ||
|
||
return found_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 && 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) | ||
{ | ||
TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, 0}; | ||
GPtrArray *filtered = g_ptr_array_new(); | ||
TMTag *tag; | ||
guint i; | ||
|
@@ -794,6 +888,8 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize | |
g_ptr_array_add(filtered, tag); | ||
} | ||
|
||
tm_tags_dedup(filtered, sort_attr, FALSE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, didnt see that, nevermind above |
||
|
||
if (filtered->len > 0) | ||
{ | ||
show_tags_list(editor, filtered, rootlen); | ||
|
@@ -804,7 +900,6 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize | |
g_ptr_array_free(filtered, TRUE); | ||
} | ||
|
||
g_free(name); | ||
return ret; | ||
} | ||
|
||
|
@@ -1895,8 +1990,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,9 +2002,27 @@ 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); | ||
if (tags->len == 0) | ||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this filter do? get_scoped_tags() already limits the found tags to the word at pos, doesn't it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No it does not, it searches members of a scope, doesn't filter by names (it supports getting members without specifying a name). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still confused. get_scope_tags() finds the function name (since pos is setup up to point to it). This is then passed to tm_workspace_find_scope_members() (and then tm_workspace_find()). Therefore the returned tags are already filtered by the function name (assuming scope_sep_typed being FALSE). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hum… no, Though, I can't really fully defend this, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's alright - you just want to skip the function name here, the scope separators get skipped in get_scoped_tags(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kugel- This is a good example why I suggested the new query interface operates on tag arrays instead - especially with scope completion you have to work with tag arrays which are results of a much more complicated query than what your query interface can handle and further filtering isn't possible using your API. |
||
} | ||
|
||
g_ptr_array_free(tags, TRUE); | ||
tags = filtered; | ||
} | ||
else | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be sure the number of filtered tags from above could be checked here and if there are 0, fall back to searching all tags. |
||
{ | ||
tags = get_tags_for_current_scope(editor, word, FALSE); | ||
} | ||
if (! tags) | ||
return NULL; | ||
else if (tags->len == 0) | ||
{ | ||
g_ptr_array_free(tags, TRUE); | ||
return NULL; | ||
|
@@ -2046,7 +2160,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 */ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tm_workspace_find() doest dedup which is probably not what you want for autocompletion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I later altered everything to manually dedup (in the autoc side) so I can chose depending on calltip/autoc