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

Filter for symbol tree - improved version #3055

Merged
merged 12 commits into from
Jan 23, 2022
Merged
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
2 changes: 1 addition & 1 deletion HACKING
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ MAP_ENTRY(FOO) to parser_map.
(You may want to make the symbols.c change before doing this).

In src/tagmanager/tm_parser.c:
Update tm_parser_context_separator() and tm_parser_has_full_context() to
Update tm_parser_scope_separator() and tm_parser_has_full_scope() to
handle the new parser if applicable, by adding a TM_PARSER_FOO case entry.

In filetypes.c, init_builtin_filetypes():
Expand Down
45 changes: 39 additions & 6 deletions data/geany.glade
Original file line number Diff line number Diff line change
Expand Up @@ -8245,17 +8245,50 @@
<signal name="switch-page" handler="on_tv_notebook_switch_page" swapped="no"/>
<signal name="switch-page" handler="on_tv_notebook_switch_page_after" after="yes" swapped="no"/>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<object class="GtkVBox" id="vbox46">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="can_focus">False</property>
<child>
<object class="GtkEntry" id="entry_tagfilter">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Filter the symbol list using the entered text. Separate multiple filters with a space.</property>
<property name="invisible_char">•</property>
<property name="primary_icon_stock">gtk-find</property>
<property name="secondary_icon_stock">gtk-clear</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">True</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<signal name="changed" handler="on_entry_tagfilter_changed" swapped="no"/>
<signal name="activate" handler="on_entry_tagfilter_activate" swapped="no"/>
<signal name="icon-press" handler="on_entry_tagfilter_icon_press" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTreeView" id="treeview2">
<object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="enable_search">False</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<object class="GtkTreeView" id="treeview2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="enable_search">False</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
Expand Down
25 changes: 25 additions & 0 deletions doc/geany.txt
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,31 @@ The position of the tabs can be selected in the interface preferences.
The sizes of the sidebar and message window can be adjusted by
dragging the dividers.

Sidebar Usage
^^^^^^^^^^^^^

The sidebar has a right click menu that can control what is visible and
has actions specific to the tab (other tabs added by plugins are
described by that plugin documentation):

* Symbols

* expand/collapse the tree
* control sorting order
* locate the symbol in documents

The symbols tab can also be filtered by typing a string into
the entry at the top of the tab. All symbols that contain the entered
string as a substring will be shown in the tree. Multiple filters can
be separated by a space.

* Documents

* expand/collapse the tree
* save to or reload from files
* search tree based at selected file
* show or hide the document paths

Command line options
--------------------

Expand Down
49 changes: 48 additions & 1 deletion src/callbacks.c
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,44 @@ void on_toolbutton_search_clicked(GtkAction *action, gpointer user_data)
}


void on_entry_tagfilter_changed(GtkAction *action, gpointer user_data)
{
GeanyDocument *doc = document_get_current();
GtkEntry *filter_entry;

if (!doc)
return;

filter_entry = GTK_ENTRY(ui_lookup_widget(main_widgets.window, "entry_tagfilter"));
g_free(doc->priv->tag_filter);
doc->priv->tag_filter = g_strdup(gtk_entry_get_text(filter_entry));

/* make sure the tree is fully re-created so it appears correctly
* after applying filter */
if (doc->priv->tag_store)
gtk_tree_store_clear(doc->priv->tag_store);
sidebar_update_tag_list(doc, TRUE);
}


void on_entry_tagfilter_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, gpointer user_data)
{
if (event->button.button == 1)
gtk_entry_set_text(entry, "");
}


void on_entry_tagfilter_activate(GtkEntry *entry, gpointer user_data)
{
GeanyDocument *doc = document_get_current();

if (!doc)
return;

gtk_widget_grab_focus(doc->priv->tag_tree);
}


