[gnome-builder/wip/chergert/completion] snippets: add completion provider as a plugin



commit 24a1e7e22eaff013a517e80d8ea654b79661db3f
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jun 1 04:38:42 2018 -0700

    snippets: add completion provider as a plugin
    
    We still keep all the snippet plumbing internal, but this moves the
    provider into the src/plugins/ directory so we dont have to mix plumbing
    and plugin stuff in the same directory in libide. They all get dynamically
    linked together anyway.

 meson_options.txt                                  |   1 +
 src/libide/ide.h                                   |   1 +
 .../snippets/ide-snippet-completion-provider.c     |  58 ------
 src/libide/snippets/ide-snippet-storage.c          |   2 +-
 src/libide/snippets/ide-snippet.c                  |  25 ++-
 src/libide/snippets/ide-snippet.h                  |  30 +--
 src/libide/snippets/meson.build                    |  10 +-
 src/plugins/meson.build                            |   1 +
 .../snippets/ide-snippet-completion-provider.c     | 213 +++++++++++++++++++++
 .../snippets/ide-snippet-completion-provider.h     |  10 +-
 src/plugins/snippets/meson.build                   |  17 ++
 src/plugins/snippets/snippets-plugin.c             |  30 +++
 src/plugins/snippets/snippets.gresource.xml        |   8 +
 src/plugins/snippets/snippets.plugin               |  11 ++
 14 files changed, 324 insertions(+), 93 deletions(-)
---
diff --git a/meson_options.txt b/meson_options.txt
index c1dac3790..43dedfae0 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -71,6 +71,7 @@ option('with_retab', type: 'boolean')
 option('with_rust_langserv', type: 'boolean')
 option('with_rustup', type: 'boolean')
 option('with_spellcheck', type: 'boolean')
+option('with_snippets', type: 'boolean')
 option('with_support', type: 'boolean')
 option('with_symbol_tree', type: 'boolean')
 option('with_sysmon', type: 'boolean')
diff --git a/src/libide/ide.h b/src/libide/ide.h
index 52741e32d..e17092e31 100644
--- a/src/libide/ide.h
+++ b/src/libide/ide.h
@@ -162,6 +162,7 @@ G_BEGIN_DECLS
 #include "snippets/ide-snippet.h"
 #include "snippets/ide-snippet-chunk.h"
 #include "snippets/ide-snippet-context.h"
+#include "snippets/ide-snippet-model.h"
 #include "snippets/ide-snippet-storage.h"
 #include "sourceview/ide-indenter.h"
 #include "sourceview/ide-language.h"
