[gnome-builder] editor: add rudimentary symbol browser



commit 4df927becbc19d84dfbe371bd9d0cf6208d9fb43
Author: Christian Hergert <christian hergert me>
Date:   Sun Mar 29 01:02:22 2015 -0700

    editor: add rudimentary symbol browser
    
    If the IdeLanguage provides an IdeSymbolResolver, allow jumping to a
    symbol within the file directly from the drop down.
    
    We have a lot to do here, to make this feel nice, but this is the general
    plumbing required. We should add icons for symbol type (class, enum,
    struct, func, etc) as well as deal with keyboard navigation better.

 data/ui/gb-editor-view.ui           |   43 ++++++++
 src/editor/gb-editor-view-private.h |    7 ++
 src/editor/gb-editor-view.c         |  195 +++++++++++++++++++++++++++++++++++
 3 files changed, 245 insertions(+), 0 deletions(-)
---
diff --git a/data/ui/gb-editor-view.ui b/data/ui/gb-editor-view.ui
index eee0105..80fe0b3 100644
--- a/data/ui/gb-editor-view.ui
+++ b/data/ui/gb-editor-view.ui
@@ -88,6 +88,17 @@
         <child internal-child="controls">
           <object class="GtkBox">
             <child>
+              <object class="GtkMenuButton" id="symbols_button">
+                <property name="popover">symbols_popover</property>
+                <property name="visible">true</property>
+                <style>
+                  <class name="dim-label"/>
+                  <class name="text-button"/>
+                  <class name="flat"/>
+                </style>
+              </object>
+            </child>
+            <child>
               <object class="GtkMenuButton" id="tweak_button">
                 <property name="popover">popover</property>
                 <property name="visible">true</property>
@@ -111,4 +122,36 @@
       </object>
     </child>
   </object>
+  <object class="GtkPopover" id="symbols_popover">
+    <child>
+      <object class="GtkBox">
+        <property name="border-width">12</property>
+        <property name="orientation">vertical</property>
+        <property name="visible">true</property>
+        <style>
+          <class name="linked"/>
+        </style>
+        <child>
+          <object class="GtkSearchEntry" id="symbols_search_entry">
+            <property name="visible">true</property>
+          </object>
+        </child>
+        <child>
+          <object class="GbScrolledWindow">
+            <property name="max-content-height">500</property>
+            <property name="max-content-width">400</property>
+            <property name="min-content-width">200</property>
+            <property name="min-content-height">30</property>
+            <property name="shadow-type">in</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkListBox" id="symbols_listbox">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/src/editor/gb-editor-view-private.h b/src/editor/gb-editor-view-private.h
index b833636..3e2f2de 100644
--- a/src/editor/gb-editor-view-private.h
+++ b/src/editor/gb-editor-view-private.h
@@ -19,6 +19,8 @@
 #ifndef GB_EDITOR_VIEW_PRIVATE_H
 #define GB_EDITOR_VIEW_PRIVATE_H
 
+#include <ide.h>
+
 #include "gb-editor-document.h"
 #include "gb-editor-frame.h"
 #include "gb-editor-tweak-widget.h"
@@ -32,6 +34,7 @@ struct _GbEditorView
 
   GbEditorDocument    *document;
   GSettings           *settings;
+  IdePatternSpec      *symbol_spec;
 
   GbEditorFrame       *frame1;
   GbEditorFrame       *frame2;
@@ -39,8 +42,12 @@ struct _GbEditorView
   GtkRevealer         *modified_revealer;
   GtkPaned            *paned;
   GtkProgressBar      *progress_bar;
+  GtkListBox          *symbols_listbox;
+  GtkSearchEntry      *symbols_search_entry;
   GtkMenuButton       *tweak_button;
   GbEditorTweakWidget *tweak_widget;
+
+  guint                symbol_timeout;
 };
 
 G_END_DECLS
diff --git a/src/editor/gb-editor-view.c b/src/editor/gb-editor-view.c
index 0c7de22..a94cb90 100644
--- a/src/editor/gb-editor-view.c
+++ b/src/editor/gb-editor-view.c
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define G_LOG_DOMAIN "gb-editor-view"
+
 #include <glib/gi18n.h>
 
 #include "gb-editor-frame-private.h"
