Revision: 3850 http://geany.svn.sourceforge.net/geany/?rev=3850&view=rev Author: ntrel Date: 2009-06-10 12:36:13 +0000 (Wed, 10 Jun 2009)
Log Message: ----------- Autocomplete scoped fields like struct members when typing '.' (and also '->' or '::' in C/C++). Save all tag types for C/C++ when generating a global tags file, so we can use autocompletion for structs also. Merge tm_workspace_find_scope_members(), tm_workspace_find_namespace_members() (currently not built) from Anjuta 2.24.1 tagmanager.
Modified Paths: -------------- trunk/ChangeLog trunk/TODO trunk/src/editor.c trunk/tagmanager/include/tm_workspace.h trunk/tagmanager/tm_workspace.c
Modified: trunk/ChangeLog =================================================================== --- trunk/ChangeLog 2009-06-09 21:02:46 UTC (rev 3849) +++ trunk/ChangeLog 2009-06-10 12:36:13 UTC (rev 3850) @@ -1,3 +1,16 @@ +2009-06-10 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com> + + * src/editor.c, tagmanager/include/tm_workspace.h, + tagmanager/tm_workspace.c, TODO: + Autocomplete scoped fields like struct members when typing '.' (and + also '->' or '::' in C/C++). + Save all tag types for C/C++ when generating a global tags file, so + we can use autocompletion for structs also. + Merge tm_workspace_find_scope_members(), + tm_workspace_find_namespace_members() (currently not built) from + Anjuta 2.24.1 tagmanager. + + 2009-06-09 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
* tagmanager/pascal.c:
Modified: trunk/TODO =================================================================== --- trunk/TODO 2009-06-09 21:02:46 UTC (rev 3849) +++ trunk/TODO 2009-06-10 12:36:13 UTC (rev 3850) @@ -41,5 +41,4 @@ o (better tags support for popular languages? - this is a moving target...) o Some kind of support for CTags tags files - o Scope resolution for object members o Python plugin interface (different concept from Lua scripting)
Modified: trunk/src/editor.c =================================================================== --- trunk/src/editor.c 2009-06-09 21:02:46 UTC (rev 3849) +++ trunk/src/editor.c 2009-06-10 12:36:13 UTC (rev 3850) @@ -431,6 +431,103 @@ }
+static void show_autocomplete(ScintillaObject *sci, gint rootlen, const gchar *words) +{ + /* store whether a calltip is showing, so we can reshow it after autocompletion */ + calltip.set = SSM(sci, SCI_CALLTIPACTIVE, 0, 0); + SSM(sci, SCI_AUTOCSHOW, rootlen, (sptr_t) words); +} + + +static void show_tags_list(GeanyEditor *editor, const GPtrArray *tags, gsize rootlen) +{ + ScintillaObject *sci = editor->sci; + + g_return_if_fail(tags); + + if (tags->len > 0) + { + GString *words = g_string_sized_new(150); + guint j; + + for (j = 0; j < tags->len; ++j) + { + if (j > 0) + g_string_append_c(words, '\n'); + + if (j == editor_prefs.autocompletion_max_entries) + { + g_string_append(words, "..."); + break; + } + g_string_append(words, ((TMTag *) tags->pdata[j])->name); + } + show_autocomplete(sci, rootlen, words->str); + g_string_free(words, TRUE); + } +} + + +/* do not use with long strings */ +static gboolean match_last_chars(ScintillaObject *sci, gint pos, const gchar *str) +{ + gsize len = strlen(str); + gchar *buf; + + g_return_val_if_fail(len < 100, FALSE); + + buf = g_alloca(len + 1); + sci_get_text_range(sci, pos - len, pos, buf); + return strcmp(str, buf) == 0; +} + + +static void autocomplete_scope(GeanyEditor *editor) +{ + ScintillaObject *sci = editor->sci; + gint pos = sci_get_current_position(editor->sci); + gchar typed = sci_get_char_at(sci, pos - 1); + gchar *name; + const GPtrArray *tags = NULL; + const TMTag *tag; + gint ft_id = FILETYPE_ID(editor->document->file_type); + + if (ft_id == GEANY_FILETYPES_C || ft_id == GEANY_FILETYPES_CPP) + { + if (match_last_chars(sci, pos, "->") || match_last_chars(sci, pos, "::")) + pos--; + else if (typed != '.') + return; + } + else if (typed != '.') + return; + + /* allow for a space between word and operator */ + if (isspace(sci_get_char_at(sci, pos - 2))) + pos--; + name = editor_get_word_at_pos(editor, pos - 1, NULL); + if (!name) + return; + + tags = tm_workspace_find(name, tm_tag_max_t, NULL, FALSE, -1); + g_free(name); + if (!tags || tags->len == 0) + return; + + tag = g_ptr_array_index(tags, 0); + name = tag->atts.entry.var_type; + if (name) + { + TMWorkObject *obj = editor->document->tm_file; + + tags = tm_workspace_find_scope_members(obj ? obj->tags_array : NULL, + name, TRUE, FALSE); + if (tags) + show_tags_list(editor, tags, 0); + } +} + + static void on_char_added(GeanyEditor *editor, SCNotification *nt) { ScintillaObject *sci = editor->sci; @@ -450,6 +547,8 @@ break; } case '>': + editor_start_auto_complete(editor, pos, FALSE); /* C/C++ ptr-> scope completion */ + /* fall through */ case '/': { /* close xml-tags */ handle_xml(editor, pos, nt->ch); @@ -489,6 +588,10 @@ close_block(editor, pos - 1); break; } + /* scope autocompletion */ + case '.': + case ':': /* C/C++ class:: syntax */ + /* tag autocompletion */ default: editor_start_auto_complete(editor, pos, FALSE); } @@ -1520,14 +1623,6 @@ }
-static void show_autocomplete(ScintillaObject *sci, gint rootlen, const gchar *words) -{ - /* store whether a calltip is showing, so we can reshow it after autocompletion */ - calltip.set = SSM(sci, SCI_CALLTIPACTIVE, 0, 0); - SSM(sci, SCI_AUTOCSHOW, rootlen, (sptr_t) words); -} - - static gboolean autocomplete_html(ScintillaObject *sci, const gchar *root, gsize rootlen) { /* HTML entities auto completion */ @@ -1566,35 +1661,15 @@ { TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 }; const GPtrArray *tags; - ScintillaObject *sci; GeanyDocument *doc;
g_return_val_if_fail(editor != NULL && editor->document->file_type != NULL, FALSE);
- sci = editor->sci; doc = editor->document;
tags = tm_workspace_find(root, tm_tag_max_t, attrs, TRUE, doc->file_type->lang); - if (NULL != tags && tags->len > 0) - { - GString *words = g_string_sized_new(150); - guint j; - - for (j = 0; j < tags->len; ++j) - { - if (j > 0) - g_string_append_c(words, '\n'); - - if (j == editor_prefs.autocompletion_max_entries) - { - g_string_append(words, "..."); - break; - } - g_string_append(words, ((TMTag *) tags->pdata[j])->name); - } - show_autocomplete(sci, rootlen, words->str); - g_string_free(words, TRUE); - } + if (tags) + show_tags_list(editor, tags, rootlen); return TRUE; }
@@ -1658,6 +1733,8 @@ if (!force && !is_code_style(lexer, style)) return FALSE;
+ autocomplete_scope(editor); + linebuf = sci_get_line(sci, line);
if (ft->id == GEANY_FILETYPES_LATEX)
Modified: trunk/tagmanager/include/tm_workspace.h =================================================================== --- trunk/tagmanager/include/tm_workspace.h 2009-06-09 21:02:46 UTC (rev 3849) +++ trunk/tagmanager/include/tm_workspace.h 2009-06-10 12:36:13 UTC (rev 3850) @@ -139,6 +139,19 @@ tm_workspace_find_scoped (const char *name, const char *scope, gint type, TMTagAttrType *attrs, gboolean partial, langType lang, gboolean global_search);
+/*! Returns all matching members tags found in given struct/union/class name. + \param name Name of the struct/union/class. + \param file_tags A GPtrArray of edited file TMTag pointers (for search speedup, can be NULL). + \return A GPtrArray of TMTag pointers to struct/union/class members */ +const GPtrArray *tm_workspace_find_scope_members(const GPtrArray *file_tags, + const char *scope_name, + gboolean find_global, + gboolean no_definitions); + +const GPtrArray * +tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *name, + gboolean search_global); + /* Returns TMTag to function which "own" given line \param line Current line in edited file. \param file_tags A GPtrArray of edited file TMTag pointers.
Modified: trunk/tagmanager/tm_workspace.c =================================================================== --- trunk/tagmanager/tm_workspace.c 2009-06-09 21:02:46 UTC (rev 3849) +++ trunk/tagmanager/tm_workspace.c 2009-06-10 12:36:13 UTC (rev 3850) @@ -257,22 +257,6 @@ } }
-static gint get_global_tag_type_mask(gint lang) -{ - switch (lang) - { - case 0: - case 1: - /* C/C++ */ - return tm_tag_class_t | tm_tag_typedef_t | tm_tag_enum_t | tm_tag_enumerator_t | - tm_tag_prototype_t | - tm_tag_function_t | tm_tag_method_t | /* for inline functions */ - tm_tag_macro_t | tm_tag_macro_with_arg_t; - default: - return tm_tag_max_t; - } -} - gboolean tm_workspace_create_global_tags(const char *config_dir, const char *pre_process, const char **includes, int includes_count, const char *tags_file, int lang) { @@ -415,7 +399,7 @@ tm_source_file_free(source_file); return FALSE; } - tags_array = tm_tags_extract(source_file->tags_array, get_global_tag_type_mask(lang)); + tags_array = tm_tags_extract(source_file->tags_array, tm_tag_max_t); if ((NULL == tags_array) || (0 == tags_array->len)) { if (tags_array) @@ -752,6 +736,461 @@ }
+static int +find_scope_members_tags (const GPtrArray * all, GPtrArray * tags, + const langType langJava, const char *name, + const char *filename, gboolean no_definitions) +{ + GPtrArray *local = g_ptr_array_new (); + unsigned int i; + TMTag *tag; + size_t len = strlen (name); + for (i = 0; (i < all->len); ++i) + { + tag = TM_TAG (all->pdata[i]); + if (no_definitions && filename && tag->atts.entry.file && + 0 != strcmp (filename, + tag->atts.entry.file->work_object.short_name)) + { + continue; + } + if (tag && tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0') + { + if (0 == strncmp (name, tag->atts.entry.scope, len)) + { + g_ptr_array_add (local, tag); + } + } + } + if (local->len > 0) + { + unsigned int j; + TMTag *tag2; + char backup = 0; + char *s_backup = NULL; + char *var_type = NULL; + char *scope; + for (i = 0; (i < local->len); ++i) + { + tag = TM_TAG (local->pdata[i]); + scope = tag->atts.entry.scope; + if (scope && 0 == strcmp (name, scope)) + { + g_ptr_array_add (tags, tag); + continue; + } + s_backup = NULL; + j = 0; /* someone could write better code :P */ + while (scope) + { + if (s_backup) + { + backup = s_backup[0]; + s_backup[0] = '\0'; + if (0 == strcmp (name, tag->atts.entry.scope)) + { + j = local->len; + s_backup[0] = backup; + break; + } + } + if (tag->atts.entry.file + && tag->atts.entry.file->lang == langJava) + { + scope = strrchr (tag->atts.entry.scope, '.'); + if (scope) + var_type = scope + 1; + } + else + { + scope = strrchr (tag->atts.entry.scope, ':'); + if (scope) + { + var_type = scope + 1; + scope--; + } + } + if (s_backup) + { + s_backup[0] = backup; + } + if (scope) + { + if (s_backup) + { + backup = s_backup[0]; + s_backup[0] = '\0'; + } + for (j = 0; (j < local->len); ++j) + { + if (i == j) + continue; + tag2 = TM_TAG (local->pdata[j]); + if (tag2->atts.entry.var_type && + 0 == strcmp (var_type, tag2->atts.entry.var_type)) + { + break; + } + } + if (s_backup) + s_backup[0] = backup; + } + if (j < local->len) + { + break; + } + s_backup = scope; + } + if (j == local->len) + { + g_ptr_array_add (tags, tag); + } + } + } + g_ptr_array_free (local, TRUE); + return (int) tags->len; +} + + +#if 0 +static int +find_namespace_members_tags (const GPtrArray * all, GPtrArray * tags, + const langType langJava, const char *name, + const char *filename) +{ + GPtrArray *local = g_ptr_array_new (); + unsigned int i; + TMTag *tag; + size_t len = strlen (name); + + g_return_val_if_fail (all != NULL, 0); + + for (i = 0; (i < all->len); ++i) + { + tag = TM_TAG (all->pdata[i]); + if (filename && tag->atts.entry.file && + 0 != strcmp (filename, + tag->atts.entry.file->work_object.short_name)) + { + continue; + } + + if (tag && tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0') + { + if (0 == strncmp (name, tag->atts.entry.scope, len)) + { + g_ptr_array_add (local, tag); + } + } + } + + if (local->len > 0) + { + char *scope; + for (i = 0; (i < local->len); ++i) + { + tag = TM_TAG (local->pdata[i]); + scope = tag->atts.entry.scope; + + /* if we wanna complete something like + * namespace1:: + * we'll just return the tags that have "namespace1" + * as their scope. So we won't return classes/members/namespaces + * under, for example, namespace2, where namespace1::namespace2 + */ + if (scope && 0 == strcmp (name, scope)) + { + g_ptr_array_add (tags, tag); + } + } + } + + g_ptr_array_free (local, TRUE); + return (int) tags->len; +} + +const GPtrArray * +tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *name, + gboolean search_global) +{ + static GPtrArray *tags = NULL; + GPtrArray *local = NULL; + char *new_name = (char *) name; + char *filename = NULL; + int found = 0, del = 0; + static langType langJava = -1; + TMTag *tag = NULL; + + g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL); + + if (!tags) + tags = g_ptr_array_new (); + + while (1) + { + const GPtrArray *tags2; + int got = 0, types = (tm_tag_class_t | tm_tag_namespace_t | + tm_tag_struct_t | tm_tag_typedef_t | + tm_tag_union_t | tm_tag_enum_t); + + if (file_tags) + { + g_ptr_array_set_size (tags, 0); + got = fill_find_tags_array (tags, file_tags, + new_name, NULL, types, FALSE, -1, FALSE); + } + + + if (got) + { + tags2 = tags; + } + else + { + TMTagAttrType attrs[] = { + tm_tag_attr_name_t, tm_tag_attr_type_t, + tm_tag_attr_none_t + }; + tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1); + } + + if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0]))) + { + if (tag->type == tm_tag_typedef_t && tag->atts.entry.var_type + && tag->atts.entry.var_type[0] != '\0') + { + new_name = tag->atts.entry.var_type; + continue; + } + filename = (tag->atts.entry.file ? + tag->atts.entry.file->work_object.short_name : NULL); + if (tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0') + { + del = 1; + if (tag->atts.entry.file && + tag->atts.entry.file->lang == langJava) + { + new_name = g_strdup_printf ("%s.%s", + tag->atts.entry.scope, + new_name); + } + else + { + new_name = g_strdup_printf ("%s::%s", + tag->atts.entry.scope, + new_name); + } + } + break; + } + else + { + return NULL; + } + } + + g_ptr_array_set_size (tags, 0); + + if (tag && tag->atts.entry.file) + { + local = tm_tags_extract (tag->atts.entry.file->work_object.tags_array, + (tm_tag_function_t | + tm_tag_field_t | tm_tag_enumerator_t | + tm_tag_namespace_t | tm_tag_class_t )); + } + else + { + local = tm_tags_extract (theWorkspace->work_object.tags_array, + (tm_tag_function_t | tm_tag_prototype_t | + tm_tag_member_t | + tm_tag_field_t | tm_tag_enumerator_t | + tm_tag_namespace_t | tm_tag_class_t )); + } + + if (local) + { + found = find_namespace_members_tags (local, tags, + langJava, new_name, filename); + g_ptr_array_free (local, TRUE); + } + + + if (!found && search_global) + { + GPtrArray *global = tm_tags_extract (theWorkspace->global_tags, + (tm_tag_member_t | + tm_tag_prototype_t | + tm_tag_field_t | + tm_tag_method_t | + tm_tag_function_t | + tm_tag_enumerator_t | + tm_tag_namespace_t | + tm_tag_class_t )); + + if (global) + { + find_namespace_members_tags (global, tags, langJava, + new_name, filename); +/*/ + DEBUG_PRINT ("returning these"); + gint i; + for (i=0; i < tags->len; i++) { + TMTag *cur_tag; + + cur_tag = (TMTag*)g_ptr_array_index (tags, i); + tm_tag_print (cur_tag, stdout ); + } +/*/ + g_ptr_array_free (global, TRUE); + } + } + + + if (del) + { + g_free (new_name); + } + + return tags; +} +#endif + +const GPtrArray * +tm_workspace_find_scope_members (const GPtrArray * file_tags, const char *name, + gboolean search_global, gboolean no_definitions) +{ + static GPtrArray *tags = NULL; + GPtrArray *local = NULL; + char *new_name = (char *) name; + char *filename = NULL; + int found = 0, del = 0; + static langType langJava = -1; + TMTag *tag = NULL; + + /* FIXME */ + /* langJava = getNamedLanguage ("Java"); */ + + g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL); + + if (!tags) + tags = g_ptr_array_new (); + + while (1) + { + const GPtrArray *tags2; + int got = 0, types = (tm_tag_class_t | tm_tag_namespace_t | + tm_tag_struct_t | tm_tag_typedef_t | + tm_tag_union_t | tm_tag_enum_t); + + if (file_tags) + { + g_ptr_array_set_size (tags, 0); + got = fill_find_tags_array (tags, file_tags, + new_name, NULL, types, FALSE, -1, FALSE); + } + if (got) + { + tags2 = tags; + } + else + { + TMTagAttrType attrs[] = { + tm_tag_attr_name_t, tm_tag_attr_type_t, + tm_tag_attr_none_t + }; + tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1); + } + + if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0]))) + { + if (tag->type == tm_tag_typedef_t && tag->atts.entry.var_type + && tag->atts.entry.var_type[0] != '\0') + { + char *tmp_name; + tmp_name = tag->atts.entry.var_type; + if (strcmp(tmp_name, new_name) == 0) { + new_name = NULL; + } + else { + new_name = tmp_name; + } + continue; + } + filename = (tag->atts.entry.file ? + tag->atts.entry.file->work_object.short_name : NULL); + if (tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0') + { + del = 1; + if (tag->atts.entry.file && + tag->atts.entry.file->lang == langJava) + { + new_name = g_strdup_printf ("%s.%s", + tag->atts.entry.scope, + new_name); + } + else + { + new_name = g_strdup_printf ("%s::%s", + tag->atts.entry.scope, + new_name); + } + } + break; + } + else + { + return NULL; + } + } + + g_ptr_array_set_size (tags, 0); + + if (no_definitions && tag && tag->atts.entry.file) + { + local = tm_tags_extract (tag->atts.entry.file->work_object.tags_array, + (tm_tag_function_t | tm_tag_prototype_t | + tm_tag_member_t | tm_tag_field_t | + tm_tag_method_t | tm_tag_enumerator_t)); + } + else + { + local = tm_tags_extract (theWorkspace->work_object.tags_array, + (tm_tag_function_t | tm_tag_prototype_t | + tm_tag_member_t | tm_tag_field_t | + tm_tag_method_t | tm_tag_enumerator_t)); + } + if (local) + { + found = find_scope_members_tags (local, tags, langJava, new_name, + filename, no_definitions); + g_ptr_array_free (local, TRUE); + } + if (!found && search_global) + { + GPtrArray *global = tm_tags_extract (theWorkspace->global_tags, + (tm_tag_member_t | + tm_tag_prototype_t | + tm_tag_field_t | + tm_tag_method_t | + tm_tag_function_t | + tm_tag_enumerator_t + |tm_tag_struct_t | tm_tag_typedef_t | + tm_tag_union_t | tm_tag_enum_t)); + if (global) + { + find_scope_members_tags (global, tags, langJava, new_name, + filename, no_definitions); + g_ptr_array_free (global, TRUE); + } + } + if (del) + { + g_free (new_name); + } + + return tags; +} + const GPtrArray *tm_workspace_get_parents(const gchar *name) { static TMTagAttrType type[] = { tm_tag_attr_name_t, tm_tag_attr_none_t };
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.