diff --git a/src/libide/snippets/ide-snippet-storage.c b/src/libide/snippets/ide-snippet-storage.c
index f318babd3..a23edd09b 100644
--- a/src/libide/snippets/ide-snippet-storage.c
+++ b/src/libide/snippets/ide-snippet-storage.c
@@ -230,7 +230,7 @@ ide_snippet_storage_add (IdeSnippetStorage *self,
 }
 
 /**
- * ide_snippet_storage_query:
+ * ide_snippet_storage_foreach:
  * @self: a #IdeSnippetStorage
  * @foreach: (scope call): the closure to call for each info
  * @user_data: closure data for @foreach
diff --git a/src/libide/snippets/ide-snippet.c b/src/libide/snippets/ide-snippet.c
index a285a72ae..66f3919e0 100644
--- a/src/libide/snippets/ide-snippet.c
+++ b/src/libide/snippets/ide-snippet.c
@@ -25,10 +25,11 @@
 
 #include "ide-debug.h"
 
-#include "ide-snippet.h"
-#include "ide-snippet-private.h"
-#include "ide-snippet-chunk.h"
-#include "ide-snippet-context.h"
+#include "completion/ide-completion-proposal.h"
+#include "snippets/ide-snippet.h"
+#include "snippets/ide-snippet-private.h"
+#include "snippets/ide-snippet-chunk.h"
+#include "snippets/ide-snippet-context.h"
 
 /**
  * SECTION:ide-snippet
@@ -74,7 +75,21 @@ enum {
   LAST_PROP
 };
 
-G_DEFINE_TYPE (IdeSnippet, ide_snippet, G_TYPE_OBJECT)
+static gchar *
+ide_snippet_get_comment (IdeCompletionProposal *proposal)
+{
+  return g_strdup (IDE_SNIPPET (proposal)->description);
+}
+
+static void
+proposal_iface_init (IdeCompletionProposalInterface *iface)
+{
+  iface->get_comment = ide_snippet_get_comment;
+}
+
+G_DEFINE_TYPE_WITH_CODE (IdeSnippet, ide_snippet, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_COMPLETION_PROPOSAL,
+                                                proposal_iface_init))
 
 DZL_DEFINE_COUNTER (instances, "Snippets", "N Snippets", "Number of IdeSnippet instances.");
 
diff --git a/src/libide/snippets/ide-snippet.h b/src/libide/snippets/ide-snippet.h
index 7de541a85..003f2ada7 100644
--- a/src/libide/snippets/ide-snippet.h
+++ b/src/libide/snippets/ide-snippet.h
@@ -27,45 +27,45 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_SNIPPET (ide_snippet_get_type())
 
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 G_DECLARE_FINAL_TYPE (IdeSnippet, ide_snippet, IDE, SNIPPET, GObject)
 
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 IdeSnippet              *ide_snippet_new              (const gchar     *trigger,
                                                        const gchar     *language);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 IdeSnippet              *ide_snippet_copy             (IdeSnippet      *self);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 const gchar             *ide_snippet_get_trigger      (IdeSnippet      *self);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 void                     ide_snippet_set_trigger      (IdeSnippet      *self,
                                                        const gchar     *trigger);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 const gchar             *ide_snippet_get_language     (IdeSnippet      *self);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 void                     ide_snippet_set_language     (IdeSnippet      *self,
                                                        const gchar     *language);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 const gchar             *ide_snippet_get_description  (IdeSnippet      *self);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 void                     ide_snippet_set_description  (IdeSnippet      *self,
                                                        const gchar     *description);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 void                     ide_snippet_add_chunk        (IdeSnippet      *self,
                                                        IdeSnippetChunk *chunk);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 guint                    ide_snippet_get_n_chunks     (IdeSnippet      *self);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 gint                     ide_snippet_get_tab_stop     (IdeSnippet      *self);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 IdeSnippetChunk         *ide_snippet_get_nth_chunk    (IdeSnippet      *self,
                                                        guint            n);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 void                     ide_snippet_get_chunk_range  (IdeSnippet      *self,
                                                        IdeSnippetChunk *chunk,
                                                        GtkTextIter     *begin,
                                                        GtkTextIter     *end);
-IDE_AVAILABLE_IN_ALL
+IDE_AVAILABLE_IN_3_30
 IdeSnippetContext       *ide_snippet_get_context      (IdeSnippet      *self);
 
 G_END_DECLS
diff --git a/src/libide/snippets/meson.build b/src/libide/snippets/meson.build
index dfb7d2612..551023575 100644
--- a/src/libide/snippets/meson.build
+++ b/src/libide/snippets/meson.build
@@ -7,11 +7,6 @@ snippets_headers = [
 ]
 
 snippets_sources = [
-  'ide-snippet-parser.h',
-  'ide-snippet-private.h',
-]
-
-snippets_private_sources = [
   'ide-snippet.c',
   'ide-snippet-chunk.c',
   'ide-snippet-context.c',
@@ -20,6 +15,11 @@ snippets_private_sources = [
   'ide-snippet-storage.c',
 ]
 
+snippets_private_sources = [
+  'ide-snippet-parser.h',
+  'ide-snippet-private.h',
+]
+
 libide_public_headers += files(snippets_headers)
 libide_public_sources += files(snippets_sources)
 libide_private_sources += files(snippets_private_sources)
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 776636ef7..dd37b30ae 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -63,6 +63,7 @@ subdir('retab')
 subdir('rust-langserv')
 subdir('rustup')
 subdir('spellcheck')
+subdir('snippets')
 subdir('support')
 subdir('symbol-tree')
 subdir('sysmon')
diff --git a/src/plugins/snippets/ide-snippet-completion-provider.c 
b/src/plugins/snippets/ide-snippet-completion-provider.c
new file mode 100644
index 000000000..66e87bbd8
--- /dev/null
+++ b/src/plugins/snippets/ide-snippet-completion-provider.c
@@ -0,0 +1,213 @@
+/* ide-snippet-completion-provider.c
+ *
+ * Copyright © 2018 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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-snippet-completion-provider.h"
+
+#include "ide-snippet-completion-provider.h"
+
+struct _IdeSnippetCompletionProvider
+{
+  IdeObject        parent_instance;
+  IdeSnippetModel *model;
+};
+
+static void provider_iface_init (IdeCompletionProviderInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (IdeSnippetCompletionProvider,
+                         ide_snippet_completion_provider,
+                         IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_COMPLETION_PROVIDER, provider_iface_init))
+
+static void
+ide_snippet_completion_provider_finalize (GObject *object)
+{
+  IdeSnippetCompletionProvider *self = (IdeSnippetCompletionProvider *)object;
+
+  g_clear_object (&self->model);
+
+  G_OBJECT_CLASS (ide_snippet_completion_provider_parent_class)->finalize (object);
+}
+
+static void
+ide_snippet_completion_provider_class_init (IdeSnippetCompletionProviderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_snippet_completion_provider_finalize;
+}
+
+static void
+ide_snippet_completion_provider_init (IdeSnippetCompletionProvider *self)
+{
+}
+
+static void
+ide_snippet_completion_provider_load (IdeCompletionProvider *provider,
+                                      IdeContext            *context)
+{
+  IdeSnippetCompletionProvider *self = (IdeSnippetCompletionProvider *)provider;
+  IdeSnippetStorage *storage;
+
+  g_assert (IDE_IS_SNIPPET_COMPLETION_PROVIDER (self));
+  g_assert (IDE_IS_CONTEXT (context));
+
+  storage = ide_context_get_snippets (context);
+  self->model = ide_snippet_model_new (storage);
+}
+
+static gint
+ide_snippet_completion_provider_get_priority (IdeCompletionProvider *provider)
+{
+  return -100;
+}
+
+static gchar *
+ide_snippet_completion_provider_get_title (IdeCompletionProvider *provider)
+{
+  return g_strdup ("Snippets");
+}
+
+static void
+ide_snippet_completion_provider_populate_async (IdeCompletionProvider  *provider,
+                                                IdeCompletionContext   *context,
+                                                GCancellable           *cancellable,
+                                                GListModel            **proposals,
+                                                GAsyncReadyCallback     callback,
+                                                gpointer                user_data)
+{
+  IdeSnippetCompletionProvider *self = (IdeSnippetCompletionProvider *)provider;
+  g_autoptr(IdeTask) task = NULL;
+  g_autofree gchar *prefix = NULL;
+  GtkSourceLanguage *lang;
+  GtkTextBuffer *buffer;
+  const gchar *lang_id = NULL;
+  GtkTextIter begin, end;
+
+  g_assert (IDE_IS_SNIPPET_COMPLETION_PROVIDER (self));
+  g_assert (IDE_IS_COMPLETION_CONTEXT (context));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, ide_snippet_completion_provider_populate_async);
+
+  *proposals = g_object_ref (G_LIST_MODEL (self->model));
+
+  if (ide_completion_context_get_bounds (context, &begin, &end))
+    prefix = gtk_text_iter_get_slice (&begin, &end);
+
+  if ((buffer = ide_completion_context_get_buffer (context)) &&
+      GTK_SOURCE_IS_BUFFER (buffer) &&
+      (lang = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (buffer))))
+    lang_id = gtk_source_language_get_id (lang);
+
+  ide_snippet_model_set_language (self->model, lang_id);
+  ide_snippet_model_set_prefix (self->model, prefix);
+
+  ide_task_return_pointer (task, g_object_ref (self->model), g_object_unref);
+}
+
+static GListModel *
+ide_snippet_completion_provider_populate_finish (IdeCompletionProvider  *provider,
+                                                 GAsyncResult           *result,
+                                                 GError                **error)
+{
+  g_assert (IDE_IS_SNIPPET_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_pointer (IDE_TASK (result), error);
+}
+
+static gboolean
+ide_snippet_completion_provider_refilter (IdeCompletionProvider *provider,
+                                          IdeCompletionContext  *context,
+                                          GListModel            *proposals)
+{
+  g_autofree gchar *prefix = NULL;
+  GtkTextIter begin, end;
+
+  g_assert (IDE_IS_SNIPPET_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_COMPLETION_CONTEXT (context));
+  g_assert (IDE_IS_SNIPPET_MODEL (proposals));
+
+  if (ide_completion_context_get_bounds (context, &begin, &end))
+    prefix = gtk_text_iter_get_slice (&begin, &end);
+
+  ide_snippet_model_set_prefix (IDE_SNIPPET_MODEL (proposals), prefix);
+
+  return TRUE;
+}
+
+static void
+ide_snippet_completion_provider_display_proposal (IdeCompletionProvider   *provider,
+                                                  IdeCompletionListBoxRow *row,
+                                                  IdeCompletionContext    *context,
+                                                  const gchar             *typed_text,
+                                                  IdeCompletionProposal   *proposal)
+{
+  const gchar *trigger;
+
+  g_assert (IDE_IS_SNIPPET_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_COMPLETION_LIST_BOX_ROW (row));
+  g_assert (IDE_IS_COMPLETION_CONTEXT (context));
+  g_assert (IDE_IS_SNIPPET (proposal));
+
+  trigger = ide_snippet_get_trigger (IDE_SNIPPET (proposal));
+
+  /* TODO: have jimmac make us a real icon */
+  ide_completion_list_box_row_set_icon_name (row, "ui-section-symbolic");
+  ide_completion_list_box_row_set_left (row, NULL);
+  ide_completion_list_box_row_set_center (row, trigger);
+  ide_completion_list_box_row_set_right (row, NULL);
+}
+
+static void
+ide_snippet_completion_provider_activate_proposal (IdeCompletionProvider *provider,
+                                                   IdeCompletionContext  *context,
+                                                   IdeCompletionProposal *proposal,
+                                                   const GdkEventKey     *key)
+{
+  GtkTextIter begin, end;
+  GtkTextBuffer *buffer;
+  GtkTextView *view;
+
+  g_assert (IDE_IS_SNIPPET_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_COMPLETION_CONTEXT (context));
+  g_assert (IDE_IS_SNIPPET (proposal));
+
+  buffer = ide_completion_context_get_buffer (context);
+  view = ide_completion_context_get_view (context);
+
+  gtk_text_buffer_begin_user_action (buffer);
+  if (ide_completion_context_get_bounds (context, &begin, &end))
+    gtk_text_buffer_delete (buffer, &begin, &end);
+  ide_source_view_push_snippet (IDE_SOURCE_VIEW (view), IDE_SNIPPET (proposal), &begin);
+  gtk_text_buffer_end_user_action (buffer);
+}
+
+static void
+provider_iface_init (IdeCompletionProviderInterface *iface)
+{
+  iface->load = ide_snippet_completion_provider_load;
+  iface->get_priority = ide_snippet_completion_provider_get_priority;
+  iface->get_title = ide_snippet_completion_provider_get_title;
+  iface->populate_async = ide_snippet_completion_provider_populate_async;
+  iface->populate_finish = ide_snippet_completion_provider_populate_finish;
+  iface->refilter = ide_snippet_completion_provider_refilter;
+  iface->display_proposal = ide_snippet_completion_provider_display_proposal;
+  iface->activate_proposal = ide_snippet_completion_provider_activate_proposal;
+}
diff --git a/src/libide/snippets/ide-snippet-completion-provider.h 
b/src/plugins/snippets/ide-snippet-completion-provider.h
similarity index 68%
rename from src/libide/snippets/ide-snippet-completion-provider.h
rename to src/plugins/snippets/ide-snippet-completion-provider.h
index 08ba84bf2..77ead4081 100644
--- a/src/libide/snippets/ide-snippet-completion-provider.h
+++ b/src/plugins/snippets/ide-snippet-completion-provider.h
@@ -18,20 +18,12 @@
 
 #pragma once
 
