[gnome-builder] plugins/symbol-tree: port to GTK 4
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] plugins/symbol-tree: port to GTK 4
- Date: Tue, 12 Jul 2022 06:39:19 +0000 (UTC)
commit 621af4a4b2bd78d8a15164acc20a1e0c8c6c48df
Author: Christian Hergert <chergert redhat com>
Date: Mon Jul 11 23:20:25 2022 -0700
plugins/symbol-tree: port to GTK 4
This ports the symbol tree plugin to GTK 4 and also changes how it is
presented in the workspace. It's now in the statusbar and updates as
you move through the document like before. However there is now just a
single symbol-tree for the workspace window.
src/plugins/symbol-tree/gbp-symbol-frame-addin.c | 574 ---------------------
.../symbol-tree/gbp-symbol-hover-provider.c | 94 ++--
src/plugins/symbol-tree/gbp-symbol-list-model.c | 184 +++++++
...mbol-tree-builder.h => gbp-symbol-list-model.h} | 15 +-
src/plugins/symbol-tree/gbp-symbol-menu-button.c | 330 ------------
src/plugins/symbol-tree/gbp-symbol-menu-button.ui | 86 ---
src/plugins/symbol-tree/gbp-symbol-popover-row.ui | 42 ++
src/plugins/symbol-tree/gbp-symbol-popover.c | 433 ++++++++++++++++
...p-symbol-menu-button.h => gbp-symbol-popover.h} | 21 +-
src/plugins/symbol-tree/gbp-symbol-popover.ui | 51 ++
src/plugins/symbol-tree/gbp-symbol-tree-builder.c | 263 ----------
src/plugins/symbol-tree/gbp-symbol-util.c | 324 ++++++++++++
src/plugins/symbol-tree/gbp-symbol-util.h | 42 ++
.../symbol-tree/gbp-symbol-workspace-addin.c | 473 +++++++++++++++++
...-frame-addin.h => gbp-symbol-workspace-addin.h} | 10 +-
src/plugins/symbol-tree/gtk/keybindings.json | 1 +
src/plugins/symbol-tree/meson.build | 7 +-
src/plugins/symbol-tree/symbol-tree-plugin.c | 15 +-
src/plugins/symbol-tree/symbol-tree.gresource.xml | 5 +-
src/plugins/symbol-tree/symbol-tree.plugin | 6 +-
src/plugins/symbol-tree/themes/shared.css | 20 -
21 files changed, 1643 insertions(+), 1353 deletions(-)
---
diff --git a/src/plugins/symbol-tree/gbp-symbol-hover-provider.c
b/src/plugins/symbol-tree/gbp-symbol-hover-provider.c
index 75f06c190..3b1dd96ea 100644
--- a/src/plugins/symbol-tree/gbp-symbol-hover-provider.c
+++ b/src/plugins/symbol-tree/gbp-symbol-hover-provider.c
@@ -22,10 +22,11 @@
#include "config.h"
+#include <glib/gi18n.h>
+
#include <libide-code.h>
#include <libide-gui.h>
#include <libide-editor.h>
-#include <glib/gi18n.h>
#include "gbp-symbol-hover-provider.h"
@@ -41,8 +42,8 @@ on_activate_link (GtkLabel *label,
const gchar *uristr,
IdeLocation *location)
{
+ g_autoptr(IdePanelPosition) position = NULL;
IdeWorkspace *workspace;
- IdeSurface *surface;
g_assert (uristr != NULL);
g_assert (GTK_IS_LABEL (label));
@@ -51,10 +52,8 @@ on_activate_link (GtkLabel *label,
if (!(workspace = ide_widget_get_workspace (GTK_WIDGET (label))))
return FALSE;
- if (!(surface = ide_workspace_get_surface_by_name (workspace, "editor")))
- return FALSE;
-
- ide_editor_surface_focus_location (IDE_EDITOR_SURFACE (surface), location);
+ position = ide_panel_position_new ();
+ ide_editor_focus_location (workspace, position, location);
return TRUE;
}
@@ -69,9 +68,9 @@ gbp_symbol_hover_provider_get_symbol_cb (GObject *object,
g_autoptr(IdeSymbol) symbol = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *tt = NULL;
- IdeHoverContext *context;
+ GtkSourceHoverDisplay *display;
const gchar *name;
- GtkWidget *box;
+ GtkBox *box;
struct {
const gchar *kind;
IdeLocation *loc;
@@ -90,16 +89,19 @@ gbp_symbol_hover_provider_get_symbol_cb (GObject *object,
return;
}
- context = ide_task_get_task_data (task);
- g_assert (context != NULL);
- g_assert (IDE_IS_HOVER_CONTEXT (context));
+ display = ide_task_get_task_data (task);
+ g_assert (display != NULL);
+ g_assert (GTK_SOURCE_IS_HOVER_DISPLAY (display));
loc[0].loc = ide_symbol_get_location (symbol);
loc[1].loc = ide_symbol_get_header_location (symbol);
if (!loc[0].loc && !loc[1].loc)
{
- ide_task_return_boolean (task, TRUE);
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Not supported");
return;
}
@@ -113,16 +115,17 @@ gbp_symbol_hover_provider_get_symbol_cb (GObject *object,
name = _("Unnamed Symbol");
tt = g_strdup_printf ("<tt><span size='smaller'>%s</span></tt>", name);
- gtk_container_add (GTK_CONTAINER (box),
- g_object_new (GTK_TYPE_LABEL,
- "ellipsize", PANGO_ELLIPSIZE_END,
- "visible", TRUE,
- "xalign", 0.0f,
- "margin-bottom", 3,
- "selectable", TRUE,
- "use-markup", TRUE,
- "label", tt,
- NULL));
+
+ gtk_box_append (box,
+ g_object_new (GTK_TYPE_LABEL,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "visible", TRUE,
+ "xalign", 0.0f,
+ "margin-bottom", 3,
+ "selectable", TRUE,
+ "use-markup", TRUE,
+ "label", tt,
+ NULL));
for (guint i = 0; i < G_N_ELEMENTS (loc); i++)
@@ -147,47 +150,52 @@ gbp_symbol_hover_provider_get_symbol_cb (GObject *object,
g_object_ref (loc[i].loc),
(GClosureNotify)g_object_unref,
0);
- gtk_container_add (GTK_CONTAINER (box), label);
+ gtk_box_append (box, label);
}
+
}
- ide_hover_context_add_widget (context, SYMBOL_TREE_HOVER_PRIORITY, _("Symbol"), box);
+ gtk_source_hover_display_append (display, GTK_WIDGET (box));
+
ide_task_return_boolean (task, TRUE);
}
static void
-gbp_symbol_hover_provider_hover_async (IdeHoverProvider *provider,
- IdeHoverContext *context,
- const GtkTextIter *iter,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+gbp_symbol_hover_provider_populate_async (GtkSourceHoverProvider *provider,
+ GtkSourceHoverContext *context,
+ GtkSourceHoverDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GbpSymbolHoverProvider *self = (GbpSymbolHoverProvider *)provider;
g_autoptr(IdeTask) task = NULL;
+ GtkTextIter iter;
IdeBuffer *buffer;
g_assert (GBP_IS_SYMBOL_HOVER_PROVIDER (self));
- g_assert (IDE_IS_HOVER_CONTEXT (context));
- g_assert (iter != NULL);
+ g_assert (GTK_SOURCE_IS_HOVER_CONTEXT (context));
+ g_assert (GTK_SOURCE_IS_HOVER_DISPLAY (display));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_task_new (self, cancellable, callback, user_data);
- ide_task_set_source_tag (task, gbp_symbol_hover_provider_hover_async);
- ide_task_set_task_data (task, g_object_ref (context), g_object_unref);
+ ide_task_set_source_tag (task, gbp_symbol_hover_provider_populate_async);
+ ide_task_set_task_data (task, g_object_ref (display), g_object_unref);
+
+ gtk_source_hover_context_get_iter (context, &iter);
+ buffer = IDE_BUFFER (gtk_source_hover_context_get_buffer (context));
- buffer = IDE_BUFFER (gtk_text_iter_get_buffer (iter));
ide_buffer_get_symbol_at_location_async (buffer,
- iter,
+ &iter,
cancellable,
gbp_symbol_hover_provider_get_symbol_cb,
g_steal_pointer (&task));
}
static gboolean
-gbp_symbol_hover_provider_hover_finish (IdeHoverProvider *provider,
- GAsyncResult *result,
- GError **error)
+gbp_symbol_hover_provider_populate_finish (GtkSourceHoverProvider *provider,
+ GAsyncResult *result,
+ GError **error)
{
g_assert (GBP_IS_SYMBOL_HOVER_PROVIDER (provider));
g_assert (IDE_IS_TASK (result));
@@ -196,14 +204,14 @@ gbp_symbol_hover_provider_hover_finish (IdeHoverProvider *provider,
}
static void
-hover_provider_iface_init (IdeHoverProviderInterface *iface)
+hover_provider_iface_init (GtkSourceHoverProviderInterface *iface)
{
- iface->hover_async = gbp_symbol_hover_provider_hover_async;
- iface->hover_finish = gbp_symbol_hover_provider_hover_finish;
+ iface->populate_async = gbp_symbol_hover_provider_populate_async;
+ iface->populate_finish = gbp_symbol_hover_provider_populate_finish;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (GbpSymbolHoverProvider, gbp_symbol_hover_provider, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (IDE_TYPE_HOVER_PROVIDER, hover_provider_iface_init))
+ G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_HOVER_PROVIDER,
hover_provider_iface_init))
static void
gbp_symbol_hover_provider_class_init (GbpSymbolHoverProviderClass *klass)
diff --git a/src/plugins/symbol-tree/gbp-symbol-list-model.c b/src/plugins/symbol-tree/gbp-symbol-list-model.c
new file mode 100644
index 000000000..bde035a76
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-list-model.c
@@ -0,0 +1,184 @@
+/* gbp-symbol-list-model.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-symbol-list-model"
+
+#include "config.h"
+
+#include "gbp-symbol-list-model.h"
+
+struct _GbpSymbolListModel
+{
+ GObject parent_instance;
+ IdeSymbolTree *tree;
+ IdeSymbolNode *parent;
+};
+
+enum {
+ PROP_0,
+ PROP_TREE,
+ PROP_PARENT,
+ N_PROPS
+};
+
+static GType
+gbp_symbol_list_model_get_item_type (GListModel *model)
+{
+ return IDE_TYPE_SYMBOL_NODE;
+}
+
+static guint
+gbp_symbol_list_model_get_n_items (GListModel *model)
+{
+ GbpSymbolListModel *self = GBP_SYMBOL_LIST_MODEL (model);
+
+ return ide_symbol_tree_get_n_children (self->tree, self->parent);
+}
+
+static gpointer
+gbp_symbol_list_model_get_item (GListModel *model,
+ guint position)
+{
+ GbpSymbolListModel *self = GBP_SYMBOL_LIST_MODEL (model);
+
+ if (position >= ide_symbol_tree_get_n_children (self->tree, self->parent))
+ return NULL;
+
+ return ide_symbol_tree_get_nth_child (self->tree, self->parent, position);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item = gbp_symbol_list_model_get_item;
+ iface->get_n_items = gbp_symbol_list_model_get_n_items;
+ iface->get_item_type = gbp_symbol_list_model_get_item_type;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpSymbolListModel, gbp_symbol_list_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_symbol_list_model_dispose (GObject *object)
+{
+ GbpSymbolListModel *self = (GbpSymbolListModel *)object;
+
+ g_clear_object (&self->tree);
+ g_clear_object (&self->parent);
+
+ G_OBJECT_CLASS (gbp_symbol_list_model_parent_class)->dispose (object);
+}
+
+static void
+gbp_symbol_list_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpSymbolListModel *self = GBP_SYMBOL_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_TREE:
+ g_value_set_object (value, self->tree);
+ break;
+
+ case PROP_PARENT:
+ g_value_set_object (value, self->parent);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_symbol_list_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpSymbolListModel *self = GBP_SYMBOL_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_TREE:
+ self->tree = g_value_dup_object (value);
+ break;
+
+ case PROP_PARENT:
+ self->parent = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_symbol_list_model_class_init (GbpSymbolListModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gbp_symbol_list_model_dispose;
+ object_class->get_property = gbp_symbol_list_model_get_property;
+ object_class->set_property = gbp_symbol_list_model_set_property;
+
+ properties [PROP_TREE] =
+ g_param_spec_object ("tree",
+ "Tree",
+ "The tree of nodes",
+ IDE_TYPE_SYMBOL_TREE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PARENT] =
+ g_param_spec_object ("parent",
+ "Parent",
+ "The parent node",
+ IDE_TYPE_SYMBOL_NODE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_symbol_list_model_init (GbpSymbolListModel *self)
+{
+}
+
+IdeSymbolTree *
+gbp_symbol_list_model_get_tree (GbpSymbolListModel *self)
+{
+ g_return_val_if_fail (GBP_IS_SYMBOL_LIST_MODEL (self), NULL);
+
+ return self->tree;
+}
+
+GbpSymbolListModel *
+gbp_symbol_list_model_new (IdeSymbolTree *tree,
+ IdeSymbolNode *parent)
+{
+ return g_object_new (GBP_TYPE_SYMBOL_LIST_MODEL,
+ "tree", tree,
+ "parent", parent,
+ NULL);
+}
diff --git a/src/plugins/symbol-tree/gbp-symbol-tree-builder.h
b/src/plugins/symbol-tree/gbp-symbol-list-model.h
similarity index 59%
rename from src/plugins/symbol-tree/gbp-symbol-tree-builder.h
rename to src/plugins/symbol-tree/gbp-symbol-list-model.h
index f999f822c..55eb8b986 100644
--- a/src/plugins/symbol-tree/gbp-symbol-tree-builder.h
+++ b/src/plugins/symbol-tree/gbp-symbol-list-model.h
@@ -1,6 +1,6 @@
-/* gbp-symbol-tree-builder.h
+/* gbp-symbol-list-model.h
*
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,15 +20,16 @@
#pragma once
-#include <libide-gui.h>
+#include <libide-code.h>
G_BEGIN_DECLS
-#define GBP_TYPE_SYMBOL_TREE_BUILDER (gbp_symbol_tree_builder_get_type())
+#define GBP_TYPE_SYMBOL_LIST_MODEL (gbp_symbol_list_model_get_type())
-G_DECLARE_FINAL_TYPE (GbpSymbolTreeBuilder, gbp_symbol_tree_builder, GBP, SYMBOL_TREE_BUILDER,
DzlTreeBuilder)
+G_DECLARE_FINAL_TYPE (GbpSymbolListModel, gbp_symbol_list_model, GBP, SYMBOL_LIST_MODEL, GObject)
-void gbp_symbol_tree_builder_set_filter (GbpSymbolTreeBuilder *self,
- const gchar *filter);
+GbpSymbolListModel *gbp_symbol_list_model_new (IdeSymbolTree *tree,
+ IdeSymbolNode *parent);
+IdeSymbolTree *gbp_symbol_list_model_get_tree (GbpSymbolListModel *self);
G_END_DECLS
diff --git a/src/plugins/symbol-tree/gbp-symbol-popover-row.ui
b/src/plugins/symbol-tree/gbp-symbol-popover-row.ui
new file mode 100644
index 000000000..3e339354e
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-popover-row.ui
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkTreeExpander" id="expander">
+ <binding name="list-row">
+ <lookup name="item">GtkListItem</lookup>
+ </binding>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="spacing">6</property>
+ <property name="margin-top">3</property>
+ <property name="margin-bottom">3</property>
+ <property name="margin-start">3</property>
+ <property name="margin-end">3</property>
+ <child>
+ <object class="GtkImage">
+ <binding name="icon-name">
+ <lookup name="icon-name" type="IdeSymbolNode">
+ <lookup name="item">expander</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="halign">start</property>
+ <property name="use-markup">true</property>
+ <binding name="label">
+ <lookup name="name" type="IdeSymbolNode">
+ <lookup name="item">expander</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </property>
+ </template>
+</interface>
+
diff --git a/src/plugins/symbol-tree/gbp-symbol-popover.c b/src/plugins/symbol-tree/gbp-symbol-popover.c
new file mode 100644
index 000000000..87c231f37
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-popover.c
@@ -0,0 +1,433 @@
+/* gbp-symbol-popover.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-symbol-popover"
+
+#include "config.h"
+
+#include <libide-editor.h>
+#include <libide-gui.h>
+
+#include "gbp-symbol-list-model.h"
+#include "gbp-symbol-popover.h"
+
+struct _GbpSymbolPopover
+{
+ GtkPopover parent_instance;
+
+ IdeSymbolTree *symbol_tree;
+ GtkFilter *filter;
+
+ GtkSearchEntry *search_entry;
+ GtkListView *list_view;
+
+ char **search_needle;
+};
+
+enum {
+ PROP_0,
+ PROP_SYMBOL_TREE,
+ N_PROPS
+};
+
+G_DEFINE_FINAL_TYPE (GbpSymbolPopover, gbp_symbol_popover, GTK_TYPE_POPOVER)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_symbol_popover_get_location_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSymbolNode *node = (IdeSymbolNode *)object;
+ g_autoptr(GbpSymbolPopover) self = user_data;
+ g_autoptr(IdeLocation) location = NULL;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+
+ if ((location = ide_symbol_node_get_location_finish (node, result, &error)))
+ {
+ IdeWorkspace *workspace = ide_widget_get_workspace (GTK_WIDGET (self));
+ g_autoptr(IdePanelPosition) position = ide_panel_position_new ();
+
+ ide_editor_focus_location (workspace, position, location);
+
+ gtk_popover_popdown (GTK_POPOVER (self));
+ }
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_popover_activate_cb (GbpSymbolPopover *self,
+ guint position,
+ GtkListView *list_view)
+{
+ GtkTreeListRow *row;
+ IdeSymbolNode *node;
+ GListModel *model;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+ g_assert (GTK_IS_LIST_VIEW (list_view));
+
+ g_debug ("Activating symbol row at position %u", position);
+
+ model = G_LIST_MODEL (gtk_list_view_get_model (list_view));
+ row = g_list_model_get_item (model, position);
+ node = gtk_tree_list_row_get_item (row);
+
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+
+ ide_symbol_node_get_location_async (node,
+ NULL,
+ gbp_symbol_popover_get_location_cb,
+ g_object_ref (self));
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_popover_search_activate_cb (GbpSymbolPopover *self,
+ GtkSearchEntry *search_entry)
+{
+ guint position;
+
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+ g_assert (GTK_IS_SEARCH_ENTRY (search_entry));
+
+ if (ide_gtk_list_view_get_selected_row (self->list_view, &position))
+ gbp_symbol_popover_activate_cb (self, position, self->list_view);
+}
+
+static void
+gbp_symbol_popover_search_changed_cb (GbpSymbolPopover *self,
+ GtkSearchEntry *search_entry)
+{
+ const char *text;
+
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+ g_assert (GTK_IS_SEARCH_ENTRY (search_entry));
+
+ text = gtk_editable_get_text (GTK_EDITABLE (search_entry));
+ g_clear_pointer (&self->search_needle, g_strfreev);
+
+ if (text && *text)
+ self->search_needle = g_str_tokenize_and_fold (text, NULL, NULL);
+
+ if (self->filter != NULL)
+ gtk_filter_changed (self->filter, GTK_FILTER_CHANGE_DIFFERENT);
+}
+
+static gboolean
+gbp_symbol_popover_grab_focus (GtkWidget *widget)
+{
+ GbpSymbolPopover *self = (GbpSymbolPopover *)widget;
+ gboolean ret;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+
+ ret = gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
+ gtk_editable_select_region (GTK_EDITABLE (self->search_entry), 0, -1);
+
+ IDE_RETURN (ret);
+}
+
+static gboolean
+on_search_key_pressed_cb (GbpSymbolPopover *self,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state,
+ GtkEventControllerKey *key)
+{
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+ g_assert (GTK_IS_EVENT_CONTROLLER_KEY (key));
+
+ if ((state & (GDK_CONTROL_MASK | GDK_ALT_MASK)) == 0)
+ {
+ switch (keyval)
+ {
+ case GDK_KEY_Escape:
+ {
+ IdeWorkspace *workspace = ide_widget_get_workspace (GTK_WIDGET (self));
+ IdePage *page = ide_workspace_get_most_recent_page (workspace);
+
+ gtk_popover_popdown (GTK_POPOVER (self));
+ if (page)
+ gtk_widget_grab_focus (GTK_WIDGET (page));
+
+ return TRUE;
+ }
+
+ case GDK_KEY_Up:
+ case GDK_KEY_KP_Up:
+ ide_gtk_list_view_move_previous (self->list_view);
+ return TRUE;
+
+ case GDK_KEY_Down:
+ case GDK_KEY_KP_Down:
+ ide_gtk_list_view_move_next (self->list_view);
+ return TRUE;
+
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gbp_symbol_popover_dispose (GObject *object)
+{
+ GbpSymbolPopover *self = (GbpSymbolPopover *)object;
+
+ g_clear_object (&self->filter);
+ g_clear_object (&self->symbol_tree);
+ g_clear_pointer (&self->search_needle, g_strfreev);
+
+ G_OBJECT_CLASS (gbp_symbol_popover_parent_class)->dispose (object);
+}
+
+static void
+gbp_symbol_popover_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpSymbolPopover *self = GBP_SYMBOL_POPOVER (object);
+
+ switch (prop_id)
+ {
+ case PROP_SYMBOL_TREE:
+ g_value_set_object (value, gbp_symbol_popover_get_symbol_tree (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_symbol_popover_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpSymbolPopover *self = GBP_SYMBOL_POPOVER (object);
+
+ switch (prop_id)
+ {
+ case PROP_SYMBOL_TREE:
+ gbp_symbol_popover_set_symbol_tree (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_symbol_popover_class_init (GbpSymbolPopoverClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gbp_symbol_popover_dispose;
+ object_class->get_property = gbp_symbol_popover_get_property;
+ object_class->set_property = gbp_symbol_popover_set_property;
+
+ widget_class->grab_focus = gbp_symbol_popover_grab_focus;
+
+ properties [PROP_SYMBOL_TREE] =
+ g_param_spec_object ("symbol-tree",
+ "Symbol Tree",
+ "The symbol tree to display",
+ IDE_TYPE_SYMBOL_TREE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/plugins/symbol-tree/gbp-symbol-popover.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpSymbolPopover, list_view);
+ gtk_widget_class_bind_template_child (widget_class, GbpSymbolPopover, search_entry);
+ gtk_widget_class_bind_template_callback (widget_class, gbp_symbol_popover_activate_cb);
+ gtk_widget_class_bind_template_callback (widget_class, gbp_symbol_popover_search_activate_cb);
+ gtk_widget_class_bind_template_callback (widget_class, gbp_symbol_popover_search_changed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_search_key_pressed_cb);
+}
+
+static void
+gbp_symbol_popover_init (GbpSymbolPopover *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget *
+gbp_symbol_popover_new (void)
+{
+ return g_object_new (GBP_TYPE_SYMBOL_POPOVER, NULL);
+}
+
+IdeSymbolTree *
+gbp_symbol_popover_get_symbol_tree (GbpSymbolPopover *self)
+{
+ g_return_val_if_fail (GBP_IS_SYMBOL_POPOVER (self), NULL);
+ g_return_val_if_fail (!self->symbol_tree ||
+ IDE_IS_SYMBOL_TREE (self->symbol_tree), NULL);
+
+ return self->symbol_tree;
+}
+
+static GListModel *
+get_child_model (gpointer item,
+ gpointer user_data)
+{
+ IdeSymbolNode *node = item;
+ IdeSymbolTree *tree = user_data;
+
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+ g_assert (IDE_IS_SYMBOL_TREE (tree));
+
+ if (ide_symbol_tree_get_n_children (tree, node) == 0)
+ return NULL;
+
+ return G_LIST_MODEL (gbp_symbol_list_model_new (tree, node));
+}
+
+static gboolean
+filter_node (IdeSymbolNode *node,
+ const char * const *search_needle)
+{
+ /* Show only if the name matches every needle */
+ for (guint i = 0; search_needle[i]; i++)
+ {
+ const char *name = ide_symbol_node_get_name (node);
+
+ if (!name)
+ return FALSE;
+
+ if (g_str_match_string (search_needle[i], name, TRUE))
+ continue;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+filter_by_name (gpointer item,
+ gpointer user_data)
+{
+ GbpSymbolPopover *self = user_data;
+ GtkTreeListRow *row = item;
+ GtkTreeListRow *parent;
+ IdeSymbolNode *node;
+ GListModel *children;
+ guint i, n;
+
+ g_assert (GTK_IS_TREE_LIST_ROW (row));
+ g_assert (GBP_IS_SYMBOL_POPOVER (self));
+
+ /* Show all items if search is empty */
+ if (!self->search_needle || !self->search_needle[0] || !*self->search_needle[0])
+ return TRUE;
+
+ /* Show a row if itself or any parent matches */
+ for (parent = row; parent; parent = gtk_tree_list_row_get_parent (parent))
+ {
+ node = gtk_tree_list_row_get_item (parent);
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+
+ if (filter_node (node, (const char * const *)self->search_needle))
+ return TRUE;
+ }
+
+ /* Show a row if any child matches */
+ if ((children = gtk_tree_list_row_get_children (row)))
+ {
+ n = g_list_model_get_n_items (children);
+ for (i = 0; i < n; i++)
+ {
+ gboolean ret;
+
+ node = g_list_model_get_item (children, i);
+ g_assert (IDE_IS_SYMBOL_NODE (node));
+
+ ret = filter_node (node, (const char * const *)self->search_needle);
+ g_object_unref (node);
+ if (ret)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void
+gbp_symbol_popover_set_symbol_tree (GbpSymbolPopover *self,
+ IdeSymbolTree *symbol_tree)
+{
+ g_return_if_fail (GBP_IS_SYMBOL_POPOVER (self));
+ g_return_if_fail (!symbol_tree || IDE_IS_SYMBOL_TREE (symbol_tree));
+
+ if (g_set_object (&self->symbol_tree, symbol_tree))
+ {
+ gtk_list_view_set_model (self->list_view, NULL);
+
+ if (symbol_tree != NULL)
+ {
+ GbpSymbolListModel *model;
+ GtkTreeListModel *tree_model;
+ GtkFilterListModel *filter_model;
+ GtkSingleSelection *selection;
+ GtkFilter *filter;
+
+ model = gbp_symbol_list_model_new (symbol_tree, NULL);
+ tree_model = gtk_tree_list_model_new (G_LIST_MODEL (model),
+ FALSE,
+ TRUE,
+ get_child_model,
+ g_object_ref (symbol_tree),
+ g_object_unref);
+
+ filter_model = gtk_filter_list_model_new (G_LIST_MODEL (tree_model), NULL);
+ filter = GTK_FILTER (gtk_custom_filter_new (filter_by_name, self, NULL));
+ gtk_filter_list_model_set_filter (filter_model, filter);
+ g_set_object (&self->filter, filter);
+ g_object_unref (filter);
+
+ selection = gtk_single_selection_new (G_LIST_MODEL (filter_model));
+ gtk_list_view_set_model (self->list_view, GTK_SELECTION_MODEL (selection));
+ g_object_unref (selection);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SYMBOL_TREE]);
+ }
+}
diff --git a/src/plugins/symbol-tree/gbp-symbol-menu-button.h b/src/plugins/symbol-tree/gbp-symbol-popover.h
similarity index 51%
rename from src/plugins/symbol-tree/gbp-symbol-menu-button.h
rename to src/plugins/symbol-tree/gbp-symbol-popover.h
index ebc5503f9..3ccb30074 100644
--- a/src/plugins/symbol-tree/gbp-symbol-menu-button.h
+++ b/src/plugins/symbol-tree/gbp-symbol-popover.h
@@ -1,6 +1,6 @@
-/* gbp-symbol-menu-button.h
+/* gbp-symbol-popover.h
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,18 +20,19 @@
#pragma once
-#include <libide-gui.h>
+#include <gtk/gtk.h>
+
+#include <libide-code.h>
G_BEGIN_DECLS
-#define GBP_TYPE_SYMBOL_MENU_BUTTON (gbp_symbol_menu_button_get_type())
+#define GBP_TYPE_SYMBOL_POPOVER (gbp_symbol_popover_get_type())
-G_DECLARE_FINAL_TYPE (GbpSymbolMenuButton, gbp_symbol_menu_button, GBP, SYMBOL_MENU_BUTTON, GtkMenuButton)
+G_DECLARE_FINAL_TYPE (GbpSymbolPopover, gbp_symbol_popover, GBP, SYMBOL_POPOVER, GtkPopover)
-void gbp_symbol_menu_button_set_symbol (GbpSymbolMenuButton *self,
- IdeSymbol *symbol);
-IdeSymbolTree *gbp_symbol_menu_button_get_symbol_tree (GbpSymbolMenuButton *self);
-void gbp_symbol_menu_button_set_symbol_tree (GbpSymbolMenuButton *self,
- IdeSymbolTree *symbol_tree);
+GtkWidget *gbp_symbol_popover_new (void);
+IdeSymbolTree *gbp_symbol_popover_get_symbol_tree (GbpSymbolPopover *self);
+void gbp_symbol_popover_set_symbol_tree (GbpSymbolPopover *self,
+ IdeSymbolTree *symbol_tree);
G_END_DECLS
diff --git a/src/plugins/symbol-tree/gbp-symbol-popover.ui b/src/plugins/symbol-tree/gbp-symbol-popover.ui
new file mode 100644
index 000000000..c99333589
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-popover.ui
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GbpSymbolPopover" parent="GtkPopover">
+ <property name="width-request">400</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame">
+ <child>
+ <object class="GtkScrolledWindow" id="scroller">
+ <property name="propagate-natural-height">true</property>
+ <property name="propagate-natural-width">true</property>
+ <property name="min-content-height">100</property>
+ <property name="max-content-height">600</property>
+ <property name="min-content-width">400</property>
+ <property name="max-content-width">400</property>
+ <child>
+ <object class="GtkListView" id="list_view">
+ <signal name="activate" handler="gbp_symbol_popover_activate_cb" swapped="true"
object="GbpSymbolPopover"/>
+ <property name="orientation">vertical</property>
+ <property name="single-click-activate">True</property>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="resource">/plugins/symbol-tree/gbp-symbol-popover-row.ui</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="placeholder-text" translatable="yes">Filter Symbols…</property>
+ <signal name="activate" handler="gbp_symbol_popover_search_activate_cb" swapped="true"
object="GbpSymbolPopover"/>
+ <signal name="search-changed" handler="gbp_symbol_popover_search_changed_cb" swapped="true"
object="GbpSymbolPopover"/>
+ <child>
+ <object class="GtkEventControllerKey">
+ <property name="propagation-phase">capture</property>
+ <signal name="key-pressed" handler="on_search_key_pressed_cb" swapped="true"
object="GbpSymbolPopover"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/plugins/symbol-tree/gbp-symbol-util.c b/src/plugins/symbol-tree/gbp-symbol-util.c
new file mode 100644
index 000000000..f82a752dd
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-util.c
@@ -0,0 +1,324 @@
+/* gbp-symbol-util.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-symbol-util"
+
+#include "config.h"
+
+#include <libide-threading.h>
+
+#include "gbp-symbol-util.h"
+
+typedef struct
+{
+ GPtrArray *resolvers;
+ IdeBuffer *buffer;
+ IdeLocation *location;
+} FindNearestScope;
+
+typedef struct
+{
+ GPtrArray *resolvers;
+ IdeBuffer *buffer;
+ GFile *file;
+ GBytes *contents;
+} GetSymbolTree;
+
+static void
+get_symbol_tree_free (GetSymbolTree *data)
+{
+ g_assert (data != NULL);
+ g_assert (data->resolvers != NULL);
+ g_assert (data->buffer != NULL);
+ g_assert (IDE_IS_BUFFER (data->buffer));
+ g_assert (G_IS_FILE (data->file));
+ g_assert (data->contents != NULL);
+
+ g_clear_pointer (&data->resolvers, g_ptr_array_unref);
+ g_clear_pointer (&data->buffer, ide_buffer_release);
+ g_clear_pointer (&data->contents, g_bytes_unref);
+ g_clear_object (&data->file);
+ g_slice_free (GetSymbolTree, data);
+}
+
+static void
+find_nearest_scope_free (FindNearestScope *data)
+{
+ g_assert (data != NULL);
+ g_assert (data->resolvers != NULL);
+ g_assert (data->buffer != NULL);
+ g_assert (IDE_IS_BUFFER (data->buffer));
+ g_assert (IDE_IS_LOCATION (data->location));
+
+ g_clear_pointer (&data->resolvers, g_ptr_array_unref);
+ g_clear_pointer (&data->buffer, ide_buffer_release);
+ g_clear_object (&data->location);
+ g_slice_free (FindNearestScope, data);
+}
+
+static void
+gbp_symbol_find_nearest_scope_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSymbolResolver *resolver = (IdeSymbolResolver *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(IdeSymbol) symbol = NULL;
+ g_autoptr(GError) error = NULL;
+ FindNearestScope *data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SYMBOL_RESOLVER (resolver));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if ((symbol = ide_symbol_resolver_find_nearest_scope_finish (resolver, result, &error)))
+ {
+ ide_task_return_object (task, g_steal_pointer (&symbol));
+ IDE_EXIT;
+ }
+
+ data = ide_task_get_task_data (task);
+
+ g_assert (data != NULL);
+ g_assert (data->resolvers != NULL);
+ g_assert (data->resolvers->len > 0);
+ g_assert (IDE_IS_LOCATION (data->location));
+ g_assert (IDE_IS_BUFFER (data->buffer));
+
+ g_ptr_array_remove_index (data->resolvers,
+ data->resolvers->len - 1);
+
+ if (data->resolvers->len == 0)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "No resolvers could locate the nearest scope");
+ IDE_EXIT;
+ }
+
+ ide_symbol_resolver_find_nearest_scope_async (g_ptr_array_index (data->resolvers, data->resolvers->len -
1),
+ data->location,
+ ide_task_get_cancellable (task),
+ gbp_symbol_find_nearest_scope_cb,
+ g_object_ref (task));
+
+ IDE_EXIT;
+}
+
+void
+gbp_symbol_find_nearest_scope_async (IdeBuffer *buffer,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GPtrArray) resolvers = NULL;
+ FindNearestScope *data;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_BUFFER (buffer));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (buffer, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, gbp_symbol_find_nearest_scope_async);
+
+ resolvers = ide_buffer_get_symbol_resolvers (buffer);
+ IDE_PTR_ARRAY_SET_FREE_FUNC (resolvers, g_object_unref);
+
+ if (resolvers == NULL || resolvers->len == 0)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "No symbol resolvers available");
+ IDE_EXIT;
+ }
+
+ data = g_slice_new0 (FindNearestScope);
+ data->resolvers = g_steal_pointer (&resolvers);
+ data->buffer = ide_buffer_hold (buffer);
+ data->location = ide_buffer_get_insert_location (buffer);
+ ide_task_set_task_data (task, data, find_nearest_scope_free);
+
+ ide_symbol_resolver_find_nearest_scope_async (g_ptr_array_index (data->resolvers, data->resolvers->len -
1),
+ data->location,
+ cancellable,
+ gbp_symbol_find_nearest_scope_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+IdeSymbol *
+gbp_symbol_find_nearest_scope_finish (IdeBuffer *buffer,
+ GAsyncResult *result,
+ GError **error)
+{
+ IdeSymbol *ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_BUFFER (buffer), NULL);
+ g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+
+ ret = ide_task_propagate_object (IDE_TASK (result), error);
+
+ g_return_val_if_fail (!ret || IDE_IS_SYMBOL (ret), NULL);
+
+ IDE_RETURN (ret);
+}
+
+static void
+gbp_symbol_get_symbol_tree_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSymbolResolver *resolver = (IdeSymbolResolver *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(IdeSymbolTree) tree = NULL;
+ g_autoptr(GError) error = NULL;
+ GetSymbolTree *data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SYMBOL_RESOLVER (resolver));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if ((tree = ide_symbol_resolver_get_symbol_tree_finish (resolver, result, &error)))
+ {
+ ide_task_return_object (task, g_steal_pointer (&tree));
+ IDE_EXIT;
+ }
+
+ data = ide_task_get_task_data (task);
+
+ g_assert (data != NULL);
+ g_assert (data->resolvers != NULL);
+ g_assert (data->resolvers->len > 0);
+ g_assert (IDE_IS_BUFFER (data->buffer));
+ g_assert (G_IS_FILE (data->file));
+ g_assert (data->contents != NULL);
+
+ g_ptr_array_remove_index (data->resolvers,
+ data->resolvers->len - 1);
+
+ if (data->resolvers->len == 0)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "No resolvers could create a symbol tree");
+ IDE_EXIT;
+ }
+
+ ide_symbol_resolver_get_symbol_tree_async (g_ptr_array_index (data->resolvers, data->resolvers->len - 1),
+ data->file,
+ data->contents,
+ ide_task_get_cancellable (task),
+ gbp_symbol_get_symbol_tree_cb,
+ g_object_ref (task));
+
+ IDE_EXIT;
+}
+
+void
+gbp_symbol_get_symbol_tree_async (IdeBuffer *buffer,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GPtrArray) resolvers = NULL;
+ g_autoptr(GBytes) contents = NULL;
+ GetSymbolTree *data;
+ GFile *file;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_BUFFER (buffer));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (buffer, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, gbp_symbol_get_symbol_tree_async);
+
+ file = ide_buffer_get_file (buffer);
+ contents = ide_buffer_dup_content (buffer);
+
+ if (file == NULL || contents == NULL)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Symbol resolvers require both a file and buffer contents");
+ IDE_EXIT;
+ }
+
+ resolvers = ide_buffer_get_symbol_resolvers (buffer);
+ IDE_PTR_ARRAY_SET_FREE_FUNC (resolvers, g_object_unref);
+
+ if (resolvers == NULL || resolvers->len == 0)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "No symbol resolvers available");
+ IDE_EXIT;
+ }
+
+ data = g_slice_new0 (GetSymbolTree);
+ data->resolvers = g_steal_pointer (&resolvers);
+ data->buffer = ide_buffer_hold (buffer);
+ data->file = g_object_ref (file);
+ data->contents = g_steal_pointer (&contents);
+ ide_task_set_task_data (task, data, get_symbol_tree_free);
+
+ ide_symbol_resolver_get_symbol_tree_async (g_ptr_array_index (data->resolvers, data->resolvers->len - 1),
+ data->file,
+ data->contents,
+ cancellable,
+ gbp_symbol_get_symbol_tree_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+IdeSymbolTree *
+gbp_symbol_get_symbol_tree_finish (IdeBuffer *buffer,
+ GAsyncResult *result,
+ GError **error)
+{
+ IdeSymbolTree *ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_BUFFER (buffer), NULL);
+ g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+
+ ret = ide_task_propagate_object (IDE_TASK (result), error);
+
+ g_return_val_if_fail (!ret || IDE_IS_SYMBOL_TREE (ret), NULL);
+
+ IDE_RETURN (ret);
+}
diff --git a/src/plugins/symbol-tree/gbp-symbol-util.h b/src/plugins/symbol-tree/gbp-symbol-util.h
new file mode 100644
index 000000000..ae1a35f04
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-util.h
@@ -0,0 +1,42 @@
+/* gbp-symbol-util.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-code.h>
+
+G_BEGIN_DECLS
+
+void gbp_symbol_find_nearest_scope_async (IdeBuffer *buffer,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IdeSymbol *gbp_symbol_find_nearest_scope_finish (IdeBuffer *buffer,
+ GAsyncResult *result,
+ GError **error);
+void gbp_symbol_get_symbol_tree_async (IdeBuffer *buffer,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IdeSymbolTree *gbp_symbol_get_symbol_tree_finish (IdeBuffer *buffer,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/plugins/symbol-tree/gbp-symbol-workspace-addin.c
b/src/plugins/symbol-tree/gbp-symbol-workspace-addin.c
new file mode 100644
index 000000000..2737b2bb2
--- /dev/null
+++ b/src/plugins/symbol-tree/gbp-symbol-workspace-addin.c
@@ -0,0 +1,473 @@
+/* gbp-symbol-workspace-addin.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-symbol-workspace-addin"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <libide-code.h>
+#include <libide-editor.h>
+#include <libide-gui.h>
+
+#include "gbp-symbol-popover.h"
+#include "gbp-symbol-workspace-addin.h"
+#include "gbp-symbol-util.h"
+
+#define NEAREST_SCOPE_DELAY_MSEC 500
+#define SYMBOL_TREE_DELAY_MSEC 1000
+
+struct _GbpSymbolWorkspaceAddin
+{
+ GObject parent_instance;
+
+ IdeWorkspace *workspace;
+ PanelStatusbar *statusbar;
+
+ GtkMenuButton *menu_button;
+ GtkLabel *menu_label;
+ GtkImage *menu_image;
+ GbpSymbolPopover *popover;
+
+ IdeSignalGroup *buffer_signals;
+ guint nearest_scope_timeout_source;
+ guint symbol_tree_timeout_source;
+};
+
+static void
+gbp_symbol_workspace_addin_set_symbol (GbpSymbolWorkspaceAddin *self,
+ IdeSymbol *symbol)
+{
+ const char *label = NULL;
+ const char *icon_name = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (!symbol || IDE_IS_SYMBOL (symbol));
+
+ if (symbol != NULL)
+ {
+ icon_name = ide_symbol_kind_get_icon_name (ide_symbol_get_kind (symbol));
+ label = ide_symbol_get_name (symbol);
+
+ if (ide_str_empty0 (label))
+ label = NULL;
+ }
+
+ if (label == NULL)
+ {
+ gtk_label_set_label (self->menu_label, _("Select Symbol…"));
+ gtk_image_set_from_icon_name (self->menu_image, NULL);
+ gtk_widget_hide (GTK_WIDGET (self->menu_image));
+ IDE_EXIT;
+ }
+
+ gtk_label_set_label (self->menu_label, label);
+ gtk_image_set_from_icon_name (self->menu_image, icon_name);
+ gtk_widget_set_visible (GTK_WIDGET (self->menu_image), icon_name != NULL);
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_workspace_addin_find_nearest_scope_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeBuffer *buffer = (IdeBuffer *)object;
+ g_autoptr(GbpSymbolWorkspaceAddin) self = user_data;
+ g_autoptr(IdeSymbol) symbol = NULL;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_BUFFER (buffer));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+
+ if (!(symbol = gbp_symbol_find_nearest_scope_finish (buffer, result, &error)))
+ {
+ if (!ide_error_ignore (error))
+ g_warning ("Failed to get symbol at location: %s", error->message);
+ IDE_GOTO (failure);
+ }
+
+ if ((gpointer)buffer != ide_signal_group_get_target (self->buffer_signals))
+ IDE_GOTO (failure);
+
+ gbp_symbol_workspace_addin_set_symbol (self, symbol);
+ gtk_widget_show (GTK_WIDGET (self->menu_button));
+
+ IDE_EXIT;
+
+failure:
+
+ /* Raced against another query or cleanup and lost, just bail */
+ if (ide_signal_group_get_target (self->buffer_signals) != buffer)
+ IDE_EXIT;
+
+ gbp_symbol_workspace_addin_set_symbol (self, NULL);
+ gtk_widget_show (GTK_WIDGET (self->menu_button));
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_workspace_addin_get_symbol_tree_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeBuffer *buffer = (IdeBuffer *)object;
+ g_autoptr(GbpSymbolWorkspaceAddin) self = user_data;
+ g_autoptr(IdeSymbolTree) tree = NULL;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_BUFFER (buffer));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+
+ if (!(tree = gbp_symbol_get_symbol_tree_finish (buffer, result, &error)))
+ {
+ if (!ide_error_ignore (error))
+ g_warning ("Failed to get symbol tree: %s", error->message);
+ IDE_GOTO (failure);
+ }
+
+ if ((gpointer)buffer != ide_signal_group_get_target (self->buffer_signals))
+ IDE_GOTO (failure);
+
+ gbp_symbol_popover_set_symbol_tree (self->popover, tree);
+
+ IDE_EXIT;
+
+failure:
+
+ /* Raced against another query and lost, just bail */
+ if ((gpointer)buffer != ide_signal_group_get_target (self->buffer_signals))
+ IDE_EXIT;
+
+ gbp_symbol_popover_set_symbol_tree (self->popover, NULL);
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_workspace_addin_update_nearest_scope (GbpSymbolWorkspaceAddin *self,
+ IdeBuffer *buffer)
+{
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ if (!ide_buffer_has_symbol_resolvers (buffer))
+ {
+ gbp_symbol_workspace_addin_set_symbol (self, NULL);
+ gtk_widget_hide (GTK_WIDGET (self->menu_button));
+ IDE_EXIT;
+ }
+
+ gbp_symbol_find_nearest_scope_async (buffer,
+ NULL,
+ gbp_symbol_workspace_addin_find_nearest_scope_cb,
+ g_object_ref (self));
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_workspace_addin_update_symbol_tree (GbpSymbolWorkspaceAddin *self,
+ IdeBuffer *buffer)
+{
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ if (!ide_buffer_has_symbol_resolvers (buffer))
+ {
+ gbp_symbol_popover_set_symbol_tree (self->popover, NULL);
+ IDE_EXIT;
+ }
+
+ gbp_symbol_get_symbol_tree_async (buffer,
+ NULL,
+ gbp_symbol_workspace_addin_get_symbol_tree_cb,
+ g_object_ref (self));
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_workspace_addin_buffer_bind_cb (GbpSymbolWorkspaceAddin *self,
+ IdeBuffer *buffer,
+ IdeSignalGroup *signal_group)
+{
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+ g_assert (IDE_IS_SIGNAL_GROUP (signal_group));
+
+ gbp_symbol_workspace_addin_update_nearest_scope (self, buffer);
+ gbp_symbol_workspace_addin_update_symbol_tree (self, buffer);
+}
+
+static gboolean
+gbp_symbol_workspace_addin_nearest_scope_timeout (gpointer data)
+{
+ GbpSymbolWorkspaceAddin *self = data;
+ IdeBuffer *buffer;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+
+ self->nearest_scope_timeout_source = 0;
+
+ if ((buffer = ide_signal_group_get_target (self->buffer_signals)))
+ gbp_symbol_workspace_addin_update_nearest_scope (self, buffer);
+ else
+ gtk_widget_hide (GTK_WIDGET (self->menu_button));
+
+ IDE_RETURN (G_SOURCE_REMOVE);
+}
+
+static void
+gbp_symbol_workspace_addin_buffer_cursor_moved_cb (GbpSymbolWorkspaceAddin *self,
+ IdeBuffer *buffer)
+{
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ if (self->nearest_scope_timeout_source == 0)
+ self->nearest_scope_timeout_source = g_timeout_add (NEAREST_SCOPE_DELAY_MSEC,
+ gbp_symbol_workspace_addin_nearest_scope_timeout,
+ self);
+}
+
+static gboolean
+gbp_symbol_workspace_addin_symbol_tree_timeout (gpointer data)
+{
+ GbpSymbolWorkspaceAddin *self = data;
+ IdeBuffer *buffer;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+
+ self->symbol_tree_timeout_source = 0;
+
+ if ((buffer = ide_signal_group_get_target (self->buffer_signals)))
+ gbp_symbol_workspace_addin_update_symbol_tree (self, buffer);
+ else
+ gbp_symbol_popover_set_symbol_tree (self->popover, NULL);
+
+ IDE_RETURN (G_SOURCE_REMOVE);
+}
+
+static void
+gbp_symbol_workspace_addin_buffer_changed_cb (GbpSymbolWorkspaceAddin *self,
+ IdeBuffer *buffer)
+{
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ g_clear_handle_id (&self->symbol_tree_timeout_source, g_source_remove);
+ self->symbol_tree_timeout_source = g_timeout_add (SYMBOL_TREE_DELAY_MSEC,
+ gbp_symbol_workspace_addin_symbol_tree_timeout,
+ self);
+}
+
+static void
+focus_symbol_tree (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ GbpSymbolWorkspaceAddin *self = user_data;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+
+ if (gtk_widget_get_visible (GTK_WIDGET (self->menu_button)))
+ {
+ gtk_menu_button_popup (self->menu_button);
+ gtk_widget_grab_focus (GTK_WIDGET (self->popover));
+ }
+}
+
+static const GActionEntry actions[] = {
+ { "focus-symbol-tree", focus_symbol_tree },
+};
+
+static void
+gbp_symbol_workspace_addin_load (IdeWorkspaceAddin *addin,
+ IdeWorkspace *workspace)
+{
+ GbpSymbolWorkspaceAddin *self = (GbpSymbolWorkspaceAddin *)addin;
+ GtkBox *box;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_WORKSPACE (workspace));
+
+ self->workspace = workspace;
+ self->statusbar = ide_workspace_get_statusbar (workspace);
+
+ g_action_map_add_action_entries (G_ACTION_MAP (workspace),
+ actions,
+ G_N_ELEMENTS (actions),
+ self);
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "spacing", 6,
+ NULL);
+ self->menu_image = g_object_new (GTK_TYPE_IMAGE,
+ "icon-name", "lang-function-symbolic",
+ NULL);
+ gtk_box_append (box, GTK_WIDGET (self->menu_image));
+ self->menu_label = g_object_new (GTK_TYPE_LABEL,
+ "label", _("Select Symbol…"),
+ "xalign", 0.0f,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+ gtk_box_append (box, GTK_WIDGET (self->menu_label));
+ self->popover = GBP_SYMBOL_POPOVER (gbp_symbol_popover_new ());
+ self->menu_button = g_object_new (GTK_TYPE_MENU_BUTTON,
+ "child", box,
+ "direction", GTK_ARROW_UP,
+ "popover", self->popover,
+ "visible", FALSE,
+ NULL);
+
+ panel_statusbar_add_suffix (self->statusbar, 20000, GTK_WIDGET (self->menu_button));
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_workspace_addin_unload (IdeWorkspaceAddin *addin,
+ IdeWorkspace *workspace)
+{
+ GbpSymbolWorkspaceAddin *self = (GbpSymbolWorkspaceAddin *)addin;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ g_assert (PANEL_IS_STATUSBAR (self->statusbar));
+ g_assert (workspace == self->workspace);
+
+ for (guint i = 0; i < G_N_ELEMENTS (actions); i++)
+ g_action_map_remove_action (G_ACTION_MAP (workspace), actions[i].name);
+
+ ide_signal_group_set_target (self->buffer_signals, NULL);
+
+ g_clear_handle_id (&self->nearest_scope_timeout_source, g_source_remove);
+ g_clear_handle_id (&self->symbol_tree_timeout_source, g_source_remove);
+
+ panel_statusbar_remove (self->statusbar, GTK_WIDGET (self->menu_button));
+
+ self->menu_button = NULL;
+ self->menu_label = NULL;
+ self->menu_image = NULL;
+ self->workspace = NULL;
+ self->statusbar = NULL;
+
+ IDE_EXIT;
+}
+
+static void
+gbp_symbol_workspace_addin_page_changed (IdeWorkspaceAddin *addin,
+ IdePage *page)
+{
+ GbpSymbolWorkspaceAddin *self = (GbpSymbolWorkspaceAddin *)addin;
+ IdeBuffer *buffer = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SYMBOL_WORKSPACE_ADDIN (self));
+ g_assert (!page || IDE_IS_PAGE (page));
+
+ gbp_symbol_workspace_addin_set_symbol (self, NULL);
+ gtk_widget_hide (GTK_WIDGET (self->menu_button));
+
+ if (IDE_IS_EDITOR_PAGE (page))
+ buffer = ide_editor_page_get_buffer (IDE_EDITOR_PAGE (page));
+
+ ide_signal_group_set_target (self->buffer_signals, buffer);
+
+ IDE_EXIT;
+}
+
+static void
+workspace_addin_iface_init (IdeWorkspaceAddinInterface *iface)
+{
+ iface->load = gbp_symbol_workspace_addin_load;
+ iface->unload = gbp_symbol_workspace_addin_unload;
+ iface->page_changed = gbp_symbol_workspace_addin_page_changed;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpSymbolWorkspaceAddin, gbp_symbol_workspace_addin, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKSPACE_ADDIN, workspace_addin_iface_init))
+
+static void
+gbp_symbol_workspace_addin_finalize (GObject *object)
+{
+ GbpSymbolWorkspaceAddin *self = (GbpSymbolWorkspaceAddin *)object;
+
+ g_clear_object (&self->buffer_signals);
+
+ G_OBJECT_CLASS (gbp_symbol_workspace_addin_parent_class)->finalize (object);
+}
+
+static void
+gbp_symbol_workspace_addin_class_init (GbpSymbolWorkspaceAddinClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gbp_symbol_workspace_addin_finalize;
+}
+
+static void
+gbp_symbol_workspace_addin_init (GbpSymbolWorkspaceAddin *self)
+{
+ self->buffer_signals = ide_signal_group_new (IDE_TYPE_BUFFER);
+ g_signal_connect_object (self->buffer_signals,
+ "bind",
+ G_CALLBACK (gbp_symbol_workspace_addin_buffer_bind_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (self->buffer_signals,
+ "cursor-moved",
+ G_CALLBACK (gbp_symbol_workspace_addin_buffer_cursor_moved_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (self->buffer_signals,
+ "changed",
+ G_CALLBACK (gbp_symbol_workspace_addin_buffer_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+}
diff --git a/src/plugins/symbol-tree/gbp-symbol-frame-addin.h
b/src/plugins/symbol-tree/gbp-symbol-workspace-addin.h
similarity index 70%
rename from src/plugins/symbol-tree/gbp-symbol-frame-addin.h
rename to src/plugins/symbol-tree/gbp-symbol-workspace-addin.h
index db160ac53..e0cd9e580 100644
--- a/src/plugins/symbol-tree/gbp-symbol-frame-addin.h
+++ b/src/plugins/symbol-tree/gbp-symbol-workspace-addin.h
@@ -1,6 +1,6 @@
-/* gbp-symbol-frame-addin.h
+/* gbp-symbol-workspace-addin.h
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,12 +20,12 @@
#pragma once
-#include <libide-gui.h>
+#include <glib-object.h>
G_BEGIN_DECLS
-#define GBP_TYPE_SYMBOL_FRAME_ADDIN (gbp_symbol_frame_addin_get_type())
+#define GBP_TYPE_SYMBOL_WORKSPACE_ADDIN (gbp_symbol_workspace_addin_get_type())
-G_DECLARE_FINAL_TYPE (GbpSymbolFrameAddin, gbp_symbol_frame_addin, GBP, SYMBOL_FRAME_ADDIN, GObject)
+G_DECLARE_FINAL_TYPE (GbpSymbolWorkspaceAddin, gbp_symbol_workspace_addin, GBP, SYMBOL_WORKSPACE_ADDIN,
GObject)
G_END_DECLS
diff --git a/src/plugins/symbol-tree/gtk/keybindings.json b/src/plugins/symbol-tree/gtk/keybindings.json
new file mode 100644
index 000000000..00758dae5
--- /dev/null
+++ b/src/plugins/symbol-tree/gtk/keybindings.json
@@ -0,0 +1 @@
+{ "trigger" : "<Control><Shift>k", "action" : "win.focus-symbol-tree", "when" : "canEdit()", "phase" :
"capture" },
diff --git a/src/plugins/symbol-tree/meson.build b/src/plugins/symbol-tree/meson.build
index 1eeb61f54..94f64dc50 100644
--- a/src/plugins/symbol-tree/meson.build
+++ b/src/plugins/symbol-tree/meson.build
@@ -1,8 +1,9 @@
plugins_sources += files([
'gbp-symbol-hover-provider.c',
- 'gbp-symbol-frame-addin.c',
- 'gbp-symbol-menu-button.c',
- 'gbp-symbol-tree-builder.c',
+ 'gbp-symbol-list-model.c',
+ 'gbp-symbol-popover.c',
+ 'gbp-symbol-util.c',
+ 'gbp-symbol-workspace-addin.c',
'symbol-tree-plugin.c',
])
diff --git a/src/plugins/symbol-tree/symbol-tree-plugin.c b/src/plugins/symbol-tree/symbol-tree-plugin.c
index 0408a034e..b641650a3 100644
--- a/src/plugins/symbol-tree/symbol-tree-plugin.c
+++ b/src/plugins/symbol-tree/symbol-tree-plugin.c
@@ -1,6 +1,6 @@
/* symbol-tree-plugin.c
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2017-2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,19 +21,20 @@
#include "config.h"
#include <libpeas/peas.h>
-#include <libide-sourceview.h>
+
#include <libide-gui.h>
+#include <libide-sourceview.h>
-#include "gbp-symbol-frame-addin.h"
#include "gbp-symbol-hover-provider.h"
+#include "gbp-symbol-workspace-addin.h"
_IDE_EXTERN void
_gbp_symbol_tree_register_types (PeasObjectModule *module)
{
peas_object_module_register_extension_type (module,
- IDE_TYPE_FRAME_ADDIN,
- GBP_TYPE_SYMBOL_FRAME_ADDIN);
- peas_object_module_register_extension_type (module,
- IDE_TYPE_HOVER_PROVIDER,
+ GTK_SOURCE_TYPE_HOVER_PROVIDER,
GBP_TYPE_SYMBOL_HOVER_PROVIDER);
+ peas_object_module_register_extension_type (module,
+ IDE_TYPE_WORKSPACE_ADDIN,
+ GBP_TYPE_SYMBOL_WORKSPACE_ADDIN);
}
diff --git a/src/plugins/symbol-tree/symbol-tree.gresource.xml
b/src/plugins/symbol-tree/symbol-tree.gresource.xml
index 44bbb417c..63075fa12 100644
--- a/src/plugins/symbol-tree/symbol-tree.gresource.xml
+++ b/src/plugins/symbol-tree/symbol-tree.gresource.xml
@@ -2,7 +2,8 @@
<gresources>
<gresource prefix="/plugins/symbol-tree">
<file>symbol-tree.plugin</file>
- <file>gbp-symbol-menu-button.ui</file>
- <file>themes/shared.css</file>
+ <file>gtk/keybindings.json</file>
+ <file preprocess="xml-stripblanks">gbp-symbol-popover.ui</file>
+ <file preprocess="xml-stripblanks">gbp-symbol-popover-row.ui</file>
</gresource>
</gresources>
diff --git a/src/plugins/symbol-tree/symbol-tree.plugin b/src/plugins/symbol-tree/symbol-tree.plugin
index cc49c678d..923746413 100644
--- a/src/plugins/symbol-tree/symbol-tree.plugin
+++ b/src/plugins/symbol-tree/symbol-tree.plugin
@@ -1,10 +1,10 @@
[Plugin]
Authors=Christian Hergert <christian hergert me>
Builtin=true
-Copyright=Copyright © 2015-2018 Christian Hergert
-Depends=editor;
+Copyright=Copyright © 2015-2022 Christian Hergert
Description=Provides a Symbol Tree for the currently focused document
Embedded=_gbp_symbol_tree_register_types
-Hidden=true
Module=symbol-tree
Name=Symbol Tree
+X-Category=editing
+X-Workspace-Kind=primary;editor;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]