Skip to content

Commit

Permalink
Merge pull request #3055 from techee/tagfilter-new
Browse files Browse the repository at this point in the history
Filter for symbol tree - improved version
  • Loading branch information
eht16 committed Jan 23, 2022
2 parents 8c222f8 + bd93be4 commit 5a369a4
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 33 deletions.
2 changes: 1 addition & 1 deletion HACKING
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
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
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
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);
}
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
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
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
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
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
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);

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 @@ -940,7 +972,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 @@ -1196,7 +1228,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 @@ -1208,7 +1240,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 @@ -1378,7 +1410,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 @@ -2431,7 +2463,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

0 comments on commit 5a369a4

Please sign in to comment.