[gnome-builder/wip/chergert/completion] langserv: port to new completion API



commit 83033e1785b3a423be1f31bac1207313bfad1e79
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jun 4 01:05:04 2018 -0700

    langserv: port to new completion API
    
    This brings the minimal language server implementation
    we have up to date using the new completion API.

 src/libide/ide.h                                   |   2 +
 src/libide/langserv/ide-langserv-completion-item.c | 141 +++++++++
 src/libide/langserv/ide-langserv-completion-item.h |  47 +++
 .../langserv/ide-langserv-completion-provider.c    | 322 ++++++++++-----------
 .../langserv/ide-langserv-completion-provider.h    |   9 +-
 .../langserv/ide-langserv-completion-results.c     | 177 +++++++++++
 .../langserv/ide-langserv-completion-results.h     |  36 +++
 src/libide/langserv/meson.build                    |   4 +
 src/plugins/go-langserv/go_langserver_plugin.py    |   2 +-
 src/plugins/rust-langserv/rust_langserv_plugin.py  |   2 -
 10 files changed, 565 insertions(+), 177 deletions(-)
---
diff --git a/src/libide/ide.h b/src/libide/ide.h
index ec4ad3d00..dc21a4873 100644
--- a/src/libide/ide.h
+++ b/src/libide/ide.h
@@ -121,7 +121,9 @@ G_BEGIN_DECLS
 #include "highlighting/ide-highlight-index.h"
 #include "highlighting/ide-highlighter.h"
 #include "langserv/ide-langserv-client.h"
+#include "langserv/ide-langserv-completion-item.h"
 #include "langserv/ide-langserv-completion-provider.h"
+#include "langserv/ide-langserv-completion-results.h"
 #include "langserv/ide-langserv-diagnostic-provider.h"
 #include "langserv/ide-langserv-rename-provider.h"
 #include "langserv/ide-langserv-symbol-resolver.h"
