[gnome-builder/gnome-builder-43] plugins/codeui: add dialog to find references



commit a94267f09982cbf5bfd5be8b8f286ec1d17c541d
Author: Christian Hergert <chergert redhat com>
Date:   Tue Sep 20 22:56:17 2022 -0700

    plugins/codeui: add dialog to find references
    
    This was missing from the GTK 4 port of IdeSourceView still.

 po/POTFILES.in                                    |   2 +
 src/plugins/codeui/codeui.gresource.xml           |   1 +
 src/plugins/codeui/gbp-codeui-editor-page-addin.c | 131 ++++++++++++++++
 src/plugins/codeui/gbp-codeui-range-dialog.c      | 177 ++++++++++++++++++++++
 src/plugins/codeui/gbp-codeui-range-dialog.h      |  33 ++++
 src/plugins/codeui/gbp-codeui-range-dialog.ui     |  35 +++++
 src/plugins/codeui/gtk/menus.ui                   |   2 +-
 src/plugins/codeui/meson.build                    |   1 +
 8 files changed, 381 insertions(+), 1 deletion(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f27d85066..89412ef66 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -131,6 +131,8 @@ src/plugins/code-index/ide-code-index-index.c
 src/plugins/codespell/ide-codespell-diagnostic-provider.c
 src/plugins/codeui/gbp-codeui-editor-page-addin.c
 src/plugins/codeui/gbp-codeui-hover-provider.c
+src/plugins/codeui/gbp-codeui-range-dialog.c
+src/plugins/codeui/gbp-codeui-range-dialog.ui
 src/plugins/codeui/gbp-codeui-rename-dialog.c
 src/plugins/codeui/gbp-codeui-rename-dialog.ui
 src/plugins/codeui/gtk/menus.ui
diff --git a/src/plugins/codeui/codeui.gresource.xml b/src/plugins/codeui/codeui.gresource.xml
index 32f4c9aac..1eb2d12e7 100644
--- a/src/plugins/codeui/codeui.gresource.xml
+++ b/src/plugins/codeui/codeui.gresource.xml
@@ -2,6 +2,7 @@
 <gresources>
   <gresource prefix="/plugins/codeui">
     <file>codeui.plugin</file>
+    <file preprocess="xml-stripblanks">gbp-codeui-range-dialog.ui</file>
     <file preprocess="xml-stripblanks">gbp-codeui-rename-dialog.ui</file>
     <file preprocess="xml-stripblanks">gtk/menus.ui</file>
   </gresource>
diff --git a/src/plugins/codeui/gbp-codeui-editor-page-addin.c 
b/src/plugins/codeui/gbp-codeui-editor-page-addin.c
index 0bc7f604e..d49e32825 100644
--- a/src/plugins/codeui/gbp-codeui-editor-page-addin.c
+++ b/src/plugins/codeui/gbp-codeui-editor-page-addin.c
@@ -27,6 +27,7 @@
 #include <libide-editor.h>
 
 #include "gbp-codeui-editor-page-addin.h"
+#include "gbp-codeui-range-dialog.h"
 #include "gbp-codeui-rename-dialog.h"
 
 struct _GbpCodeuiEditorPageAddin
@@ -40,6 +41,8 @@ struct _GbpCodeuiEditorPageAddin
   gulong         notify_has_selection;
 };
 
+static void find_references_action  (GbpCodeuiEditorPageAddin *self,
+                                     GVariant                 *params);
 static void goto_declaration_action (GbpCodeuiEditorPageAddin *self,
                                      GVariant                 *params);
 static void goto_definition_action  (GbpCodeuiEditorPageAddin *self,
@@ -51,6 +54,7 @@ IDE_DEFINE_ACTION_GROUP (GbpCodeuiEditorPageAddin, gbp_codeui_editor_page_addin,
   { "rename-symbol", rename_symbol_action },
   { "goto-declaration", goto_declaration_action },
   { "goto-definition", goto_definition_action },
+  { "find-references", find_references_action },
 })
 
 static void
@@ -73,6 +77,7 @@ gbp_codeui_editor_page_addin_update_state (GbpCodeuiEditorPageAddin *self)
   gbp_codeui_editor_page_addin_set_action_enabled (self, "rename-symbol", has_selection && provider);
   gbp_codeui_editor_page_addin_set_action_enabled (self, "goto-declaration", has_resolvers);
   gbp_codeui_editor_page_addin_set_action_enabled (self, "goto-definition", has_resolvers);