/* hides toolbar from toolbar popup menu */
static void on_hide_toolbar1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
Expand Down Expand Up @@ -491,14 +529,23 @@ static void on_notebook1_switch_page_after(GtkNotebook *notebook, gpointer page,

if (doc != NULL)
{
GtkEntry *filter_entry = GTK_ENTRY(ui_lookup_widget(main_widgets.window, "entry_tagfilter"));
const gchar *entry_text = gtk_entry_get_text(filter_entry);

sidebar_select_openfiles_item(doc);
ui_save_buttons_toggle(doc->changed);
ui_set_window_title(doc);
ui_update_statusbar(doc, -1);
ui_update_popup_reundo_items(doc);
ui_document_show_hide(doc); /* update the document menu */
build_menu_update(doc);
sidebar_update_tag_list(doc, FALSE);
if (g_strcmp0(entry_text, doc->priv->tag_filter) != 0)
{
/* calls sidebar_update_tag_list() in on_entry_tagfilter_changed() */
gtk_entry_set_text(filter_entry, doc->priv->tag_filter);
techee marked this conversation as resolved.
Show resolved Hide resolved
}
else
sidebar_update_tag_list(doc, TRUE);
document_highlight_tags(doc);

document_check_disk_status(doc, TRUE);
Expand Down
6 changes: 6 additions & 0 deletions src/callbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ void on_toolbar_search_entry_changed(GtkAction *action, const gchar *text, gpoin

void on_toolbar_search_entry_activate(GtkAction *action, const gchar *text, gpointer user_data);

void on_entry_tagfilter_changed(GtkAction *action, gpointer user_data);

void on_entry_tagfilter_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, gpointer user_data);

void on_entry_tagfilter_activate(GtkEntry *entry, gpointer user_data);

void on_toggle_case1_activate(GtkMenuItem *menuitem, gpointer user_data);

void on_find_usage1_activate(GtkMenuItem *menuitem, gpointer user_data);
Expand Down
2 changes: 2 additions & 0 deletions src/document.c
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ static GeanyDocument *document_create(const gchar *utf8_filename)

/* initialize default document settings */
doc->priv = g_new0(GeanyDocumentPrivate, 1);
doc->priv->tag_filter = g_strdup("");
doc->id = ++doc_id_counter;
doc->index = new_idx;
doc->file_name = g_strdup(utf8_filename);
Expand Down Expand Up @@ -734,6 +735,7 @@ static gboolean remove_page(guint page_num)
}
g_free(doc->encoding);
g_free(doc->priv->saved_encoding.encoding);
g_free(doc->priv->tag_filter);
g_free(doc->file_name);
g_free(doc->real_path);
if (doc->tm_file)
Expand Down
2 changes: 2 additions & 0 deletions src/documentprivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ typedef struct GeanyDocumentPrivate
GtkWidget *info_bars[NUM_MSG_TYPES];
/* Keyed Data List to attach arbitrary data to the document */
GData *data;
/* Text used for filtering symbol tree. */
gchar *tag_filter;
}
GeanyDocumentPrivate;