diff --git a/src/libide/langserv/ide-langserv-completion-item.c 
b/src/libide/langserv/ide-langserv-completion-item.c
new file mode 100644
index 000000000..0f09ca35a
--- /dev/null
+++ b/src/libide/langserv/ide-langserv-completion-item.c
@@ -0,0 +1,141 @@
+/* ide-langserv-completion-item.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 "config.h"
+
+#define G_LOG_DOMAIN "ide-langserv-completion-item"
+
+#include <jsonrpc-glib.h>
+
+#include "ide-debug.h"
+
+#include "completion/ide-completion-item.h"
+#include "langserv/ide-langserv-completion-item.h"
+#include "langserv/ide-langserv-util.h"
+#include "snippets/ide-snippet-chunk.h"
+#include "symbols/ide-symbol.h"
+
+struct _IdeLangservCompletionItem
+{
+  GObject parent_instance;
+  GVariant *variant;
+  const gchar *label;
+  const gchar *detail;
+  guint kind;
+};
+
+G_DEFINE_TYPE_WITH_CODE (IdeLangservCompletionItem, ide_langserv_completion_item, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_COMPLETION_PROPOSAL, NULL))
+
+static void
+ide_langserv_completion_item_finalize (GObject *object)
+{
+  IdeLangservCompletionItem *self = (IdeLangservCompletionItem *)object;
+
+  g_clear_pointer (&self->variant, g_variant_unref);
+
+  G_OBJECT_CLASS (ide_langserv_completion_item_parent_class)->finalize (object);
+}
+
+static void
+ide_langserv_completion_item_class_init (IdeLangservCompletionItemClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_langserv_completion_item_finalize;
+}
+
+static void
+ide_langserv_completion_item_init (IdeLangservCompletionItem *self)
+{
+}
+
+IdeLangservCompletionItem *
+ide_langserv_completion_item_new (GVariant *variant)
+{
+  g_autoptr(GVariant) unboxed = NULL;
+  IdeLangservCompletionItem *self;
+  gint64 kind = 0;
+
+  g_return_val_if_fail (variant != NULL, NULL);
+
+  if (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARIANT))
+    variant = unboxed = g_variant_get_variant (variant);
+
+  self = g_object_new (IDE_TYPE_LANGSERV_COMPLETION_ITEM, NULL);
+  self->variant = g_variant_ref_sink (variant);
+
+  g_variant_lookup (variant, "label", "&s", &self->label);
+  g_variant_lookup (variant, "detail", "&s", &self->detail);
+
+  if (JSONRPC_MESSAGE_PARSE (variant, "kind", JSONRPC_MESSAGE_GET_INT64 (&kind)))
+    self->kind = ide_langserv_decode_completion_kind (kind);
+
+  return self;
+}
+
+gchar *
+ide_langserv_completion_item_get_markup (IdeLangservCompletionItem *self,
+                                         const gchar               *typed_text)
+{
+  g_return_val_if_fail (IDE_IS_LANGSERV_COMPLETION_ITEM (self), NULL);
+
+  return ide_completion_item_fuzzy_highlight (self->label, typed_text);
+}
+
+const gchar *
+ide_langserv_completion_item_get_return_type (IdeLangservCompletionItem *self)
+{
+  g_return_val_if_fail (IDE_IS_LANGSERV_COMPLETION_ITEM (self), NULL);
+
+  /* TODO: How do we get this from langserv? */
+
+  return NULL;
+}
+
+const gchar *
+ide_langserv_completion_item_get_icon_name (IdeLangservCompletionItem *self)
+{
+  g_return_val_if_fail (IDE_IS_LANGSERV_COMPLETION_ITEM (self), NULL);
+
+  return ide_symbol_kind_get_icon_name (self->kind);
+}
+
+const gchar *
+ide_langserv_completion_item_get_detail (IdeLangservCompletionItem *self)
+{
+  g_return_val_if_fail (IDE_IS_LANGSERV_COMPLETION_ITEM (self), NULL);
+
+  return self->detail;
+}
+
+IdeSnippet *
+ide_langserv_completion_item_get_snippet (IdeLangservCompletionItem *self)
+{
+  g_autoptr(IdeSnippet) snippet = NULL;
+  g_autoptr(IdeSnippetChunk) chunk = NULL;
+
+  g_return_val_if_fail (IDE_IS_LANGSERV_COMPLETION_ITEM (self), NULL);
+
+  snippet = ide_snippet_new (NULL, NULL);
+  chunk = ide_snippet_chunk_new ();
+  ide_snippet_chunk_set_spec (chunk, self->label);
+  ide_snippet_add_chunk (snippet, chunk);
+
+  return g_steal_pointer (&snippet);
+}
diff --git a/src/libide/langserv/ide-langserv-completion-item.h 
b/src/libide/langserv/ide-langserv-completion-item.h
new file mode 100644
index 000000000..e5b9d948f
--- /dev/null
+++ b/src/libide/langserv/ide-langserv-completion-item.h
@@ -0,0 +1,47 @@
+/* ide-langserv-completion-item.h
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include "ide-version-macros.h"
+
+#include "completion/ide-completion-proposal.h"
+#include "snippets/ide-snippet.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_LANGSERV_COMPLETION_ITEM (ide_langserv_completion_item_get_type())
+
+IDE_AVAILABLE_IN_3_30
+G_DECLARE_FINAL_TYPE (IdeLangservCompletionItem, ide_langserv_completion_item, IDE, 
LANGSERV_COMPLETION_ITEM, GObject)
+
+IDE_AVAILABLE_IN_3_30
+IdeLangservCompletionItem *ide_langserv_completion_item_new             (GVariant *variant);
+IDE_AVAILABLE_IN_3_30
+const gchar               *ide_langserv_completion_item_get_icon_name   (IdeLangservCompletionItem *self);
+IDE_AVAILABLE_IN_3_30
+const gchar               *ide_langserv_completion_item_get_return_type (IdeLangservCompletionItem *self);
+IDE_AVAILABLE_IN_3_30
+const gchar               *ide_langserv_completion_item_get_detail      (IdeLangservCompletionItem *self);
+IDE_AVAILABLE_IN_3_30
+gchar                     *ide_langserv_completion_item_get_markup      (IdeLangservCompletionItem *self,
+                                                                         const gchar               
*typed_text);
+IDE_AVAILABLE_IN_3_30
+IdeSnippet                *ide_langserv_completion_item_get_snippet     (IdeLangservCompletionItem *self);
+
+G_END_DECLS
diff --git a/src/libide/langserv/ide-langserv-completion-provider.c 
b/src/libide/langserv/ide-langserv-completion-provider.c
index 9561333fa..87b541aef 100644
--- a/src/libide/langserv/ide-langserv-completion-provider.c
+++ b/src/libide/langserv/ide-langserv-completion-provider.c
@@ -16,36 +16,38 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#define G_LOG_DOMAIN "ide-langserv-completion-provider"
-
 #include "config.h"
 
+#define G_LOG_DOMAIN "ide-langserv-completion-provider"
+
 #include <jsonrpc-glib.h>
 
 #include "ide-debug.h"
 
 #include "buffers/ide-buffer.h"
+#include "completion/ide-completion-context.h"
+#include "completion/ide-completion-provider.h"
+#include "completion/ide-completion-list-box-row.h"
 #include "langserv/ide-langserv-completion-provider.h"
+#include "langserv/ide-langserv-completion-item.h"
+#include "langserv/ide-langserv-completion-results.h"
 #include "langserv/ide-langserv-util.h"
+#include "snippets/ide-snippet.h"
+#include "sourceview/ide-source-view.h"
 #include "symbols/ide-symbol.h"
+#include "threading/ide-task.h"
 
 typedef struct
 {
   IdeLangservClient *client;
+  gchar *word;
 } IdeLangservCompletionProviderPrivate;
 
-typedef struct
-{
-  IdeLangservCompletionProvider *self;
-  GtkSourceCompletionContext    *context;
-} CompletionState;
-
-static void source_completion_provider_iface_init (GtkSourceCompletionProviderIface *iface);
+static void provider_iface_init (IdeCompletionProviderInterface *iface);
 
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdeLangservCompletionProvider, ide_langserv_completion_provider, 
IDE_TYPE_OBJECT,
                                   G_ADD_PRIVATE (IdeLangservCompletionProvider)
-                                  G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROVIDER, 
source_completion_provider_iface_init)
-                                  G_IMPLEMENT_INTERFACE (IDE_TYPE_COMPLETION_PROVIDER, NULL))
+                                  G_IMPLEMENT_INTERFACE (IDE_TYPE_COMPLETION_PROVIDER, provider_iface_init))
 
 enum {
   PROP_0,
@@ -55,29 +57,6 @@ enum {
 
 static GParamSpec *properties [N_PROPS];
 
-static void
-completion_state_free (CompletionState *state)
-{
-  g_clear_object (&state->self);
-  g_clear_object (&state->context);
-  g_slice_free (CompletionState, state);
-}
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (CompletionState, completion_state_free);
-
-CompletionState *
-completion_state_new (IdeLangservCompletionProvider *self,
-                      GtkSourceCompletionContext    *context)
-{
-  CompletionState *state;
-
-  state = g_slice_new0 (CompletionState);
-  state->self = g_object_ref (self);
-  state->context = g_object_ref (context);
-
-  return state;
-}
-
 static void
 ide_langserv_completion_provider_finalize (GObject *object)
 {
@@ -85,6 +64,7 @@ ide_langserv_completion_provider_finalize (GObject *object)
   IdeLangservCompletionProviderPrivate *priv = ide_langserv_completion_provider_get_instance_private (self);
 
   g_clear_object (&priv->client);
+  g_clear_pointer (&priv->word, g_free);
 
   G_OBJECT_CLASS (ide_langserv_completion_provider_parent_class)->finalize (object);
 }
@@ -182,170 +162,93 @@ ide_langserv_completion_provider_set_client (IdeLangservCompletionProvider *self
     g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CLIENT]);
 }
 
-static gchar *
-ide_langserv_completion_provider_get_name (GtkSourceCompletionProvider *provider)
-{
-  return g_strdup ("Rust");
-}
-
 static gint
-ide_langserv_completion_provider_get_priority (GtkSourceCompletionProvider *provider)
+ide_langserv_completion_provider_get_priority (IdeCompletionProvider *provider)
 {
   return IDE_LANGSERV_COMPLETION_PROVIDER_PRIORITY;
 }
 
-static gboolean
-ide_langserv_completion_provider_match (GtkSourceCompletionProvider *provider,
-                                        GtkSourceCompletionContext  *context)
-{
-  GtkSourceCompletionActivation activation;
-  GtkTextIter iter;
-
-  g_assert (IDE_IS_LANGSERV_COMPLETION_PROVIDER (provider));
-  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
-
-  if (!gtk_source_completion_context_get_iter (context, &iter))
-    return FALSE;
-
-  activation = gtk_source_completion_context_get_activation (context);
-
-  if (activation == GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE)
-    {
-      if (gtk_text_iter_starts_line (&iter) ||
-          !gtk_text_iter_backward_char (&iter) ||
-          g_unichar_isspace (gtk_text_iter_get_char (&iter)))
-        return FALSE;
-    }
-
-  if (ide_completion_provider_context_in_comment (context))
-    return FALSE;
-
-  return TRUE;
-}
-
 static void
 ide_langserv_completion_provider_complete_cb (GObject      *object,
                                               GAsyncResult *result,
                                               gpointer      user_data)
 {
+  IdeLangservCompletionProviderPrivate *priv;
+  IdeLangservCompletionProvider *self;
   IdeLangservClient *client = (IdeLangservClient *)object;
-  g_autoptr(CompletionState) state = user_data;
   g_autoptr(GVariant) return_value = NULL;
+  g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
-  GQueue queue = G_QUEUE_INIT;
-  GVariant *node;
-  GVariantIter iter;
+  IdeLangservCompletionResults *ret;
 
   IDE_ENTRY;
 
   g_assert (IDE_IS_LANGSERV_CLIENT (client));
   g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (state != NULL);
-  g_assert (IDE_IS_LANGSERV_COMPLETION_PROVIDER (state->self));
-  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (state->context));
 
   if (!ide_langserv_client_call_finish (client, result, &return_value, &error))
     {
-      /* If we were cancelled, we shouldn't report anything to the context,
-       * as it is no longer the active contenxt for completion.
-       */
-      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        IDE_EXIT;
-      g_debug ("%s", error->message);
-      IDE_GOTO (failure);
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
     }
 
