[gnome-builder] plugins/top-match: add plugin to elevate exect matches



commit c7aa3e260ad50c548fcfdba3c291cc21405ff3c3
Author: Christian Hergert <chergert redhat com>
Date:   Fri Sep 2 23:33:31 2022 -0700

    plugins/top-match: add plugin to elevate exect matches
    
    When we have an exact match, that is found in a lower completion set, we
    really want to see it elevated to the top of the results so that if the
    user hits Enter, for example, it does the expected thing (or at least the
    least likely to be wrong thing).
    
    We still need a better icon for this than the edit-find-symbolic. It would
    also be nice if we could add a separator between the rows but that requires
    changes to GtkSourceView.

 src/plugins/meson.build                            |   1 +
 .../top-match/gbp-top-match-completion-filter.c    | 217 +++++++++++++++++++++
 .../top-match/gbp-top-match-completion-filter.h    |  39 ++++
 .../top-match/gbp-top-match-completion-model.c     | 188 ++++++++++++++++++
 .../top-match/gbp-top-match-completion-model.h     |  35 ++++
 .../top-match/gbp-top-match-completion-proposal.c  |  68 +++++++
 .../top-match/gbp-top-match-completion-proposal.h  |  41 ++++
 .../top-match/gbp-top-match-completion-provider.c  | 176 +++++++++++++++++
 .../top-match/gbp-top-match-completion-provider.h  |  31 +++
 src/plugins/top-match/meson.build                  |  15 ++
 src/plugins/top-match/top-match-plugin.c           |  37 ++++
 src/plugins/top-match/top-match.gresource.xml      |   6 +
 src/plugins/top-match/top-match.plugin             |   9 +
 13 files changed, 863 insertions(+)
---
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 1993c2aaf..0d1af7d4a 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -124,6 +124,7 @@ subdir('sysroot')
 subdir('terminal')
 subdir('testui')
 subdir('todo')
+subdir('top-match')
 subdir('ts-language-server')
 subdir('trim-spaces')
 subdir('update-dependencies')