-#include "completion/ide-completion-provider.h"
-#include "snippets/ide-snippet.h"
+#include <ide.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_SNIPPET_COMPLETION_PROVIDER (ide_snippet_completion_provider_get_type())
 
-IDE_AVAILABLE_IN_3_30
 G_DECLARE_FINAL_TYPE (IdeSnippetCompletionProvider, ide_snippet_completion_provider, IDE, 
SNIPPET_COMPLETION_PROVIDER, IdeObject)
 
-IDE_AVAILABLE_IN_3_30
-IdeSnippetCompletionProvider *ide_snippet_completion_provider_new          (void);
-IDE_AVAILABLE_IN_3_30
-void                          ide_snippet_completion_provider_set_language (IdeSnippetCompletionProvider 
*self,
-                                                                            const gchar                  
*lang_id);
-
 G_END_DECLS
diff --git a/src/plugins/snippets/meson.build b/src/plugins/snippets/meson.build
new file mode 100644
index 000000000..486c9349b
--- /dev/null
+++ b/src/plugins/snippets/meson.build
@@ -0,0 +1,17 @@
+if get_option('with_snippets')
+
+snippets_resources = gnome.compile_resources(
+  'snippets-resources',
+  'snippets.gresource.xml',
+  c_name: 'gbp_snippets',
+)
+
+snippets_sources = [
+  'snippets-plugin.c',
+  'ide-snippet-completion-provider.c',
+]
+
+gnome_builder_plugins_sources += files(snippets_sources)
+gnome_builder_plugins_sources += snippets_resources[0]
+
+endif
diff --git a/src/plugins/snippets/snippets-plugin.c b/src/plugins/snippets/snippets-plugin.c
new file mode 100644
index 000000000..e37e63773
--- /dev/null
+++ b/src/plugins/snippets/snippets-plugin.c
@@ -0,0 +1,30 @@
+/* snippets-plugin.c
+ *
+ * Copyright 2018 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/>.
+ */
+
+#include <ide.h>
+#include <libpeas/peas.h>
+
+#include "ide-snippet-completion-provider.h"
+
+void
+gbp_snippets_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_COMPLETION_PROVIDER,
+                                              IDE_TYPE_SNIPPET_COMPLETION_PROVIDER);
+}
diff --git a/src/plugins/snippets/snippets.gresource.xml b/src/plugins/snippets/snippets.gresource.xml
new file mode 100644
index 000000000..560b08dac
--- /dev/null
+++ b/src/plugins/snippets/snippets.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/builder/plugins">
+    <file>snippets.plugin</file>
+  </gresource>
+  <gresource prefix="/org/gnome/builder/plugins/snippets-plugin">
+  </gresource>
+</gresources>
diff --git a/src/plugins/snippets/snippets.plugin b/src/plugins/snippets/snippets.plugin
new file mode 100644
index 000000000..037dcec7f
--- /dev/null
+++ b/src/plugins/snippets/snippets.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Module=snippets-plugin
+Name=Snippets
+Description=Support for snippets in a variety of languages
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2018 Christian Hergert
+Depends=editor;
+Builtin=true
+Hidden=true
+Embedded=gbp_snippets_register_types
+X-Completion-Provider-Languages=*


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