-  /*
-   * TODO: We will want to make a much more optimized version of this using
-   *       the other completion result work we've done.
-   */
-
-  g_variant_iter_init (&iter, return_value);
+  self = ide_task_get_source_object (task);
+  priv = ide_langserv_completion_provider_get_instance_private (self);
 
-  while (g_variant_iter_loop (&iter, "v", &node))
-    {
-      g_autoptr(GtkSourceCompletionItem) item = NULL;
-      g_autofree gchar *full_label = NULL;
-      const gchar *label;
-      const gchar *detail;
-      const gchar *icon_name = NULL;
-      gboolean success;
-      gint64 kind = 0;
-
-      success = JSONRPC_MESSAGE_PARSE (node,
-        "label", JSONRPC_MESSAGE_GET_STRING (&label),
-        "detail", JSONRPC_MESSAGE_GET_STRING (&detail)
-      );
-
-      if (!success)
-        {
-          IDE_TRACE_MSG ("Failed to extract completion item from node");
-          continue;
-        }
-
-      /* Optional kind field */
-      JSONRPC_MESSAGE_PARSE (node, "kind", JSONRPC_MESSAGE_GET_INT64 (&kind));
-      kind = ide_langserv_decode_completion_kind (kind);
-      if (kind != IDE_SYMBOL_NONE)
-        icon_name = ide_symbol_kind_get_icon_name (kind);
-
-      if (label != NULL && detail != NULL)
-        full_label = g_strdup_printf ("%s : %s", label, detail);
-      else
-        full_label = g_strdup (label);
-
-      item = g_object_new (GTK_SOURCE_TYPE_COMPLETION_ITEM,
-                           "icon-name", icon_name,
-                           "label", full_label,
-                           "text", label,
-                           NULL);
-
-      g_queue_push_tail (&queue, g_steal_pointer (&item));
-    }
-
-failure:
-  gtk_source_completion_context_add_proposals (state->context,
-                                               GTK_SOURCE_COMPLETION_PROVIDER (state->self),
-                                               queue.head,
-                                               TRUE);
+  ret = ide_langserv_completion_results_new (return_value);
+  if (priv->word != NULL && *priv->word != 0)
+    ide_langserv_completion_results_refilter (ret, priv->word);
 