+  gbp_codeui_editor_page_addin_set_action_enabled (self, "find-references", has_resolvers);
 
   IDE_EXIT;
 }
@@ -337,6 +342,132 @@ goto_definition_action (GbpCodeuiEditorPageAddin *self,
   IDE_EXIT;
 }
 
+typedef struct
+{
+  IdeLocation          *location;
+  GPtrArray            *active;
+  GListStore           *references;
+  GbpCodeuiRangeDialog *dialog;
+} FindReferences;
+
+static void
+find_references_free (FindReferences *state)
+{
+  g_clear_object (&state->location);
+  g_clear_object (&state->references);
+  g_clear_object (&state->dialog);
+  g_clear_pointer (&state->active, g_ptr_array_unref);
+  g_slice_free (FindReferences, state);
+}
+
+static void
+find_references_append (FindReferences *state,
+                        GPtrArray      *ranges)
+{
+  g_assert (state != NULL);
+  g_assert (G_IS_LIST_STORE (state->references));
+  g_assert (ranges != NULL);
+
+  for (guint i = 0; i < ranges->len; i++)
+    g_list_store_append (state->references, g_ptr_array_index (ranges, i));
+}
+
+static void
+find_references_cb (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  IdeSymbolResolver *resolver = (IdeSymbolResolver *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GPtrArray) references = NULL;
+  g_autoptr(GError) error = NULL;
+  GbpCodeuiEditorPageAddin *self;
+  FindReferences *state;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_SYMBOL_RESOLVER (resolver));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  self = ide_task_get_source_object (task);
+  state = ide_task_get_task_data (task);
+
+  if (!(references = ide_symbol_resolver_find_references_finish (resolver, result, &error)))
+    gbp_codeui_editor_page_addin_error (self, error);
+  else
+    find_references_append (state, references);
+
+  g_ptr_array_remove (state->active, resolver);
+
+  if (state->active->len == 0)
+    {
+      adw_message_dialog_set_response_label (ADW_MESSAGE_DIALOG (state->dialog),
+                                             "close", _("Close"));
+      gbp_codeui_range_dialog_done (state->dialog);
+      ide_task_return_boolean (task, TRUE);
+    }
+}
+
+static void
+find_references_action (GbpCodeuiEditorPageAddin *self,
+                        GVariant                 *params)
+{
+  g_autoptr(GPtrArray) resolvers = NULL;
+  g_autoptr(IdeTask) task = NULL;
+  FindReferences *state;
+  const char *language_id;
+  GtkWidget *dialog;
+  GtkRoot *toplevel;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODEUI_EDITOR_PAGE_ADDIN (self));
+
+  resolvers = ide_buffer_get_symbol_resolvers (self->buffer);
+  IDE_PTR_ARRAY_SET_FREE_FUNC (resolvers, g_object_unref);
+
+  if (resolvers == NULL || resolvers->len == 0)
+    return;
+
+  task = ide_task_new (self, NULL, NULL, NULL);
+  ide_task_set_source_tag (task, find_references_action);
+
+  state = g_slice_new0 (FindReferences);
+  state->location = ide_buffer_get_insert_location (self->buffer);
+  state->active = g_ptr_array_ref (resolvers);
+  state->references = g_list_store_new (IDE_TYPE_RANGE);
+  ide_task_set_task_data (task, state, find_references_free);
+
+  language_id = ide_buffer_get_language_id (self->buffer);
+
+  for (guint i = 0; i < resolvers->len; i++)
+    {
+      IdeSymbolResolver *resolver = g_ptr_array_index (resolvers, i);
+
+      ide_symbol_resolver_find_references_async (resolver,
+                                                 state->location,
+                                                 language_id,
+                                                 NULL,
+                                                 find_references_cb,
+                                                 g_object_ref (task));
+    }
+
+  toplevel = gtk_widget_get_root (GTK_WIDGET (self->page));
+  dialog = g_object_new (GBP_TYPE_CODEUI_RANGE_DIALOG,
+                         "transient-for", toplevel,
+                         "modal", FALSE,
+                         "model", state->references,
+                         "heading", _("Find References"),
+                         NULL);
+
+  state->dialog = g_object_ref_sink (GBP_CODEUI_RANGE_DIALOG (dialog));
+
+  gtk_window_present (GTK_WINDOW (dialog));
+
+  IDE_EXIT;
+}
+
 G_DEFINE_FINAL_TYPE_WITH_CODE (GbpCodeuiEditorPageAddin, gbp_codeui_editor_page_addin, G_TYPE_OBJECT,
                                G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, 
gbp_codeui_editor_page_addin_init_action_group)
                                G_IMPLEMENT_INTERFACE (IDE_TYPE_EDITOR_PAGE_ADDIN, 
editor_page_addin_iface_init))
diff --git a/src/plugins/codeui/gbp-codeui-range-dialog.c b/src/plugins/codeui/gbp-codeui-range-dialog.c
new file mode 100644
index 000000000..d9dcff21a
--- /dev/null
+++ b/src/plugins/codeui/gbp-codeui-range-dialog.c
@@ -0,0 +1,177 @@
+/* gbp-codeui-range-dialog.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-codeui-range-dialog"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <libpanel.h>
+
+#include <libide-editor.h>
+#include <libide-code.h>
+
+#include "gbp-codeui-range-dialog.h"
+
+struct _GbpCodeuiRangeDialog
+{
+  AdwMessageDialog parent_instance;
+  GtkListBox *list_box;
+  AdwActionRow *loading;
+  AdwPreferencesGroup *group;
+  guint count;
+};
+
+G_DEFINE_FINAL_TYPE (GbpCodeuiRangeDialog, gbp_codeui_range_dialog, ADW_TYPE_MESSAGE_DIALOG)
+
+enum {
+  PROP_0,
+  PROP_MODEL,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static GtkWidget *
+create_widget_cb (gpointer item,
+                  gpointer user_data)
+{
+  GbpCodeuiRangeDialog *self = user_data;
+  IdeRange *range = item;
+  IdeLocation *begin = ide_range_get_begin (range);
+  GFile *file = ide_location_get_file (begin);
+  g_autofree char *name = g_file_get_basename (file);
+  g_autoptr(GFile) parent = g_file_get_parent (file);
+  g_autofree char *dir = g_file_is_native (parent) ? g_file_get_path (parent) : g_file_get_uri (parent);
+  guint line = ide_location_get_line (begin) + 1;
+  guint line_offset = ide_location_get_line_offset (begin) + 1;
+  g_autofree char *title = g_strdup_printf ("%s:%u:%u", name, line, line_offset);
+  GtkWidget *image = gtk_image_new_from_icon_name ("go-next-symbolic");
+  AdwActionRow *row;
+
+  self->count++;
+
+  row = g_object_new (ADW_TYPE_ACTION_ROW,
+                      "activatable", TRUE,
+                      "title", title,
+                      "subtitle", dir,
+                      NULL);
+
+  g_object_set_data_full (G_OBJECT (row),
+                          "IDE_LOCATION",
+                          g_object_ref (begin),
+                          g_object_unref);
+
+  adw_action_row_add_suffix (row, image);
+
+  gtk_widget_show (GTK_WIDGET (self->list_box));
+
+  return GTK_WIDGET (row);
+}
+
+static void
+gbp_codeui_range_dialog_activate_row_cb (GbpCodeuiRangeDialog *self,
+                                         GtkListBoxRow        *row,
+                                         GtkListBox           *list_box)
+{
+  g_autoptr(PanelPosition) position = NULL;
+  IdeWorkspace *workspace;
+  IdeLocation *location;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODEUI_RANGE_DIALOG (self));
+  g_assert (GTK_IS_LIST_BOX_ROW (row));
+  g_assert (GTK_IS_LIST_BOX (list_box));
+
+  workspace = ide_widget_get_workspace (GTK_WIDGET (self));
+  location = g_object_get_data (G_OBJECT (row), "IDE_LOCATION");
+  position = panel_position_new ();
+
+  ide_editor_focus_location (workspace, position, location);
+}
+
+static void
+gbp_codeui_range_dialog_set_property (GObject      *object,
+                                      guint         prop_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec)
+{
+  GbpCodeuiRangeDialog *self = GBP_CODEUI_RANGE_DIALOG (object);
+
+  switch (prop_id)
+    {
+    case PROP_MODEL:
+      gtk_list_box_bind_model (self->list_box,
+                               g_value_get_object (value),
+                               create_widget_cb, self, NULL);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_codeui_range_dialog_class_init (GbpCodeuiRangeDialogClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->set_property = gbp_codeui_range_dialog_set_property;
+
+  properties[PROP_MODEL] =
+    g_param_spec_object ("model", NULL, NULL,
+                         G_TYPE_LIST_MODEL,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/plugins/codeui/gbp-codeui-range-dialog.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpCodeuiRangeDialog, group);
+  gtk_widget_class_bind_template_child (widget_class, GbpCodeuiRangeDialog, list_box);
+  gtk_widget_class_bind_template_child (widget_class, GbpCodeuiRangeDialog, loading);
+  gtk_widget_class_bind_template_callback (widget_class, gbp_codeui_range_dialog_activate_row_cb);
+}
+
+static void
+gbp_codeui_range_dialog_init (GbpCodeuiRangeDialog *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+void
+gbp_codeui_range_dialog_done (GbpCodeuiRangeDialog *self)
+{
+  g_return_if_fail (GBP_IS_CODEUI_RANGE_DIALOG (self));
+
+  if (self->count == 0)
+    {
+      g_object_set (self->loading,
+                    "title", _("No references found"),
+                    "subtitle", _("The programming language tooling may not support finding references"),
+                    NULL);
+      gtk_widget_hide (GTK_WIDGET (self->list_box));
+      return;
+    }
+
+  adw_preferences_group_remove (self->group, GTK_WIDGET (self->loading));
+  self->loading = NULL;
+}
diff --git a/src/plugins/codeui/gbp-codeui-range-dialog.h b/src/plugins/codeui/gbp-codeui-range-dialog.h
new file mode 100644
index 000000000..a5fcd9687
--- /dev/null
+++ b/src/plugins/codeui/gbp-codeui-range-dialog.h
@@ -0,0 +1,33 @@
+/* gbp-codeui-range-dialog.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 <adwaita.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_CODEUI_RANGE_DIALOG (gbp_codeui_range_dialog_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpCodeuiRangeDialog, gbp_codeui_range_dialog, GBP, CODEUI_RANGE_DIALOG, 
AdwMessageDialog)
+
+void gbp_codeui_range_dialog_done (GbpCodeuiRangeDialog *self);
+
+G_END_DECLS
diff --git a/src/plugins/codeui/gbp-codeui-range-dialog.ui b/src/plugins/codeui/gbp-codeui-range-dialog.ui
new file mode 100644
index 000000000..ed5252f56
--- /dev/null
+++ b/src/plugins/codeui/gbp-codeui-range-dialog.ui
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GbpCodeuiRangeDialog" parent="AdwMessageDialog">
+    <property name="heading" translatable="yes">Find References</property>
+    <property name="close-response">close</property>
+    <responses>
+      <response id="close" translatable="yes">_Cancel</response>
+    </responses>
+    <property name="extra-child">
+      <object class="AdwPreferencesPage">
+        <child>
+          <object class="AdwPreferencesGroup" id="group">
+            <property name="width-request">375</property>
+            <property name="height-request">75</property>
+            <child>
+              <object class="AdwActionRow" id="loading">
+                <property name="title" translatable="yes">Loading…</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBox" id="list_box">
+                <property name="margin-top">18</property>
+                <property name="visible">false</property>
+                <signal name="row-activated" handler="gbp_codeui_range_dialog_activate_row_cb" 
swapped="true" object="GbpCodeuiRangeDialog"/>
+                <style>
+                  <class name="boxed-list"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </property>
+  </template>
+</interface>
diff --git a/src/plugins/codeui/gtk/menus.ui b/src/plugins/codeui/gtk/menus.ui
index afbedbaf2..d22d52921 100644
--- a/src/plugins/codeui/gtk/menus.ui
+++ b/src/plugins/codeui/gtk/menus.ui
@@ -22,7 +22,7 @@
       <item>
         <attribute name="id">source-view-find-references</attribute>
         <attribute name="label" translatable="yes">_Find References</attribute>
-        <attribute name="action">sourceview.find-references</attribute>
+        <attribute name="action">page.codeui.find-references</attribute>
       </item>
     </section>
   </menu>
diff --git a/src/plugins/codeui/meson.build b/src/plugins/codeui/meson.build
index 13b9c353d..7eeafc8bf 100644
--- a/src/plugins/codeui/meson.build
+++ b/src/plugins/codeui/meson.build
@@ -3,6 +3,7 @@ plugins_sources += files([
   'gbp-codeui-buffer-addin.c',
   'gbp-codeui-editor-page-addin.c',
   'gbp-codeui-hover-provider.c',
+  'gbp-codeui-range-dialog.c',
   'gbp-codeui-rename-dialog.c',
   'gbp-codeui-tree-addin.c',
 ])


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