Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Thomas Martitz <thomas.martitz(a)mailbox.org>
Date: Sat, 27 Aug 2022 22:44:50 UTC
Commit: 2a5da2225f7aa87bdc7f82c3966f6fb32754ae5c
https://github.com/geany/geany/commit/2a5da2225f7aa87bdc7f82c3966f6fb32754a…
Log Message:
-----------
Rewrite member_at_method_scope() to handle more situations
When performing scope completion for
void A::foo() {
bar. // <--
}
we need to determine what 'bar' is. It could be a global variable, in which
case we should look for variable tags, it could however also be a member
of A in which case we should look for member tags.
To determine this, the previous code checked whether there's a tag
named 'bar' with the same scope as the method tag. One drawback of this
approach is that it doesn't take namespace manipulation functions
like 'using namespace' into account so for header
namespace X {
class A {
Baz bar;
};
};
and source
using namespace X;
void A::foo() {
bar. // <--
}
it wouldn't find 'bar' because ctags reports foo() to have scope A
and bar to have scope X::A.
Another drawback of this approach is that it doesn't take inheritance
into account so it wouldn't find 'bar' when defined in a super-class,
such as
class B {
Baz bar;
};
class A : B {
void foo();
}
To avoid these problems, this patch rewrites member_at_method_scope()
(and renames it to member_accessible()) so it gets the class name from
the scope of the method in which we are (A in the above example) and
returns all members of A (including the super-classes members).
Afterwards, it checks if one of the members is really the member tag
we are interested in ('bar' in the above example).
Modified Paths:
--------------
src/tagmanager/tm_workspace.c
Modified: src/tagmanager/tm_workspace.c
54 lines changed, 25 insertions(+), 29 deletions(-)
===================================================================
@@ -1047,8 +1047,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);
@@ -1061,35 +1061,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;
+
+ /* check if one of the class members is member_tag */
+ for (i = 0; i < cls_tags->len; i++)
+ {
+ TMTag *t = cls_tags->pdata[i];
- g_free(cls_scope);
- g_free(member_scope);
+ if (t == member_tag)
+ {
+ ret = TRUE;
+ break;
+ }
+ }
+ g_ptr_array_free(cls_tags, TRUE);
+ }
+ }
}
g_strfreev(comps);
@@ -1129,7 +1125,7 @@ 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, TRUE);
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Thomas Martitz <thomas.martitz(a)mailbox.org>
Date: Sat, 27 Aug 2022 22:44:50 UTC
Commit: a91a9c6ca17e5a7a163b095d5a9065afa7e8af77
https://github.com/geany/geany/commit/a91a9c6ca17e5a7a163b095d5a9065afa7e8a…
Log Message:
-----------
Update documentation related to scope autocompletion
Modified Paths:
--------------
doc/geany.txt
Modified: doc/geany.txt
12 lines changed, 9 insertions(+), 3 deletions(-)
===================================================================
@@ -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
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Thomas Martitz <thomas.martitz(a)mailbox.org>
Date: Sat, 27 Aug 2022 22:44:12 UTC
Commit: 25f150931e376d57654fb1df59b892c48af80e5a
https://github.com/geany/geany/commit/25f150931e376d57654fb1df59b892c48af80…
Log Message:
-----------
Update scope completion to take into account local variables
The code removes invalid local variables from other functions and behind
current position. In addition, it sorts the found tags corresponding
to the name in the editor for which we perform scope completion so
local variables are searched first for their members, followed by tags
from the current file, followed by workspace tags and finally using
global tags.
Modified Paths:
--------------
src/editor.c
src/tagmanager/tm_workspace.c
src/tagmanager/tm_workspace.h
Modified: src/editor.c
3 lines changed, 2 insertions(+), 1 deletions(-)
===================================================================
@@ -698,6 +698,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 +765,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();
Modified: src/tagmanager/tm_workspace.c
65 lines changed, 58 insertions(+), 7 deletions(-)
===================================================================
@@ -684,16 +684,19 @@ GPtrArray *tm_workspace_find(const char *name, const char *scope, TMTagType type
}
-static gboolean is_valid_autocomplete_tag(TMTag *tag,
+gboolean tm_workspace_is_autocomplete_tag(TMTag *tag,
TMSourceFile *current_file,
guint current_line,
const gchar *current_scope)
{
+ TMParserType lang = current_file ? current_file->lang : TM_PARSER_NONE;
+
/* ignore local variables from other files/functions or after current line */
- return !(tag->type & tm_tag_local_var_t) ||
+ 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);
}
@@ -714,11 +717,9 @@ static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src,
tag = tm_tags_find(src, name, TRUE, &count);
for (i = 0; i < count && num < max_num; ++i)
{
- if (is_valid_autocomplete_tag(*tag, current_file, current_line, current_scope))
+ if (tm_workspace_is_autocomplete_tag(*tag, current_file, current_line, current_scope))
{
- if (tm_parser_langs_compatible(lang, (*tag)->lang) &&
- !tm_tag_is_anon(*tag) &&
- (!last || g_strcmp0(last->name, (*tag)->name) != 0))
+ if (!last || g_strcmp0(last->name, (*tag)->name) != 0)
{
g_ptr_array_add(dst, *tag);
last = *tag;
@@ -730,6 +731,39 @@ static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src,
}
+typedef struct
+{
+ TMSourceFile *file;
+} 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 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 0;
+}
+
+
/* 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.
@@ -1026,11 +1060,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;
@@ -1053,12 +1089,27 @@ tm_workspace_find_scope_members (TMSourceFile *source_file, const char *name,
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;
+ 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. */
Modified: src/tagmanager/tm_workspace.h
4 lines changed, 3 insertions(+), 1 deletions(-)
===================================================================
@@ -61,7 +61,7 @@ GPtrArray *tm_workspace_find_prefix(const char *prefix,
TMParserType lang, 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);
@@ -71,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);
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Thomas Martitz <thomas.martitz(a)mailbox.org>
Date: Sat, 27 Aug 2022 22:44:12 UTC
Commit: 13bdb37cf7496a27b0b77c9893839c0fc3aba186
https://github.com/geany/geany/commit/13bdb37cf7496a27b0b77c9893839c0fc3aba…
Log Message:
-----------
Update goto symbol definitions to take into account local variables
We only want to use local variables within the current function for
the goto. This means we want to filter out local variable tags that:
1. Are from a different file
2. Are declared later in the file than where the current cursor is
3. Have different scope than the current function scope
Fundamentally the same requirements as for (non-scope) autocompletion.
Modified Paths:
--------------
src/symbols.c
Modified: src/symbols.c
10 lines changed, 10 insertions(+), 0 deletions(-)
===================================================================
@@ -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)))
{
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).