-  g_queue_foreach (&queue, (GFunc)g_object_unref, NULL);
-  g_queue_clear (&queue);
+  ide_task_return_object (task, g_steal_pointer (&ret));
 
   IDE_EXIT;
 }
 
 static void
-ide_langserv_completion_provider_populate (GtkSourceCompletionProvider *provider,
-                                           GtkSourceCompletionContext  *context)
+ide_langserv_completion_provider_populate_async (IdeCompletionProvider  *provider,
+                                                 IdeCompletionContext   *context,
+                                                 GCancellable           *cancellable,
+                                                 GListModel            **proposals,
+                                                 GAsyncReadyCallback     callback,
+                                                 gpointer                user_data)
 {
   IdeLangservCompletionProvider *self = (IdeLangservCompletionProvider *)provider;
   IdeLangservCompletionProviderPrivate *priv = ide_langserv_completion_provider_get_instance_private (self);
+  g_autoptr(IdeTask) task = NULL;
   g_autoptr(GVariant) params = NULL;
-  g_autoptr(GCancellable) cancellable = NULL;
-  g_autoptr(CompletionState) state = NULL;
   g_autofree gchar *uri = NULL;
-  GtkTextIter iter;
-  IdeBuffer *buffer;
+  GtkTextIter iter, end;
+  GtkTextBuffer *buffer;
   gint line;
   gint column;
 
   IDE_ENTRY;
 
   g_assert (IDE_IS_LANGSERV_COMPLETION_PROVIDER (self));
-  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+  g_assert (IDE_IS_COMPLETION_CONTEXT (context));
+  g_assert (proposals != NULL);
+
+  *proposals = NULL;
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, ide_langserv_completion_provider_populate_async);
 
   if (priv->client == NULL)
     {
-      IDE_TRACE_MSG ("No client set, cannot provide proposals");
-      gtk_source_completion_context_add_proposals (context, provider, NULL, TRUE);
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 "No client for completion");
       IDE_EXIT;
     }
 