diff --git a/src/plugins/top-match/gbp-top-match-completion-filter.c 
b/src/plugins/top-match/gbp-top-match-completion-filter.c
new file mode 100644
index 000000000..c011f8834
--- /dev/null
+++ b/src/plugins/top-match/gbp-top-match-completion-filter.c
@@ -0,0 +1,217 @@
+/* gbp-top-match-completion-filter.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-top-match-completion-filter"
+
+#include "config.h"
+
+#include <libide-core.h>
+
+#include "gbp-top-match-completion-filter.h"
+#include "gbp-top-match-completion-proposal.h"
+
+struct _GbpTopMatchCompletionFilter
+{
+  GObject parent_instance;
+  GbpTopMatchCompletionProposal *proposal;
+  GtkSourceCompletionProvider *provider;
+  GListModel *model;
+  char *typed_text;
+  gulong items_changed_handler;
+};
+
+static GType
+gbp_top_match_completion_filter_get_item_type (GListModel *model)
+{
+  return GTK_SOURCE_TYPE_COMPLETION_PROPOSAL;
+}
+
+static guint
+gbp_top_match_completion_filter_get_n_items (GListModel *model)
+{
+  return GBP_TOP_MATCH_COMPLETION_FILTER (model)->proposal ? 1 : 0;
+}
+
+static gpointer
+gbp_top_match_completion_filter_get_item (GListModel *model,
+                                          guint       position)
+{
+  if (position == 0)
+    {
+      GbpTopMatchCompletionFilter *self = GBP_TOP_MATCH_COMPLETION_FILTER (model);
+
+      return self->proposal ? g_object_ref (self->proposal) : NULL;
+    }
+
+  return NULL;
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+  iface->get_item_type = gbp_top_match_completion_filter_get_item_type;
+  iface->get_n_items = gbp_top_match_completion_filter_get_n_items;
+  iface->get_item = gbp_top_match_completion_filter_get_item;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpTopMatchCompletionFilter, gbp_top_match_completion_filter, G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static void
+gbp_top_match_completion_filter_dispose (GObject *object)
+{
+  GbpTopMatchCompletionFilter *self = (GbpTopMatchCompletionFilter *)object;
+
+  if (self->model != NULL)
+    g_clear_signal_handler (&self->items_changed_handler, self->model);
+
+  g_clear_object (&self->proposal);
+  g_clear_object (&self->provider);
+  g_clear_object (&self->model);
+  g_clear_pointer (&self->typed_text, g_free);
+
+  G_OBJECT_CLASS (gbp_top_match_completion_filter_parent_class)->dispose (object);
+}
+
+static void
+gbp_top_match_completion_filter_class_init (GbpTopMatchCompletionFilterClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gbp_top_match_completion_filter_dispose;
+}
+
+static void
+gbp_top_match_completion_filter_init (GbpTopMatchCompletionFilter *self)
+{
+}
+
+static void
+gbp_top_match_completion_filter_update (GbpTopMatchCompletionFilter *self,
+                                        gboolean                     definitely_has_items)
+{
+  guint removed = 0;
+  guint added = 0;
+
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_FILTER (self));
+
+  if (self->proposal != NULL)
+    {
+      g_clear_object (&self->proposal);
+      removed = 1;
+    }
+
+  if (!ide_str_empty0 (self->typed_text) &&
+      self->provider != NULL &&
+      self->model != NULL &&
+      (definitely_has_items || g_list_model_get_n_items (self->model) > 0))
+    {
+      g_autoptr(GtkSourceCompletionProposal) first = g_list_model_get_item (self->model, 0);
+      g_autofree char *typed_text = NULL;
+
+      if (first != NULL &&
+          (typed_text = gtk_source_completion_proposal_get_typed_text (first)) &&
+          ide_str_equal0 (self->typed_text, typed_text))
+        {
+          self->proposal = gbp_top_match_completion_proposal_new (self->provider, first);
+          added = 1;
+        }
+    }
+
+  if (removed || added)
+    g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
+}
+
+
+GbpTopMatchCompletionFilter *
+gbp_top_match_completion_filter_new (GtkSourceCompletionProvider *provider,
+                                     GListModel                  *model)
+{
+  GbpTopMatchCompletionFilter *self;
+
+  g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), NULL);
+  g_return_val_if_fail (!model || G_IS_LIST_MODEL (model), NULL);
+
+  self = g_object_new (GBP_TYPE_TOP_MATCH_COMPLETION_FILTER, NULL);
+  g_set_object (&self->provider, provider);
+  g_set_object (&self->model, model);
+
+  gbp_top_match_completion_filter_update (self, FALSE);
+
+  return self;
+}
+
+static void
+gbp_top_match_completion_filter_items_changed_cb (GbpTopMatchCompletionFilter *self,
+                                                  guint                        position,
+                                                  guint                        added,
+                                                  guint                        removed,
+                                                  GListModel                  *model)
+{
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_FILTER (self));
+  g_assert (G_IS_LIST_MODEL (model));
+
+  if (position == 0 && (added || removed))
+    gbp_top_match_completion_filter_update (self, added > removed);
+}
+
+void
+gbp_top_match_completion_filter_set_model (GbpTopMatchCompletionFilter *self,
+                                           GListModel                  *model)
+{
+  g_return_if_fail (GBP_IS_TOP_MATCH_COMPLETION_FILTER (self));
+  g_return_if_fail (!model || G_IS_LIST_MODEL (model));
+
+  if (self->model == model)
+    return;
+
+  if (self->model != NULL)
+    g_clear_signal_handler (&self->items_changed_handler, self->model);
+
+  g_set_object (&self->model, model);
+
+  if (self->model != NULL)
+    self->items_changed_handler =
+      g_signal_connect_object (self->model,
+                               "items-changed",
+                               G_CALLBACK (gbp_top_match_completion_filter_items_changed_cb),
+                               self,
+                               G_CONNECT_SWAPPED);
+
+  gbp_top_match_completion_filter_update (self, FALSE);
+}
+
+void
+gbp_top_match_completion_filter_set_typed_text (GbpTopMatchCompletionFilter *self,
+                                                const char                  *typed_text)
+{
+  g_return_if_fail (GBP_IS_TOP_MATCH_COMPLETION_FILTER (self));
+
+  if (ide_set_string (&self->typed_text, typed_text))
+    gbp_top_match_completion_filter_update (self, FALSE);
+}
+
+GtkSourceCompletionProvider *
+gbp_top_match_completion_filter_get_provider (GbpTopMatchCompletionFilter *self)
+{
+  g_return_val_if_fail (GBP_IS_TOP_MATCH_COMPLETION_FILTER (self), NULL);
+
+  return self->provider;
+}
diff --git a/src/plugins/top-match/gbp-top-match-completion-filter.h 
b/src/plugins/top-match/gbp-top-match-completion-filter.h
new file mode 100644
index 000000000..841233c47
--- /dev/null
+++ b/src/plugins/top-match/gbp-top-match-completion-filter.h
@@ -0,0 +1,39 @@
+/* gbp-top-match-completion-filter.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 <gtksourceview/gtksource.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_TOP_MATCH_COMPLETION_FILTER (gbp_top_match_completion_filter_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpTopMatchCompletionFilter, gbp_top_match_completion_filter, GBP, 
TOP_MATCH_COMPLETION_FILTER, GObject)
+
+GbpTopMatchCompletionFilter *gbp_top_match_completion_filter_new            (GtkSourceCompletionProvider 
*provider,
+                                                                             GListModel                  
*model);
+GtkSourceCompletionProvider *gbp_top_match_completion_filter_get_provider   (GbpTopMatchCompletionFilter 
*self);
+void                         gbp_top_match_completion_filter_set_model      (GbpTopMatchCompletionFilter 
*self,
+                                                                             GListModel                  
*model);
+void                         gbp_top_match_completion_filter_set_typed_text (GbpTopMatchCompletionFilter 
*self,
+                                                                             const char                  
*word);
+
+G_END_DECLS
diff --git a/src/plugins/top-match/gbp-top-match-completion-model.c 
b/src/plugins/top-match/gbp-top-match-completion-model.c
new file mode 100644
index 000000000..b4b36be71
--- /dev/null
+++ b/src/plugins/top-match/gbp-top-match-completion-model.c
@@ -0,0 +1,188 @@
+/* gbp-top-match-completion-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-top-match-completion-model"
+
+#include "config.h"
+
+#include <gtksourceview/gtksource.h>
+
+#include <libide-core.h>
+
+#include "gbp-top-match-completion-filter.h"
+#include "gbp-top-match-completion-model.h"
+#include "gbp-top-match-completion-provider.h"
+
+struct _GbpTopMatchCompletionModel
+{
+  GObject              parent_instance;
+  GListStore          *filters;
+  GtkFlattenListModel *flatten;
+};
+
+static GType
+gbp_top_match_completion_model_get_item_type (GListModel *model)
+{
+  return GTK_SOURCE_TYPE_COMPLETION_PROPOSAL;
+}
+
+static guint
+gbp_top_match_completion_model_get_n_items (GListModel *model)
+{
+  GbpTopMatchCompletionModel *self = GBP_TOP_MATCH_COMPLETION_MODEL (model);
+
+  return g_list_model_get_n_items (G_LIST_MODEL (self->flatten));
+}
+
+static gpointer
+gbp_top_match_completion_model_get_item (GListModel *model,
+                                         guint       position)
+{
+  GbpTopMatchCompletionModel *self = GBP_TOP_MATCH_COMPLETION_MODEL (model);
+
+  return g_list_model_get_item (G_LIST_MODEL (self->flatten), position);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+  iface->get_n_items = gbp_top_match_completion_model_get_n_items;
+  iface->get_item_type = gbp_top_match_completion_model_get_item_type;
+  iface->get_item = gbp_top_match_completion_model_get_item;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpTopMatchCompletionModel, gbp_top_match_completion_model, G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static void
+gbp_top_match_completion_model_provider_model_changed_cb (GbpTopMatchCompletionModel  *self,
+                                                          GtkSourceCompletionProvider *provider,
+                                                          GListModel                  *model,
+                                                          GtkSourceCompletionContext  *context)
+{
+  GListModel *filters;
+  guint n_items;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_MODEL (self));
+  g_assert (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+  g_assert (!model || G_IS_LIST_MODEL (model));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+
+  filters = G_LIST_MODEL (self->filters);
+  n_items = g_list_model_get_n_items (filters);
+
+  for (guint i = 0; i < n_items; i++)
+    {
+      g_autoptr(GbpTopMatchCompletionFilter) filter = g_list_model_get_item (filters, i);
+
+      if (provider == gbp_top_match_completion_filter_get_provider (filter))
+        {
+          gbp_top_match_completion_filter_set_model (filter, model);
+          break;
+        }
+    }
+}
+
+static void
+gbp_top_match_completion_model_dispose (GObject *object)
+{
+  GbpTopMatchCompletionModel *self = (GbpTopMatchCompletionModel *)object;
+
+  g_clear_object (&self->flatten);
+  g_clear_object (&self->filters);
+
+  G_OBJECT_CLASS (gbp_top_match_completion_model_parent_class)->dispose (object);
+}
+
+static void
+gbp_top_match_completion_model_class_init (GbpTopMatchCompletionModelClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gbp_top_match_completion_model_dispose;
+}
+
+static void
+gbp_top_match_completion_model_init (GbpTopMatchCompletionModel *self)
+{
+  self->filters = g_list_store_new (G_TYPE_LIST_MODEL);
+  self->flatten = gtk_flatten_list_model_new (g_object_ref (G_LIST_MODEL (self->filters)));
+}
+
+GbpTopMatchCompletionModel *
+gbp_top_match_completion_model_new (GtkSourceCompletionContext *context)
+{
+  GbpTopMatchCompletionModel *self;
+  g_autoptr(GListModel) providers = NULL;
+  g_autofree char *word = NULL;
+  guint n_items;
+
+  g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), NULL);
+
+  self = g_object_new (GBP_TYPE_TOP_MATCH_COMPLETION_MODEL, NULL);
+
+  word = gtk_source_completion_context_get_word (context);
+  providers = gtk_source_completion_context_list_providers (context);
+  n_items = g_list_model_get_n_items (providers);
+
+  for (guint i = 0; i < n_items; i++)
+    {
+      g_autoptr(GtkSourceCompletionProvider) provider = g_list_model_get_item (providers, i);
+      g_autoptr(GbpTopMatchCompletionFilter) filter = NULL;
+      GListModel *model;
+
+      if (GBP_IS_TOP_MATCH_COMPLETION_PROVIDER (provider))
+        continue;
+
+      model = gtk_source_completion_context_get_proposals_for_provider (context, provider);
+      filter = gbp_top_match_completion_filter_new (provider, model);
+      gbp_top_match_completion_filter_set_typed_text (filter, word);
+      g_list_store_append (self->filters, filter);
+    }
+
+  g_signal_connect_object (context,
+                           "provider-model-changed",
+                           G_CALLBACK (gbp_top_match_completion_model_provider_model_changed_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+
+  return self;
+}
+
+void
+gbp_top_match_completion_model_set_typed_text (GbpTopMatchCompletionModel *self,
+                                               const char                 *typed_text)
+{
+  GListModel *filters;
+  guint n_items;
+
+  g_return_if_fail (GBP_IS_TOP_MATCH_COMPLETION_MODEL (self));
+
+  filters = G_LIST_MODEL (self->filters);
+  n_items = g_list_model_get_n_items (filters);
+
+  for (guint i = 0; i < n_items; i++)
+    {
+      g_autoptr(GbpTopMatchCompletionFilter) filter = g_list_model_get_item (filters, i);
+      gbp_top_match_completion_filter_set_typed_text (filter, typed_text);
+    }
+}
diff --git a/src/plugins/top-match/gbp-top-match-completion-model.h 
b/src/plugins/top-match/gbp-top-match-completion-model.h
new file mode 100644
index 000000000..e04f49ccd
--- /dev/null
+++ b/src/plugins/top-match/gbp-top-match-completion-model.h
@@ -0,0 +1,35 @@
+/* gbp-top-match-completion-model.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 <gtksourceview/gtksource.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_TOP_MATCH_COMPLETION_MODEL (gbp_top_match_completion_model_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpTopMatchCompletionModel, gbp_top_match_completion_model, GBP, 
TOP_MATCH_COMPLETION_MODEL, GObject)
+
+GbpTopMatchCompletionModel *gbp_top_match_completion_model_new            (GtkSourceCompletionContext 
*context);
+void                        gbp_top_match_completion_model_set_typed_text (GbpTopMatchCompletionModel *self,
+                                                                           const char                 
*typed_text);
+
+G_END_DECLS
diff --git a/src/plugins/top-match/gbp-top-match-completion-proposal.c 
b/src/plugins/top-match/gbp-top-match-completion-proposal.c
new file mode 100644
index 000000000..c931dce04
--- /dev/null
+++ b/src/plugins/top-match/gbp-top-match-completion-proposal.c
@@ -0,0 +1,68 @@
+/* gbp-top-match-completion-proposal.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-top-match-completion-proposal"
+
+#include "config.h"
+
+#include "gbp-top-match-completion-proposal.h"
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpTopMatchCompletionProposal, gbp_top_match_completion_proposal, 
G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROPOSAL, NULL))
+
+static void
+gbp_top_match_completion_proposal_dispose (GObject *object)
+{
+  GbpTopMatchCompletionProposal *self = (GbpTopMatchCompletionProposal *)object;
+
+  g_clear_object (&self->provider);
+  g_clear_object (&self->proposal);
+
+  G_OBJECT_CLASS (gbp_top_match_completion_proposal_parent_class)->dispose (object);
+}
+
+static void
+gbp_top_match_completion_proposal_class_init (GbpTopMatchCompletionProposalClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gbp_top_match_completion_proposal_dispose;
+}
+
+static void
+gbp_top_match_completion_proposal_init (GbpTopMatchCompletionProposal *self)
+{
+}
+
+GbpTopMatchCompletionProposal *
+gbp_top_match_completion_proposal_new (GtkSourceCompletionProvider *provider,
+                                       GtkSourceCompletionProposal *proposal)
+{
+  GbpTopMatchCompletionProposal *self;
+
+  g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), NULL);
+  g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
+
+  self = g_object_new (GBP_TYPE_TOP_MATCH_COMPLETION_PROPOSAL, NULL);
+  g_set_object (&self->provider, provider);
+  g_set_object (&self->proposal, proposal);
+
+  return self;
+}
diff --git a/src/plugins/top-match/gbp-top-match-completion-proposal.h 
b/src/plugins/top-match/gbp-top-match-completion-proposal.h
new file mode 100644
index 000000000..d355b973e
--- /dev/null
+++ b/src/plugins/top-match/gbp-top-match-completion-proposal.h
@@ -0,0 +1,41 @@
+/* gbp-top-match-completion-proposal.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 <gtksourceview/gtksource.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_TOP_MATCH_COMPLETION_PROPOSAL (gbp_top_match_completion_proposal_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpTopMatchCompletionProposal, gbp_top_match_completion_proposal, GBP, 
TOP_MATCH_COMPLETION_PROPOSAL, GObject)
+
+struct _GbpTopMatchCompletionProposal
+{
+  GObject parent_instance;
+  GtkSourceCompletionProposal *proposal;
+  GtkSourceCompletionProvider *provider;
+};
+
+GbpTopMatchCompletionProposal *gbp_top_match_completion_proposal_new (GtkSourceCompletionProvider *provider,
+                                                                      GtkSourceCompletionProposal *proposal);
+
+G_END_DECLS
diff --git a/src/plugins/top-match/gbp-top-match-completion-provider.c 
b/src/plugins/top-match/gbp-top-match-completion-provider.c
new file mode 100644
index 000000000..9867778b6
--- /dev/null
+++ b/src/plugins/top-match/gbp-top-match-completion-provider.c
@@ -0,0 +1,176 @@
+/* gbp-top-match-completion-provider.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-top-match-completion-provider"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <libide-core.h>
+
+#include <gtksourceview/gtksource.h>
+
+#include "gbp-top-match-completion-model.h"
+#include "gbp-top-match-completion-proposal.h"
+#include "gbp-top-match-completion-provider.h"
+
+struct _GbpTopMatchCompletionProvider
+{
+  GObject parent_instance;
+};
+
+static void completion_provider_iface_init (GtkSourceCompletionProviderInterface *iface);
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpTopMatchCompletionProvider, gbp_top_match_completion_provider, 
G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROVIDER, 
completion_provider_iface_init))
+
+static void
+gbp_top_match_completion_provider_dispose (GObject *object)
+{
+  G_OBJECT_CLASS (gbp_top_match_completion_provider_parent_class)->dispose (object);
+}
+
+static void
+gbp_top_match_completion_provider_class_init (GbpTopMatchCompletionProviderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gbp_top_match_completion_provider_dispose;
+}
+
+static void
+gbp_top_match_completion_provider_init (GbpTopMatchCompletionProvider *self)
+{
+}
+
+static int
+gbp_top_match_completion_provider_get_priority (GtkSourceCompletionProvider *self,
+                                                GtkSourceCompletionContext  *context)
+{
+  return G_MAXINT;
+}
+
+static GListModel *
+gbp_top_match_completion_provider_populate (GtkSourceCompletionProvider  *provider,
+                                            GtkSourceCompletionContext   *context,
+                                            GError                      **error)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+
+  return G_LIST_MODEL (gbp_top_match_completion_model_new (context));
+}
+
+static void
+gbp_top_match_completion_provider_display (GtkSourceCompletionProvider *provider,
+                                           GtkSourceCompletionContext  *context,
+                                           GtkSourceCompletionProposal *proposalptr,
+                                           GtkSourceCompletionCell     *cell)
+{
+  GbpTopMatchCompletionProposal *proposal = (GbpTopMatchCompletionProposal *)proposalptr;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROVIDER (provider));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROPOSAL (proposal));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CELL (cell));
+
+  if (gtk_source_completion_cell_get_column (cell) == GTK_SOURCE_COMPLETION_COLUMN_ICON)
+    gtk_source_completion_cell_set_icon_name (cell, "edit-find-symbolic");
+  else
+    gtk_source_completion_provider_display (proposal->provider, context, proposal->proposal, cell);
+}
+
+static void
+gbp_top_match_completion_provider_activate (GtkSourceCompletionProvider *provider,
+                                            GtkSourceCompletionContext  *context,
+                                            GtkSourceCompletionProposal *proposalptr)
+{
+  GbpTopMatchCompletionProposal *proposal = (GbpTopMatchCompletionProposal *)proposalptr;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROVIDER (provider));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROPOSAL (proposal));
+
+  gtk_source_completion_provider_activate (proposal->provider, context, proposal->proposal);
+}
+
+static void
+gbp_top_match_completion_provider_refilter (GtkSourceCompletionProvider *provider,
+                                            GtkSourceCompletionContext  *context,
+                                            GListModel                  *model)
+{
+  g_autofree char *word = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROVIDER (provider));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_MODEL (model));
+
+  word = gtk_source_completion_context_get_word (context);
+  gbp_top_match_completion_model_set_typed_text (GBP_TOP_MATCH_COMPLETION_MODEL (model), word);
+}
+
+static gboolean
+gbp_top_match_completion_provider_key_activates (GtkSourceCompletionProvider *provider,
+                                                 GtkSourceCompletionContext  *context,
+                                                 GtkSourceCompletionProposal *proposalptr,
+                                                 guint                        keyval,
+                                                 GdkModifierType              state)
+{
+  GbpTopMatchCompletionProposal *proposal = (GbpTopMatchCompletionProposal *)proposalptr;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROVIDER (provider));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROPOSAL (proposal));
+
+  return gtk_source_completion_provider_key_activates (proposal->provider, context, proposal->proposal, 
keyval, state);
+}
+
+static GPtrArray *
+gbp_top_match_completion_provider_list_alternates (GtkSourceCompletionProvider *provider,
+                                                   GtkSourceCompletionContext  *context,
+                                                   GtkSourceCompletionProposal *proposalptr)
+{
+  GbpTopMatchCompletionProposal *proposal = (GbpTopMatchCompletionProposal *)proposalptr;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROVIDER (provider));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+  g_assert (GBP_IS_TOP_MATCH_COMPLETION_PROPOSAL (proposal));
+
+  return gtk_source_completion_provider_list_alternates (proposal->provider, context, proposal->proposal);
+}
+
+static void
+completion_provider_iface_init (GtkSourceCompletionProviderInterface *iface)
+{
+  iface->activate = gbp_top_match_completion_provider_activate;
+  iface->display = gbp_top_match_completion_provider_display;
+  iface->get_priority = gbp_top_match_completion_provider_get_priority;
+  iface->key_activates = gbp_top_match_completion_provider_key_activates;
+  iface->populate = gbp_top_match_completion_provider_populate;
+  iface->refilter = gbp_top_match_completion_provider_refilter;
+  iface->list_alternates = gbp_top_match_completion_provider_list_alternates;
+}
diff --git a/src/plugins/top-match/gbp-top-match-completion-provider.h 
b/src/plugins/top-match/gbp-top-match-completion-provider.h
new file mode 100644
index 000000000..1b32d00dd
--- /dev/null
+++ b/src/plugins/top-match/gbp-top-match-completion-provider.h
@@ -0,0 +1,31 @@
+/* gbp-top-match-completion-provider.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 <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_TOP_MATCH_COMPLETION_PROVIDER (gbp_top_match_completion_provider_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpTopMatchCompletionProvider, gbp_top_match_completion_provider, GBP, 
TOP_MATCH_COMPLETION_PROVIDER, GObject)
+
+G_END_DECLS
diff --git a/src/plugins/top-match/meson.build b/src/plugins/top-match/meson.build
new file mode 100644
index 000000000..7afae21f1
--- /dev/null
+++ b/src/plugins/top-match/meson.build
@@ -0,0 +1,15 @@
+plugins_sources += files([
+  'top-match-plugin.c',
+  'gbp-top-match-completion-provider.c',
+  'gbp-top-match-completion-proposal.c',
+  'gbp-top-match-completion-filter.c',
+  'gbp-top-match-completion-model.c',
+])
+
+plugin_top_match_resources = gnome.compile_resources(
+  'gbp-top-match-resources',
+  'top-match.gresource.xml',
+  c_name: 'gbp_top_match',
+)
+
+plugins_sources += plugin_top_match_resources
diff --git a/src/plugins/top-match/top-match-plugin.c b/src/plugins/top-match/top-match-plugin.c
new file mode 100644
index 000000000..3bdbe4765
--- /dev/null
+++ b/src/plugins/top-match/top-match-plugin.c
@@ -0,0 +1,37 @@
+/* top-match-plugin.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 "top-match-plugin"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-sourceview.h>
+
+#include "gbp-top-match-completion-provider.h"
+
+_IDE_EXTERN void
+_gbp_top_match_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              GTK_SOURCE_TYPE_COMPLETION_PROVIDER,
+                                              GBP_TYPE_TOP_MATCH_COMPLETION_PROVIDER);
+}
diff --git a/src/plugins/top-match/top-match.gresource.xml b/src/plugins/top-match/top-match.gresource.xml
new file mode 100644
index 000000000..7672e13a1
--- /dev/null
+++ b/src/plugins/top-match/top-match.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/plugins/top-match">
+    <file>top-match.plugin</file>
+  </gresource>
+</gresources>
diff --git a/src/plugins/top-match/top-match.plugin b/src/plugins/top-match/top-match.plugin
new file mode 100644
index 000000000..fc8842093
--- /dev/null
+++ b/src/plugins/top-match/top-match.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Authors=Christian Hergert <christian hergert me>
+Builtin=true
+Copyright=Copyright © 2022 Christian Hergert
+Description=Promotes exact matches to the top of completion results
+Embedded=_gbp_top_match_register_types
+Hidden=true
+Module=top-match
+Name=Top Match


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