@@ -24,6 +26,8 @@
 #include "gb-editor-view-private.h"
 #include "gb-widget.h"
 
+#define SYMBOL_UPDATE_SECS 10
+
 G_DEFINE_TYPE (GbEditorView, gb_editor_view, GB_TYPE_VIEW)
 
 enum {
@@ -202,6 +206,106 @@ gb_editor_view__buffer_notify_title (GbEditorView *self,
 }
 
 static void
+gb_editor_view_update_symbols_cb (GObject      *object,
+                                  GAsyncResult *result,
+                                  gpointer      user_data)
+{
+  IdeBuffer *buffer = (IdeBuffer *)object;
+  g_autoptr(GbEditorView) self = user_data;
+  g_autoptr(GPtrArray) ret = NULL;
+  g_autoptr(GError) error = NULL;
+  GList *children;
+  GList *iter;
+  gsize i;
+
+  g_assert (IDE_IS_BUFFER (buffer));
+
+  ret = ide_buffer_get_symbols_finish (buffer, result, &error);
+
+  if (ret == NULL)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+        g_warning ("%s", error->message);
+      return;
+    }
+
+  children = gtk_container_get_children (GTK_CONTAINER (self->symbols_listbox));
+  for (iter = children; iter; iter = iter->next)
+    gtk_container_remove (GTK_CONTAINER (self->symbols_listbox), iter->data);
+  g_list_free (children);
+
+  for (i = 0; i < ret->len; i++)
+    {
+      const gchar *name;
+      IdeSymbol *symbol;
+      GtkListBoxRow *row;
+      GtkLabel *label;
+
+      symbol = g_ptr_array_index (ret, i);
+      name = ide_symbol_get_name (symbol);
+      row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+                          "visible", TRUE,
+                          NULL);
+      g_object_set_data_full (G_OBJECT (row),
+                              "IDE_SYMBOL",
+                              ide_symbol_ref (symbol),
+                              (GDestroyNotify)ide_symbol_unref);
+      label = g_object_new (GTK_TYPE_LABEL,
+                            "label", name,
+                            "visible", TRUE,
+                            "margin-top", 3,
+                            "margin-bottom", 3,
+                            "margin-start", 6,
+                            "margin-end", 6,
+                            "xalign", 0.0f,
+                            NULL);
+      gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (label));
+      gtk_container_add (GTK_CONTAINER (self->symbols_listbox), GTK_WIDGET (row));
+    }
+}
+
+static gboolean
+gb_editor_view_update_symbols_timeout (gpointer user_data)
+{
+  GbEditorView *self = user_data;
+
+  g_assert (GB_IS_EDITOR_VIEW (self));
+
+  self->symbol_timeout = 0;
+
+  ide_buffer_get_symbols_async (IDE_BUFFER (self->document),
+                                NULL,
+                                gb_editor_view_update_symbols_cb,
+                                g_object_ref (self));
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+gb_editor_view_queue_symbol_update (GbEditorView *self)
+{
+  g_assert (GB_IS_EDITOR_VIEW (self));
+
+  if (self->symbol_timeout == 0)
+    {
+      self->symbol_timeout =
+        g_timeout_add_seconds (SYMBOL_UPDATE_SECS,
+                               gb_editor_view_update_symbols_timeout,
+                               self);
+    }
+}
+
+static void
+gb_editor_view__buffer_changed (GbEditorView     *self,
+                                GbEditorDocument *document)
+{
+  g_assert (GB_IS_EDITOR_VIEW (self));
+  g_assert (GB_IS_EDITOR_DOCUMENT (document));
+
+  gb_editor_view_queue_symbol_update (self);
+}
+
+static void
 gb_editor_view_set_document (GbEditorView     *self,
                              GbEditorDocument *document)
 {
@@ -234,6 +338,12 @@ gb_editor_view_set_document (GbEditorView     *self,
                                G_CONNECT_SWAPPED);
 
       g_signal_connect_object (document,
+                               "changed",
+                               G_CALLBACK (gb_editor_view__buffer_changed),
+                               self,
+                               G_CONNECT_SWAPPED);
+
+      g_signal_connect_object (document,
                                "notify::title",
                                G_CALLBACK (gb_editor_view__buffer_notify_title),
                                self,
@@ -248,6 +358,7 @@ gb_editor_view_set_document (GbEditorView     *self,
       g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_DOCUMENT]);
 
       gb_editor_view_actions_update (self);
+      gb_editor_view_update_symbols_timeout (self);
     }
 }
 