-  gtk_source_completion_context_get_iter (context, &iter);
+  ide_completion_context_get_bounds (context, &iter, &end);
 
-  buffer = IDE_BUFFER (gtk_text_iter_get_buffer (&iter));
-  uri = ide_buffer_get_uri (buffer);
+  g_clear_pointer (&priv->word, g_free);
+  priv->word = ide_completion_context_get_word (context);
+
+  buffer = ide_completion_context_get_buffer (context);
+  uri = ide_buffer_get_uri (IDE_BUFFER (buffer));
 
   line = gtk_text_iter_get_line (&iter);
   column = gtk_text_iter_get_line_offset (&iter);
@@ -360,32 +263,119 @@ ide_langserv_completion_provider_populate (GtkSourceCompletionProvider *provider
     "}"
   );
 
-  cancellable = g_cancellable_new ();
-
-  g_signal_connect_data (context,
-                         "cancelled",
-                         G_CALLBACK (g_cancellable_cancel),
-                         g_object_ref (cancellable),
-                         (GClosureNotify)g_object_unref,
-                         G_CONNECT_SWAPPED);
-
-  state = completion_state_new (self, context);
-
   ide_langserv_client_call_async (priv->client,
                                   "textDocument/completion",
                                   params,
                                   cancellable,
                                   ide_langserv_completion_provider_complete_cb,
-                                  g_steal_pointer (&state));
+                                  g_steal_pointer (&task));
 
   IDE_EXIT;
 }
 