Expand Down
6 changes: 3 additions & 3 deletions src/editor.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ static void show_tags_list(GeanyEditor *editor, const GPtrArray *tags, gsize roo
static gint scope_autocomplete_suffix(ScintillaObject *sci, TMParserType lang,
gint pos, gboolean *scope_sep)
{
const gchar *sep = tm_parser_context_separator(lang);
const gchar *sep = tm_parser_scope_separator(lang);
const gsize max_len = 3;
gboolean is_scope_sep;
gchar *buf;
Expand Down Expand Up @@ -708,7 +708,7 @@ static gboolean autocomplete_scope(GeanyEditor *editor, const gchar *root, gsize
gboolean scope_sep_typed = FALSE;
gboolean ret = FALSE;
const gchar *current_scope;
const gchar *context_sep = tm_parser_context_separator(ft->lang);
const gchar *context_sep = tm_parser_scope_separator(ft->lang);
gint autocomplete_suffix_len;

if (autocomplete_scope_shown)
Expand Down Expand Up @@ -1866,7 +1866,7 @@ static gchar *find_calltip(const gchar *word, GeanyFiletype *ft)
{
const TMTagType arg_types = tm_tag_function_t | tm_tag_prototype_t |
tm_tag_method_t | tm_tag_macro_with_arg_t;
const gchar *scope_sep = tm_parser_context_separator(ft->lang);
const gchar *scope_sep = tm_parser_scope_separator(ft->lang);
gchar *scope = EMPTY(tag->scope) ? g_strdup(tag->name) :
g_strjoin(scope_sep, tag->scope, tag->name, NULL);

Expand Down
54 changes: 43 additions & 11 deletions src/symbols.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ GString *symbols_find_typenames_as_string(TMParserType lang, gboolean global)
GEANY_API_SYMBOL
const gchar *symbols_get_context_separator(gint ft_id)
{
return tm_parser_context_separator(filetypes[ft_id]->lang);
return tm_parser_scope_separator(filetypes[ft_id]->lang);
}


Expand Down Expand Up @@ -319,26 +319,58 @@ static gint compare_symbol_lines(gconstpointer a, gconstpointer b)
static GList *get_tag_list(GeanyDocument *doc, TMTagType tag_types)
{
GList *tag_names = NULL;
guint i;
guint i, j;
gchar **tf_strv;

g_return_val_if_fail(doc, NULL);

if (! doc->tm_file || ! doc->tm_file->tags_array)
return NULL;

tf_strv = g_strsplit_set(doc->priv->tag_filter, " ", -1);
techee marked this conversation as resolved.
Show resolved Hide resolved

for (i = 0; i < doc->tm_file->tags_array->len; ++i)
{
TMTag *tag = TM_TAG(doc->tm_file->tags_array->pdata[i]);

if (G_UNLIKELY(tag == NULL))
return NULL;

if (tag->type & tag_types)
{
tag_names = g_list_prepend(tag_names, tag);
gboolean filtered = FALSE;
gchar **val;
gchar *full_tagname = g_strconcat(tag->scope ? tag->scope : "",
tag->scope ? tm_parser_scope_separator_printable(tag->lang) : "",
tag->name, NULL);
gchar *normalized_tagname = g_utf8_normalize(full_tagname, -1, G_NORMALIZE_ALL);

foreach_strv(val, tf_strv)
{
gchar *normalized_val = g_utf8_normalize(*val, -1, G_NORMALIZE_ALL);

if (normalized_tagname != NULL && normalized_val != NULL)
{
gchar *case_normalized_tagname = g_utf8_casefold(normalized_tagname, -1);
gchar *case_normalized_val = g_utf8_casefold(normalized_val, -1);

filtered = strstr(case_normalized_tagname, case_normalized_val) == NULL;
g_free(case_normalized_tagname);
g_free(case_normalized_val);
}
g_free(normalized_val);

if (filtered)
break;
}
if (!filtered)
tag_names = g_list_prepend(tag_names, tag);

g_free(normalized_tagname);
g_free(full_tagname);
}
}
tag_names = g_list_sort(tag_names, compare_symbol_lines);

g_strfreev(tf_strv);

return tag_names;
}

Expand Down Expand Up @@ -928,7 +960,7 @@ static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboole
if (!found_parent && scope &&
strpbrk(scope, GEANY_WORDCHARS) == scope)
{
const gchar *sep = symbols_get_context_separator(doc->file_type->id);
const gchar *sep = tm_parser_scope_separator_printable(tag->lang);

g_string_append(buffer, scope);
g_string_append(buffer, sep);
Expand Down Expand Up @@ -1184,7 +1216,7 @@ static void update_parents_table(GHashTable *table, const TMTag *tag, const GtkT
/* simple case, just use the tag name */
name = tag->name;
}
else if (! tm_parser_has_full_context(tag->lang))
else if (! tm_parser_has_full_scope(tag->lang))
{
/* if the parser doesn't use fully qualified scope, use the name alone but
* prevent Foo::Foo from making parent = child */
Expand All @@ -1196,7 +1228,7 @@ static void update_parents_table(GHashTable *table, const TMTag *tag, const GtkT
else
{
/* build the fully qualified scope as get_parent_name() would return it for a child tag */
name_free = g_strconcat(tag->scope, tm_parser_context_separator(tag->lang), tag->name, NULL);
name_free = g_strconcat(tag->scope, tm_parser_scope_separator(tag->lang), tag->name, NULL);
name = name_free;
}

Expand Down Expand Up @@ -1366,7 +1398,7 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags)
/* Build hash tables holding tags and parents */
/* parent table is GHashTable<tag_name, GTree<line_num, GtkTreeIter>>
* where tag_name might be a fully qualified name (with scope) if the language
* parser reports scope properly (see tm_parser_has_full_context()). */
* parser reports scope properly (see tm_parser_has_full_scope()). */
parents_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, parents_table_value_free);
/* tags table is another representation of the @tags list,
* GHashTable<TMTag, GTree<line_num, GList<GList<TMTag>>>> */
Expand Down Expand Up @@ -2419,7 +2451,7 @@ static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, TMTagType
{
if (tag->scope)
*tagname = g_strconcat(tag->scope,
symbols_get_context_separator(doc->file_type->id), tag->name, NULL);
tm_parser_scope_separator(tag->lang), tag->name, NULL);
else
*tagname = g_strdup(tag->name);

Expand Down