Skip to content
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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
164 changes: 139 additions & 25 deletions src/editor.c
Expand Up @@ -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))
{
Expand All @@ -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)))
Expand All @@ -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);
Expand All @@ -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, &current_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);
Copy link
Member

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

Copy link
Member Author

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

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;
Expand All @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The 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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Member Author

Choose a reason for hiding this comment

The 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).
And this part is basically just copied from L880 onwards

Copy link
Member

Choose a reason for hiding this comment

The 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).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hum… no, get_scope_tags() should find this in this.member(). And anyway, tm_workspace_find_scope_members() finds all children on the given scope, not filtered by any name.

Though, I can't really fully defend this, pos - strlen(word) might be a bit of luck, and might only work for 1-byte scope separators. But it odes seem to work somehow :]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though, I can't really fully defend this, pos - strlen(word) might be a bit of luck, and might only work for 1-byte scope separators. But it odes seem to work somehow :]

It's alright - you just want to skip the function name here, the scope separators get skipped in get_scoped_tags().

Copy link
Member

Choose a reason for hiding this comment

The 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
Copy link
Member

Choose a reason for hiding this comment

The 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;
Expand Down Expand Up @@ -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 */
Expand Down
4 changes: 0 additions & 4 deletions src/tagmanager/tm_workspace.c
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
}

Expand Down