+static GListModel *
+ide_langserv_completion_provider_populate_finish (IdeCompletionProvider  *provider,
+                                                  GAsyncResult           *result,
+                                                  GError                **error)
+{
+  GListModel *ret;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_TASK (result));
+
+  ret = ide_task_propagate_object (IDE_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+static gboolean
+ide_langserv_completion_provider_refilter (IdeCompletionProvider *provider,
+                                           IdeCompletionContext  *context,
+                                           GListModel            *model)
+{
+  IdeLangservCompletionResults *results = (IdeLangservCompletionResults *)model;
+  g_autofree gchar *word = NULL;
+
+  g_assert (IDE_IS_LANGSERV_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_COMPLETION_CONTEXT (context));
+  g_assert (IDE_IS_LANGSERV_COMPLETION_RESULTS (results));
+
+  word = ide_completion_context_get_word (context);
+  ide_langserv_completion_results_refilter (results, word);
+
+  return TRUE;
+}
+
+static void
+ide_langserv_completion_provider_display_proposal (IdeCompletionProvider   *provider,
+                                                   IdeCompletionListBoxRow *row,
+                                                   IdeCompletionContext    *context,
+                                                   const gchar             *typed_text,
+                                                   IdeCompletionProposal   *proposal)
+{
+  IdeLangservCompletionItem *item = (IdeLangservCompletionItem *)proposal;
+  g_autofree gchar *markup = NULL;
+
+  g_assert (IDE_IS_LANGSERV_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_COMPLETION_LIST_BOX_ROW (row));
+  g_assert (IDE_IS_COMPLETION_CONTEXT (context));
+  g_assert (IDE_IS_LANGSERV_COMPLETION_ITEM (proposal));
+
+  markup = ide_langserv_completion_item_get_markup (item, typed_text);
+
+  ide_completion_list_box_row_set_icon_name (row, ide_langserv_completion_item_get_icon_name (item));
+  ide_completion_list_box_row_set_left (row, NULL);
+  ide_completion_list_box_row_set_center_markup (row, markup);
+  ide_completion_list_box_row_set_right (row, NULL);
+}
+
+static void
+ide_langserv_completion_provider_activate_proposal (IdeCompletionProvider *provider,
+                                                    IdeCompletionContext  *context,
+                                                    IdeCompletionProposal *proposal,
+                                                    const GdkEventKey     *key)
+{
+  g_autoptr(IdeSnippet) snippet = NULL;
+  GtkTextBuffer *buffer;
+  GtkTextView *view;
+  GtkTextIter begin, end;
+
+  g_assert (IDE_IS_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_COMPLETION_CONTEXT (context));
+  g_assert (IDE_IS_LANGSERV_COMPLETION_ITEM (proposal));
+
+  buffer = ide_completion_context_get_buffer (context);
+  view = ide_completion_context_get_view (context);
+
+  snippet = ide_langserv_completion_item_get_snippet (IDE_LANGSERV_COMPLETION_ITEM (proposal));
+
+  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), snippet, &begin);
+  gtk_text_buffer_end_user_action (buffer);
+}
+
+static gchar *
+ide_langserv_completion_provider_get_comment (IdeCompletionProvider *provider,
+                                              IdeCompletionProposal *proposal)
+{
+  g_assert (IDE_IS_COMPLETION_PROVIDER (provider));
+  g_assert (IDE_IS_LANGSERV_COMPLETION_ITEM (proposal));
+
+  return g_strdup (ide_langserv_completion_item_get_detail (IDE_LANGSERV_COMPLETION_ITEM (proposal)));
+}
+
 static void
-source_completion_provider_iface_init (GtkSourceCompletionProviderIface *iface)
+provider_iface_init (IdeCompletionProviderInterface *iface)
 {
-  iface->get_name = ide_langserv_completion_provider_get_name;
   iface->get_priority = ide_langserv_completion_provider_get_priority;
-  iface->match = ide_langserv_completion_provider_match;
-  iface->populate = ide_langserv_completion_provider_populate;
+  iface->populate_async = ide_langserv_completion_provider_populate_async;
+  iface->populate_finish = ide_langserv_completion_provider_populate_finish;
+  iface->refilter = ide_langserv_completion_provider_refilter;
+  iface->display_proposal = ide_langserv_completion_provider_display_proposal;
+  iface->activate_proposal = ide_langserv_completion_provider_activate_proposal;
+  iface->get_comment = ide_langserv_completion_provider_get_comment;
 }
diff --git a/src/libide/langserv/ide-langserv-completion-provider.h 
b/src/libide/langserv/ide-langserv-completion-provider.h
index b88e94e94..cd664852d 100644
--- a/src/libide/langserv/ide-langserv-completion-provider.h
+++ b/src/libide/langserv/ide-langserv-completion-provider.h
@@ -39,14 +39,7 @@ struct _IdeLangservCompletionProviderClass
   IdeObjectClass parent_class;
 
   /*< private >*/
-  gpointer _reserved1;
-  gpointer _reserved2;
-  gpointer _reserved3;
-  gpointer _reserved4;
-  gpointer _reserved5;
-  gpointer _reserved6;
-  gpointer _reserved7;
-  gpointer _reserved8;
+  gpointer _reserved[8];
 };
 
 IDE_AVAILABLE_IN_ALL