@@ -339,12 +450,77 @@ gb_editor_view_hide_reload_bar (GbEditorView *self,
 }
 
 static void
+gb_editor_view__symbol_row_activated_cb (GbEditorView  *self,
+                                         GtkListBoxRow *row,
+                                         GtkListBox    *list_box)
+{
+  IdeSourceLocation *location;
+  IdeSymbol *symbol;
+
+  g_assert (GB_IS_EDITOR_VIEW (self));
+  g_assert (GTK_IS_LIST_BOX_ROW (row));
+  g_assert (GTK_IS_LIST_BOX (list_box));
+
+  symbol = g_object_get_data (G_OBJECT (row), "IDE_SYMBOL");
+  location = ide_symbol_get_canonical_location (symbol);
+
+  gb_editor_view_navigate_to (GB_VIEW (self), location);
+}
+
+static gboolean
+gb_editor_view_symbol_filter_func (GtkListBoxRow *row,
+                                   gpointer       user_data)
+{
+  GbEditorView *self = user_data;
+  IdeSymbol *symbol;
+  const gchar *name;
+
+  g_assert (GTK_IS_LIST_BOX_ROW (row));
+  g_assert (GB_IS_EDITOR_VIEW (self));
+
+  if (self->symbol_spec == NULL)
+    return TRUE;
+
+  symbol = g_object_get_data (G_OBJECT (row), "IDE_SYMBOL");
+  g_assert (symbol != NULL);
+
+  name = ide_symbol_get_name (symbol);
+
+  return ide_pattern_spec_match (self->symbol_spec, name);
+}
+
+static void
+gb_editor_view_invalidate_symbol_filter (GbEditorView *self,
+                                         GtkEntry     *entry)
+{
+  const gchar *text;
+
+  g_assert (GB_IS_EDITOR_VIEW (self));
+  g_assert (GTK_IS_ENTRY (entry));
+
+  g_clear_pointer (&self->symbol_spec, ide_pattern_spec_unref);
+
+  text = gtk_entry_get_text (entry);
+  if (text && text [0])
+    self->symbol_spec = ide_pattern_spec_new (text);
+
+  gtk_list_box_invalidate_filter (self->symbols_listbox);
+}
+
+static void
 gb_editor_view_finalize (GObject *object)
 {
   GbEditorView *self = (GbEditorView *)object;
 
+  if (self->symbol_timeout)
+    {
+      g_source_remove (self->symbol_timeout);
+      self->symbol_timeout = 0;
+    }
+
   g_clear_object (&self->document);
   g_clear_object (&self->settings);
+  g_clear_pointer (&self->symbol_spec, ide_pattern_spec_unref);
 
   G_OBJECT_CLASS (gb_editor_view_parent_class)->finalize (object);
 }
@@ -422,6 +598,8 @@ gb_editor_view_class_init (GbEditorViewClass *klass)
   GB_WIDGET_CLASS_BIND (klass, GbEditorView, modified_revealer);
   GB_WIDGET_CLASS_BIND (klass, GbEditorView, paned);
   GB_WIDGET_CLASS_BIND (klass, GbEditorView, progress_bar);
+  GB_WIDGET_CLASS_BIND (klass, GbEditorView, symbols_listbox);
+  GB_WIDGET_CLASS_BIND (klass, GbEditorView, symbols_search_entry);
   GB_WIDGET_CLASS_BIND (klass, GbEditorView, tweak_button);
   GB_WIDGET_CLASS_BIND (klass, GbEditorView, tweak_widget);
 
@@ -443,4 +621,21 @@ gb_editor_view_init (GbEditorView *self)
                            G_CALLBACK (gb_editor_view_hide_reload_bar),
                            self,
                            G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->symbols_listbox,
+                           "row-activated",
+                           G_CALLBACK (gb_editor_view__symbol_row_activated_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->symbols_search_entry,
+                           "changed",
+                           G_CALLBACK (gb_editor_view_invalidate_symbol_filter),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  gtk_list_box_set_filter_func (self->symbols_listbox,
+                                gb_editor_view_symbol_filter_func,
+                                self,
+                                NULL);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]