diff --git a/src/libide/langserv/ide-langserv-completion-results.c 
b/src/libide/langserv/ide-langserv-completion-results.c
new file mode 100644
index 000000000..15a5a8b5f
--- /dev/null
+++ b/src/libide/langserv/ide-langserv-completion-results.c
@@ -0,0 +1,177 @@
+/* ide-langserv-completion-results.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 "config.h"
+
+#define G_LOG_DOMAIN "ide-langserv-completion-results.h"
+
+#include "ide-debug.h"
+
+#include "completion/ide-completion-item.h"
+#include "langserv/ide-langserv-completion-item.h"
+#include "langserv/ide-langserv-completion-results.h"
+
+struct _IdeLangservCompletionResults
+{
+  GObject   parent_instance;
+  GVariant *results;
+  GArray   *items;
+};
+
+typedef struct
+{
+  guint index;
+  guint priority;
+} Item;
+
+static void list_model_iface_init (GListModelInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (IdeLangservCompletionResults, ide_langserv_completion_results, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static void
+ide_langserv_completion_results_finalize (GObject *object)
+{
+  IdeLangservCompletionResults *self = (IdeLangservCompletionResults *)object;
+
+  g_clear_pointer (&self->results, g_variant_unref);
+  g_clear_pointer (&self->items, g_array_unref);
+
+  G_OBJECT_CLASS (ide_langserv_completion_results_parent_class)->finalize (object);
+}
+
+static void
+ide_langserv_completion_results_class_init (IdeLangservCompletionResultsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_langserv_completion_results_finalize;
+}
+
+static void
+ide_langserv_completion_results_init (IdeLangservCompletionResults *self)
+{
+  self->items = g_array_new (FALSE, FALSE, sizeof (Item));
+}
+
+IdeLangservCompletionResults *
+ide_langserv_completion_results_new (GVariant *results)
+{
+  IdeLangservCompletionResults *self;
+
+  g_return_val_if_fail (results != NULL, NULL);
+
+  self = g_object_new (IDE_TYPE_LANGSERV_COMPLETION_RESULTS, NULL);
+  self->results = g_variant_ref_sink (results);
+
+  ide_langserv_completion_results_refilter (self, NULL);
+
+  return self;
+}
+
+static GType
+ide_langserv_completion_results_get_item_type (GListModel *model)
+{
+  return IDE_TYPE_LANGSERV_COMPLETION_ITEM;
+}
+
+static guint
+ide_langserv_completion_results_get_n_items (GListModel *model)
+{
+  IdeLangservCompletionResults *self = (IdeLangservCompletionResults *)model;
+
+  g_assert (IDE_IS_LANGSERV_COMPLETION_RESULTS (self));
+
+  return self->results ? g_variant_n_children (self->results) : 0;
+}
+
+static gpointer
+ide_langserv_completion_results_get_item (GListModel *model,
+                                          guint       position)
+{
+  IdeLangservCompletionResults *self = (IdeLangservCompletionResults *)model;
+  g_autoptr(GVariant) child = NULL;
+
+  g_assert (IDE_IS_LANGSERV_COMPLETION_RESULTS (self));
+  g_assert (self->results != NULL);
+
+  child = g_variant_get_child_value (self->results, position);
+
+  return ide_langserv_completion_item_new (child);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+  iface->get_item = ide_langserv_completion_results_get_item;
+  iface->get_n_items = ide_langserv_completion_results_get_n_items;
+  iface->get_item_type = ide_langserv_completion_results_get_item_type;
+}
+
+void
+ide_langserv_completion_results_refilter (IdeLangservCompletionResults *self,
+                                          const gchar                  *typed_text)
+{
+  GVariantIter iter;
+  GVariant *node;
+  guint index = 0;
+  guint old_len;
+
+  g_return_if_fail (IDE_IS_LANGSERV_COMPLETION_RESULTS (self));
+
+  if ((old_len = self->items->len))
+    g_array_remove_range (self->items, 0, old_len);
+
+  if (self->results == NULL)
+    return;
+
+  if (typed_text == NULL || *typed_text == 0)
+    {
+      guint n_items = g_variant_n_children (self->results);
+
+      for (guint i = 0; i < n_items; i++)
+        {
+          Item item = { i };
+          g_array_append_val (self->items, item);
+        }
+
+      g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, n_items);
+
+      return;
+    }
+
+  g_variant_iter_init (&iter, self->results);
+
+  while (g_variant_iter_loop (&iter, "v", &node))
+    {
+      const gchar *detail;
+      guint priority;
+
+      g_variant_lookup (node, "detail", "&s", &detail);
+
+      if (ide_completion_item_fuzzy_match (detail, typed_text, &priority))
+        {
+          Item item = { .index = index, .priority = priority };
+          g_array_append_val (self->items, item);
+        }
+
+      index++;
+    }
+
+  g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, index);
+}
diff --git a/src/libide/langserv/ide-langserv-completion-results.h 
b/src/libide/langserv/ide-langserv-completion-results.h
new file mode 100644
index 000000000..1887e3b40
--- /dev/null
+++ b/src/libide/langserv/ide-langserv-completion-results.h
@@ -0,0 +1,36 @@
+/* ide-langserv-completion-results.h
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_LANGSERV_COMPLETION_RESULTS (ide_langserv_completion_results_get_type())
+
+IDE_AVAILABLE_IN_3_30
+G_DECLARE_FINAL_TYPE (IdeLangservCompletionResults, ide_langserv_completion_results, IDE, 
LANGSERV_COMPLETION_RESULTS, GObject)
+
+IDE_AVAILABLE_IN_3_30
+IdeLangservCompletionResults *ide_langserv_completion_results_new      (GVariant                     
*results);
+IDE_AVAILABLE_IN_3_30
+void                          ide_langserv_completion_results_refilter (IdeLangservCompletionResults *self,
+                                                                        const gchar                  
*typed_text);
+
+G_END_DECLS
diff --git a/src/libide/langserv/meson.build b/src/libide/langserv/meson.build
index bc765159b..6e70a4630 100644
--- a/src/libide/langserv/meson.build
+++ b/src/libide/langserv/meson.build
@@ -1,6 +1,8 @@
 langserv_headers = [
   'ide-langserv-client.h',
+  'ide-langserv-completion-item.h',
   'ide-langserv-completion-provider.h',
+  'ide-langserv-completion-results.h',
   'ide-langserv-diagnostic-provider.h',
   'ide-langserv-formatter.h',
   'ide-langserv-highlighter.h',
@@ -14,7 +16,9 @@ langserv_headers = [
 
 langserv_sources = [
   'ide-langserv-client.c',
+  'ide-langserv-completion-item.c',
   'ide-langserv-completion-provider.c',
+  'ide-langserv-completion-results.c',
   'ide-langserv-diagnostic-provider.c',
   'ide-langserv-formatter.c',
   'ide-langserv-highlighter.c',
diff --git a/src/plugins/go-langserv/go_langserver_plugin.py b/src/plugins/go-langserv/go_langserver_plugin.py
index 630b2b59b..ad31384e0 100644
--- a/src/plugins/go-langserv/go_langserver_plugin.py
+++ b/src/plugins/go-langserv/go_langserver_plugin.py
@@ -111,7 +111,7 @@ class GoSymbolResolver(Ide.LangservSymbolResolver, Ide.SymbolResolver):
 ## This is supported as of a few weeks ago, but at least for me, it seems
 ## awfully crashy, so I'm going to leave it disabled by default so as to
 ## not give a bad impression
-#class GoCompletionProvider(Ide.LangservCompletionProvider, GtkSource.CompletionProvider, 
Ide.CompletionProvider):
+#class GoCompletionProvider(Ide.LangservCompletionProvider, Ide.CompletionProvider):
 #    def do_load(self, context):
 #        GoService.bind_client(self)
 
diff --git a/src/plugins/rust-langserv/rust_langserv_plugin.py 
b/src/plugins/rust-langserv/rust_langserv_plugin.py
index 1a1039edc..a56560445 100644
--- a/src/plugins/rust-langserv/rust_langserv_plugin.py
+++ b/src/plugins/rust-langserv/rust_langserv_plugin.py
@@ -27,12 +27,10 @@ import gi
 import os
 
 gi.require_version('Ide', '1.0')
-gi.require_version('GtkSource', '3.0')
 
 from gi.repository import GLib
 from gi.repository import Gio
 from gi.repository import GObject
-from gi.repository import GtkSource
 from gi.repository import Ide
 
 DEV_MODE = False


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