[gtksourceview/wip/chergert/gsv-gtk4: 194/259] completion: new completion engine




commit 3644449c16935cbb5f3afd299f5764166832155f
Author: Christian Hergert <chergert redhat com>
Date:   Sat Aug 29 23:10:31 2020 -0700

    completion: new completion engine
    
    This starts the landing of a new completion engine for GtkSourceView.

 .../completion-providers/words/meson.build         |   12 +-
 gtksourceview/gtksource.h                          |    4 +-
 gtksourceview/gtksourcecompletion-private.h        |   33 +-
 gtksourceview/gtksourcecompletion.c                | 3740 ++++++--------------
 gtksourceview/gtksourcecompletion.h                |   96 +-
 gtksourceview/gtksourcecompletion.ui               |  123 -
 gtksourceview/gtksourcecompletioncell-private.h    |   32 +
 gtksourceview/gtksourcecompletioncell.c            |  419 +++
 gtksourceview/gtksourcecompletioncell.h            |   69 +
 gtksourceview/gtksourcecompletioncontext-private.h |   36 +-
 gtksourceview/gtksourcecompletioncontext.c         | 1083 ++++--
 gtksourceview/gtksourcecompletioncontext.h         |   67 +-
 gtksourceview/gtksourcecompletioninfo-private.h    |   18 +-
 gtksourceview/gtksourcecompletioninfo.c            |  288 +-
 gtksourceview/gtksourcecompletioninfo.h            |   45 -
 gtksourceview/gtksourcecompletionitem.c            |  575 ---
 gtksourceview/gtksourcecompletionitem.h            |   70 -
 gtksourceview/gtksourcecompletionlist-private.h    |   48 +
 gtksourceview/gtksourcecompletionlist.c            |  563 +++
 gtksourceview/gtksourcecompletionlist.ui           |   74 +
 gtksourceview/gtksourcecompletionlistbox-private.h |   56 +
 gtksourceview/gtksourcecompletionlistbox.c         | 1265 +++++++
 .../gtksourcecompletionlistboxrow-private.h        |   49 +
 gtksourceview/gtksourcecompletionlistboxrow.c      |  212 ++
 gtksourceview/gtksourcecompletionlistboxrow.ui     |   65 +
 gtksourceview/gtksourcecompletionmodel-private.h   |   90 -
 gtksourceview/gtksourcecompletionmodel.c           | 1240 -------
 gtksourceview/gtksourcecompletionproposal.c        |  311 +-
 gtksourceview/gtksourcecompletionproposal.h        |   79 +-
 gtksourceview/gtksourcecompletionprovider.c        |  503 +--
 gtksourceview/gtksourcecompletionprovider.h        |  187 +-
 gtksourceview/gtksourcetypes-private.h             |    6 +-
 gtksourceview/gtksourcetypes.h                     |    4 +-
 gtksourceview/gtksourceview.c                      |   26 +-
 gtksourceview/gtksourceview.gresource.xml          |    1 -
 gtksourceview/meson.build                          |   12 +-
 36 files changed, 5269 insertions(+), 6232 deletions(-)
---
diff --git a/gtksourceview/completion-providers/words/meson.build 
b/gtksourceview/completion-providers/words/meson.build
index 29b4d449..b169b746 100644
--- a/gtksourceview/completion-providers/words/meson.build
+++ b/gtksourceview/completion-providers/words/meson.build
@@ -4,15 +4,15 @@ completionwords_c_args = [
 ]
 
 completionwords_public_h = [
-  'gtksourcecompletionwords.h',
+  # 'gtksourcecompletionwords.h',
 ]
 
 completionwords_public_c = [
-  'gtksourcecompletionwords.c',
-  'gtksourcecompletionwordsbuffer.c',
-  'gtksourcecompletionwordslibrary.c',
-  'gtksourcecompletionwordsproposal.c',
-  'gtksourcecompletionwordsutils.c',
+  # 'gtksourcecompletionwords.c',
+  # 'gtksourcecompletionwordsbuffer.c',
+  # 'gtksourcecompletionwordslibrary.c',
+  # 'gtksourcecompletionwordsproposal.c',
+  # 'gtksourcecompletionwordsutils.c',
 ]
 
 install_headers(
diff --git a/gtksourceview/gtksource.h b/gtksourceview/gtksource.h
index b0434691..d54bccd5 100644
--- a/gtksourceview/gtksource.h
+++ b/gtksourceview/gtksource.h
@@ -22,9 +22,9 @@
 #include "completion-providers/words/gtksourcecompletionwords.h"
 #include "gtksourcetypes.h"
 #include "gtksourcebuffer.h"
-#include "gtksourcecompletioncontext.h"
 #include "gtksourcecompletion.h"
-#include "gtksourcecompletionitem.h"
+#include "gtksourcecompletioncell.h"
+#include "gtksourcecompletioncontext.h"
 #include "gtksourcecompletionproposal.h"
 #include "gtksourcecompletionprovider.h"
 #include "gtksourceencoding.h"
diff --git a/gtksourceview/gtksourcecompletion-private.h b/gtksourceview/gtksourcecompletion-private.h
index c7853f48..bae3bdfe 100644
--- a/gtksourceview/gtksourcecompletion-private.h
+++ b/gtksourceview/gtksourcecompletion-private.h
@@ -1,7 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,22 +15,29 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #pragma once
 
 #include <gtk/gtk.h>
 
-#include "gtksourcetypes.h"
+#include "gtksourcecompletion.h"
+#include "gtksourcecompletionlist-private.h"
+
+G_BEGIN_DECLS
+
+GtkSourceCompletion     *_gtk_source_completion_new                (GtkSourceView               *view);
+GtkSourceCompletionList *_gtk_source_completion_get_display        (GtkSourceCompletion         *self);
+void                     _gtk_source_completion_set_font_desc      (GtkSourceCompletion         *self,
+                                                                    const PangoFontDescription  *font_desc);
+void                     _gtk_source_completion_activate           (GtkSourceCompletion         *self,
+                                                                    GtkSourceCompletionContext  *context,
+                                                                    GtkSourceCompletionProvider *provider,
+                                                                    GtkSourceCompletionProposal *proposal);
+gboolean                 _gtk_source_completion_get_select_on_show (GtkSourceCompletion         *self);
+void                     _gtk_source_completion_css_changed        (GtkSourceCompletion         *self,
+                                                                    GtkCssStyleChange           *change);
 
-G_GNUC_INTERNAL
-GtkSourceCompletion *_gtk_source_completion_new           (GtkSourceView               *source_view);
-G_GNUC_INTERNAL
-void                 _gtk_source_completion_add_proposals (GtkSourceCompletion         *completion,
-                                                           GtkSourceCompletionContext  *context,
-                                                           GtkSourceCompletionProvider *provider,
-                                                           GList                       *proposals,
-                                                           gboolean                     finished);
-G_GNUC_INTERNAL
-void                 _gtk_source_completion_css_changed   (GtkSourceCompletion         *completion,
-                                                           GtkCssStyleChange           *change);
+G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletion.c b/gtksourceview/gtksourcecompletion.c
index a825b358..569a64ff 100644
--- a/gtksourceview/gtksourcecompletion.c
+++ b/gtksourceview/gtksourcecompletion.c
@@ -1,9 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2007-2009 Jesús Barbero Rodríguez <chuchiperriman gmail com>
- * Copyright 2009 Jesse van den Kieboom <jessevdk gnome org>
- * Copyright 2013 Sébastien Wilmet <swilmet gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,2214 +15,890 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * SECTION:completion
- * @title: GtkSourceCompletion
- * @short_description: Main Completion Object
- *
- * The completion system helps the user when he/she writes some text,
- * such as words, command names, functions, and suchlike. Proposals can
- * be shown, to complete the text the user is writing. Each proposal can
- * contain an additional piece of information (for example
- * documentation), that is displayed when the "Details" button is
- * clicked.
- *
- * Proposals are created via a #GtkSourceCompletionProvider. There can
- * be for example a provider to complete words (see
- * #GtkSourceCompletionWords), another provider for the completion of
- * function names, etc. To add a provider, call
- * gtk_source_completion_add_provider().
- *
- * When the completion is activated, a #GtkSourceCompletionContext object is
- * created. The providers are asked whether they match the context, with
- * gtk_source_completion_provider_match(). If a provider doesn't match the
- * context, it will not be visible in the completion window. On the
- * other hand, if the provider matches the context, its proposals will
- * be displayed.
- *
- * When several providers match, they are all shown in the completion
- * window, but one can switch between providers: see the
- * #GtkSourceCompletion::move-page signal. It is also possible to
- * activate the first proposals with key bindings, see the
- * #GtkSourceCompletion:accelerators property.
  *
- * The #GtkSourceCompletionProposal interface represents a proposal.
- * The #GtkSourceCompletionItem class is a simple implementation of this
- * interface.
- *
- * If a proposal contains extra information (see
- * gtk_source_completion_provider_get_info_widget()), it will be
- * displayed in a #GtkSourceCompletionInfo window, which appears when
- * the "Details" button is clicked.
- *
- * A #GtkSourceCompletionInfo window can also be used to display
- * calltips. When no proposals are available, it can be useful to
- * display extra information like a function prototype (number of
- * parameters, types of parameters, etc).
- *
- * Each #GtkSourceView object is associated with a #GtkSourceCompletion
- * instance. This instance can be obtained with
- * gtk_source_view_get_completion(). The #GtkSourceView class contains also the
- * #GtkSourceView::show-completion signal.
- *
- * A same #GtkSourceCompletionProvider object can be used for several
- * #GtkSourceCompletion's.
- */
-
-/* Idea to improve the code: use a composite widget template. This class is not
- * a GtkWidget, so some refactoring needs to be done, to have a subclass of
- * GtkSourceCompletionInfo for the main completion window.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #include "config.h"
 
-#include <glib/gi18n-lib.h>
-
-#include "gtksourcecompletion.h"
+#include "gtksourcebindinggroup-private.h"
 #include "gtksourcecompletion-private.h"
-#include "gtksourcecompletionmodel-private.h"
-#include "gtksourcecompletioncontext.h"
-#include "gtksourcecompletioninfo-private.h"
+#include "gtksourcecompletioncontext-private.h"
+#include "gtksourcecompletionlist-private.h"
 #include "gtksourcecompletionproposal.h"
 #include "gtksourcecompletionprovider.h"
-#include "gtksourcecompletioncontext-private.h"
 #include "gtksourcebuffer.h"
-#include "gtksource-marshal.h"
-#include "gtksourceview.h"
-
-enum
-{
-       SHOW,
-       HIDE,
-       POPULATE_CONTEXT,
-
-       /* Actions */
-       ACTIVATE_PROPOSAL,
-       MOVE_CURSOR,
-       MOVE_PAGE,
-
-       N_SIGNALS
-};
+#include "gtksourcesignalgroup-private.h"
+#include "gtksourceview-private.h"
 
-enum
-{
-       PROP_0,
-       PROP_VIEW,
-       PROP_REMEMBER_INFO_VISIBILITY,
-       PROP_SELECT_ON_SHOW,
-       PROP_SHOW_HEADERS,
-       PROP_SHOW_ICONS,
-       PROP_ACCELERATORS,
-       PROP_AUTO_COMPLETE_DELAY,
-       PROP_PROVIDER_PAGE_SIZE,
-       PROP_PROPOSAL_PAGE_SIZE
-};
+#define DEFAULT_PAGE_SIZE 5
 
 struct _GtkSourceCompletion
 {
        GObject parent_instance;
 
-       GtkSourceCompletionInfo *main_window;
-       GtkSourceCompletionInfo *info_window;
+       /* The GtkSourceView that we are providing results for. This can be
+        * used by providers to get a reference.
+        */
+       GtkSourceView *view;
 
-       /* Bottom bar, containing the "Details" button and the selection image
-        * and label. */
-       GtkWidget *bottom_bar;
+       /* A cancellable that we'll monitor to cancel anything that is currently
+        * in-flight. This is reset to a new GCancellable after each time
+        * g_cancellable_cancel() is called.
+        */
+       GCancellable *cancellable;
 
-       /* Image and label in the bottom bar, on the right, for showing which
-        * provider(s) are selected. */
-       GtkImage *selection_image;
-       GtkLabel *selection_label;
+       /* An array of providers that have been registered. These will be queried
+        * when input is provided for completion.
+        */
+       GPtrArray *providers;
 
-       /* Used for insensitive styling */
-       GtkTreeView *insensitive;
+       /* If we are currently performing a completion, the context is stored
+        * here. It will be cleared as soon as it's no longer valid to
+        * (re)display.
+        */
+       GtkSourceCompletionContext *context;
 
-       /* The default widget for the info window */
-       GtkLabel *default_info;
+       /* The signal group is used to track changes to the context while it is
+        * our current context. That includes handling notification of the first
+        * result so that we can show the window, etc.
+        */
+       GtkSourceSignalGroup *context_signals;
 
-       /* The "Details" button, for showing the info window */
-       GtkToggleButton *info_button;
+       /* Signals to changes in the underlying GtkTextBuffer that we use to
+        * determine where and how we can do completion.
+        */
+       GtkSourceSignalGroup *buffer_signals;
 
-       /* List of proposals */
-       GtkTreeView *tree_view_proposals;
+       /* We need to track various events on the view to ensure that we don't
+        * activate at incorrect times.
+        */
+       GtkSourceSignalGroup *view_signals;
 
-       GtkCellRenderer *cell_renderer_proposal;
+       /* The display popover for results */
+       GtkSourceCompletionList *display;
 
-       /* Completion management */
+       /* The completion mark for alignment */
+       GtkTextMark *completion_mark;
 
-       GtkSourceCompletionModel *model_proposals;
+       /* Our current event while processing so that we can get access to it
+        * from a callback back into the completion instance.
+        */
+       const GdkKeyEvent *current_event;
 
-       GList *providers;
+       /* Our cached font description to apply to views. */
+       PangoFontDescription *font_desc;
 
-       GtkSourceCompletionContext *context;
-       GList *active_providers;
-       GList *running_providers;
+       /* If we have a queued update to refilter after deletions, this will be
+        * set to the GSource id.
+        */
+       guint queued_update;
 
-       guint show_timed_out_id;
+       /* This value is incremented/decremented based on if we need to suppress
+        * visibility of the completion window (and avoid doing queries).
+        */
+       guint block_count;
 
-       GtkTextBuffer *buffer;
+       /* Re-entrancy protection for gtk_source_completion_show(). */
+       guint showing;
 
-       GList *auto_completion_selection;
-       GtkSourceCompletionContext *auto_completion_context;
+       /* The number of rows to display. This is propagated to the window if/when
+        * the window is created.
+        */
+       guint page_size;
 
-       /* Number of times the interactive completion is blocked */
-       guint block_interactive_num;
+       /* If we're currently being displayed */
+       guint shown : 1;
 
-       /* Properties */
+       /* If we have a completion actively in play */
+       guint waiting_for_results : 1;
 
-       /* Weak reference to the view. You must check if view != NULL before
-        * using it.
-        */
-       GtkSourceView *view;
-       guint num_accelerators;
-       guint auto_complete_delay;
-       guint proposal_page_size;
-       guint provider_page_size;
+       /* If we should refilter after the in-flight context completes */
+       guint needs_refilter : 1;
 
-       guint remember_info_visibility : 1;
+       /* If the first item is automatically selected */
        guint select_on_show : 1;
-       guint show_headers : 1;
-       guint show_icons : 1;
-};
 
-static guint signals[N_SIGNALS];
+       guint disposed : 1;
+};
 
 G_DEFINE_TYPE (GtkSourceCompletion, gtk_source_completion, G_TYPE_OBJECT)
 
+enum {
+       PROP_0,
+       PROP_BUFFER,
+       PROP_PAGE_SIZE,
+       PROP_SELECT_ON_SHOW,
+       PROP_VIEW,
+       N_PROPS
+};
+
+enum {
+       ACTIVATE,
+       PROVIDER_ADDED,
+       PROVIDER_REMOVED,
+       SHOW,
+       HIDE,
+       N_SIGNALS
+};
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
 static void
-scroll_to_iter (GtkSourceCompletion *completion,
-                GtkTreeIter         *iter)
+display_show (GtkSourceCompletionList *display)
 {
-       GtkTreePath *path;
-       GtkTreeIter prev_iter = *iter;
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (display));
+
+       _gtk_source_completion_list_reposition (display);
+       gtk_widget_show (GTK_WIDGET (display));
+       gtk_widget_grab_focus (GTK_WIDGET (display));
+}
 
-       path = gtk_tree_model_get_path (GTK_TREE_MODEL (completion->model_proposals),
-                                       iter);
+static void
+display_hide (GtkSourceCompletionList *display)
+{
+       GtkWidget *view;
 
-       gtk_tree_view_scroll_to_cell (completion->tree_view_proposals,
-                                     path, NULL,
-                                     FALSE, 0, 0);
-       gtk_tree_path_free (path);
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (display));
 
-       if (gtk_source_completion_model_iter_previous (completion->model_proposals, &prev_iter) &&
-           gtk_source_completion_model_iter_is_header (completion->model_proposals, &prev_iter))
-       {
-               /* If we want to scroll to the first proposal of a provider,
-                * it's better to show the header too, if there is a header.
-                * We first scroll to the proposal, and then to the
-                * header, so we are sure that the proposal is visible.
-                */
+       gtk_widget_hide (GTK_WIDGET (display));
 
-               path = gtk_tree_model_get_path (GTK_TREE_MODEL (completion->model_proposals),
-                                               &prev_iter);
+       view = gtk_widget_get_ancestor (GTK_WIDGET (display), GTK_SOURCE_TYPE_VIEW);
 
-               gtk_tree_view_scroll_to_cell (completion->tree_view_proposals,
-                                             path, NULL,
-                                             FALSE, 0, 0);
-               gtk_tree_path_free (path);
+       if (view != NULL)
+       {
+               gtk_widget_grab_focus (view);
        }
 }
 
-/* Returns %TRUE if a proposal is selected.
- * Call g_object_unref() on @provider and @proposal when no longer needed.
- */
 static gboolean
-get_selected_proposal (GtkSourceCompletion          *completion,
-                      GtkSourceCompletionProvider **provider,
-                      GtkSourceCompletionProposal **proposal)
+gtk_source_completion_is_blocked (GtkSourceCompletion *self)
 {
-       GtkTreeIter iter;
-       GtkTreeSelection *selection;
+       GtkTextBuffer *buffer;
 
-       selection = gtk_tree_view_get_selection (completion->tree_view_proposals);
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
 
-       if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
-       {
-               return FALSE;
-       }
+       return self->block_count > 0 ||
+              self->view == NULL ||
+              self->providers->len == 0 ||
+              !gtk_widget_get_visible (GTK_WIDGET (self->view)) ||
+              !gtk_widget_has_focus (GTK_WIDGET (self->view)) ||
+              !(buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->view))) ||
+              gtk_text_buffer_get_has_selection (buffer) ||
+              !GTK_SOURCE_IS_VIEW (self->view);
+}
 
-       if (gtk_source_completion_model_iter_is_header (completion->model_proposals, &iter))
-       {
-               return FALSE;
-       }
+static PangoFontDescription *
+create_font_description (GtkSourceCompletion *self)
+{
+       PangoFontDescription *font_desc;
+       PangoContext *context;
 
-       if (provider != NULL)
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+
+       if (self->view == NULL)
        {
-               gtk_tree_model_get (GTK_TREE_MODEL (completion->model_proposals), &iter,
-                                   GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROVIDER, provider,
-                                   -1);
+               return NULL;
        }
 
-       if (proposal != NULL)
+       context = gtk_widget_get_pango_context (GTK_WIDGET (self->view));
+       font_desc = pango_font_description_copy (pango_context_get_font_description (context));
+
+       /*
+        * Work around issue where when a proposal provides "<b>markup</b>" and
+        * the weight is set in the font description, the <b> markup will not
+        * have it's weight respected. This seems to be happening because the
+        * weight mask is getting set in pango_font_description_from_string()
+        * even if the the value is set to normal. That matter is complicated
+        * because PangoAttrFontDesc and PangoAttrWeight will both have the
+        * same starting offset in the PangoLayout.
+        * https://bugzilla.gnome.org/show_bug.cgi?id=755968
+        */
+       if (PANGO_WEIGHT_NORMAL == pango_font_description_get_weight (font_desc))
        {
-               gtk_tree_model_get (GTK_TREE_MODEL (completion->model_proposals), &iter,
-                                   GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROPOSAL, proposal,
-                                   -1);
+               pango_font_description_unset_fields (font_desc, PANGO_FONT_MASK_WEIGHT);
        }
 
-       return TRUE;
+       return g_steal_pointer (&font_desc);
 }
 
-/* Returns %TRUE if the first proposal is selected. */
-static gboolean
-check_first_selected (GtkSourceCompletion *completion)
+gboolean
+_gtk_source_completion_get_select_on_show (GtkSourceCompletion *self)
 {
-       GtkTreeSelection *selection;
-       GtkTreeIter iter;
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (self), FALSE);
 
-       if (get_selected_proposal (completion, NULL, NULL) ||
-           !completion->select_on_show)
-       {
-               return FALSE;
-       }
+       return self->select_on_show;
+}
 
-       if (!gtk_source_completion_model_first_proposal (completion->model_proposals, &iter))
-       {
-               return FALSE;
-       }
+static void
+gtk_source_completion_set_select_on_show (GtkSourceCompletion *self,
+                                          gboolean             select_on_show)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
 
-       selection = gtk_tree_view_get_selection (completion->tree_view_proposals);
-       gtk_tree_selection_select_iter (selection, &iter);
-       scroll_to_iter (completion, &iter);
+       select_on_show = !!select_on_show;
 
-       return TRUE;
+       if (self->select_on_show != select_on_show)
+       {
+               self->select_on_show = select_on_show;
+               g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECT_ON_SHOW]);
+       }
 }
 
 static void
-get_iter_at_insert (GtkSourceCompletion *completion,
-                    GtkTextIter         *iter)
+gtk_source_completion_complete_cb (GObject      *object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data)
 {
-       gtk_text_buffer_get_iter_at_mark (completion->buffer,
-                                         iter,
-                                         gtk_text_buffer_get_insert (completion->buffer));
-}
+       GtkSourceCompletionContext *context = (GtkSourceCompletionContext *)object;
+       GtkSourceCompletionList *list;
+       GtkSourceCompletion *self = user_data;
+       GError *error = NULL;
 
-static GList *
-select_providers (GList                      *providers,
-                  GtkSourceCompletionContext *context)
-{
-       GtkTextIter context_iter;
-       GList *selection = NULL;
-       GList *l;
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+       g_assert (G_IS_ASYNC_RESULT (result));
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
 
-       if (!gtk_source_completion_context_get_iter (context, &context_iter))
+       if (self->context == context)
        {
-               return NULL;
+               self->waiting_for_results = FALSE;
        }
 
-       for (l = providers; l != NULL; l = l->next)
+       if (!_gtk_source_completion_context_complete_finish (context, result, &error))
        {
-               GtkSourceCompletionProvider *provider = l->data;
+               g_debug ("Completion failed to complete: %s", error->message);
+               goto cleanup;
+       }
 
-               gboolean good_activation = (gtk_source_completion_provider_get_activation (provider) &
-                                           gtk_source_completion_context_get_activation (context)) != 0;
+       if (context != self->context)
+               goto cleanup;
 
-               if (good_activation &&
-                   gtk_source_completion_provider_match (provider, context))
-               {
-                       selection = g_list_prepend (selection, provider);
-               }
+       if (self->needs_refilter)
+       {
+               /*
+                * At this point, we've gotten our new results for the context. But we had
+                * new content come in since we fired that request. So we need to ask the
+                * providers to further reduce the list based on updated query text.
+                */
+               self->needs_refilter = FALSE;
+               _gtk_source_completion_context_refilter (context);
        }
 
-       return g_list_reverse (selection);
+       list = _gtk_source_completion_get_display (self);
+
+       if (!gtk_source_completion_context_get_empty (context))
+               display_show (list);
+       else
+               display_hide (list);
+
+cleanup:
+       g_clear_error (&error);
+       g_clear_object (&self);
 }
 
-static gint
-minimum_auto_complete_delay (GtkSourceCompletion *completion,
-                             GList               *providers)
+static void
+_gtk_source_completion_set_context (GtkSourceCompletion        *self,
+                                    GtkSourceCompletionContext *context)
 {
-       gint min_delay = completion->auto_complete_delay;
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (!context || GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
 
-       while (providers != NULL)
+       if (g_set_object (&self->context, context))
        {
-               GtkSourceCompletionProvider *provider = providers->data;
-               gint delay = gtk_source_completion_provider_get_interactive_delay (provider);
-
-               if (0 <= delay && delay < min_delay)
-               {
-                       min_delay = delay;
-               }
-
-               providers = g_list_next (providers);
+               g_clear_handle_id (&self->queued_update, g_source_remove);
+               gtk_source_signal_group_set_target (self->context_signals, context);
        }
-
-       return min_delay;
 }
 
 static void
-reset_completion (GtkSourceCompletion *completion)
+gtk_source_completion_cancel (GtkSourceCompletion *self)
 {
-       if (completion->show_timed_out_id != 0)
-       {
-               g_source_remove (completion->show_timed_out_id);
-               completion->show_timed_out_id = 0;
-       }
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
 
-       if (completion->context != NULL)
+       /* Nothing can re-use in-flight results now */
+       self->waiting_for_results = FALSE;
+       self->needs_refilter = FALSE;
+
+       if (self->context != NULL)
        {
-               /* Inform providers of cancellation through the context */
-               _gtk_source_completion_context_cancel (completion->context);
+               g_cancellable_cancel (self->cancellable);
+               g_clear_object (&self->cancellable);
+
+               _gtk_source_completion_set_context (self, NULL);
 
-               g_clear_object (&completion->context);
+               if (self->display != NULL)
+               {
+                       _gtk_source_completion_list_set_context (self->display, NULL);
+                       gtk_widget_hide (GTK_WIDGET (self->display));
+               }
        }
+}
 
-       g_list_free (completion->running_providers);
-       g_list_free (completion->active_providers);
-       completion->running_providers = NULL;
-       completion->active_providers = NULL;
+static inline gboolean
+is_symbol_char (gunichar ch)
+{
+       return ch == '_' || g_unichar_isalnum (ch);
 }
 
-/* A separator is a character like (, a space etc. An _ is not a separator. */
 static gboolean
-is_separator (const gunichar ch)
+gtk_source_completion_compute_bounds (GtkSourceCompletion *self,
+                                      GtkTextIter         *begin,
+                                      GtkTextIter         *end)
 {
-       if (g_unichar_isprint (ch) &&
-           (g_unichar_isalnum (ch) || ch == g_utf8_get_char ("_")))
+       GtkTextBuffer *buffer;
+       GtkTextMark *insert;
+       gunichar ch = 0;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (begin != NULL);
+       g_assert (end != NULL);
+
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->view));
+       insert = gtk_text_buffer_get_insert (buffer);
+       gtk_text_buffer_get_iter_at_mark (buffer, end, insert);
+
+       *begin = *end;
+
+       do
        {
-               return FALSE;
+               if (!gtk_text_iter_backward_char (begin))
+                       break;
+               ch = gtk_text_iter_get_char (begin);
        }
+       while (is_symbol_char (ch));
 
-       return TRUE;
+       if (ch && !is_symbol_char (ch))
+       {
+               gtk_text_iter_forward_char (begin);
+       }
+
+       return !gtk_text_iter_equal (begin, end);
 }
 
-/* Assigns @start_word to the start position of the word, and @end_word to the
- * end position.
- */
 static void
-get_word_iter (GtkTextBuffer *buffer,
-              GtkTextIter   *start_word,
-              GtkTextIter   *end_word)
+gtk_source_completion_start (GtkSourceCompletion           *self,
+                             GtkSourceCompletionActivation  activation)
 {
-       gtk_text_buffer_get_iter_at_mark (buffer,
-                                         end_word,
-                                         gtk_text_buffer_get_insert (buffer));
+       g_autoptr(GtkSourceCompletionContext) context = NULL;
+       GtkTextBuffer *buffer;
+       GtkTextIter begin;
+       GtkTextIter end;
 
-       *start_word = *end_word;
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (self->context == NULL);
 
-       while (gtk_text_iter_backward_char (start_word))
-       {
-               gunichar ch = gtk_text_iter_get_char (start_word);
+       g_clear_handle_id (&self->queued_update, g_source_remove);
 
-               if (is_separator (ch))
-               {
-                       gtk_text_iter_forward_char (start_word);
+       if (!gtk_source_completion_compute_bounds (self, &begin, &end))
+       {
+               if (activation == GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE)
                        return;
-               }
+               begin = end;
        }
-}
 
-static void
-replace_current_word (GtkTextBuffer *buffer,
-                     const gchar   *new_text)
-{
-       GtkTextIter word_start;
-       GtkTextIter word_end;
+       context = _gtk_source_completion_context_new (self);
+       for (guint i = 0; i < self->providers->len; i++)
+               _gtk_source_completion_context_add_provider (context, g_ptr_array_index (self->providers, i));
+       _gtk_source_completion_set_context (self, context);
 
-       get_word_iter (buffer, &word_start, &word_end);
+       self->waiting_for_results = TRUE;
+       self->needs_refilter = FALSE;
 
-       gtk_text_buffer_begin_user_action (buffer);
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->view));
+       gtk_text_buffer_move_mark (buffer, self->completion_mark, &begin);
 
-       gtk_text_buffer_delete (buffer, &word_start, &word_end);
+       _gtk_source_completion_context_complete_async (context,
+                                                      activation,
+                                                      &begin,
+                                                      &end,
+                                                      self->cancellable,
+                                                      gtk_source_completion_complete_cb,
+                                                      g_object_ref (self));
 
-       if (new_text != NULL)
+       if (self->display != NULL)
        {
-               gtk_text_buffer_insert (buffer, &word_start, new_text, -1);
-       }
+               _gtk_source_completion_list_set_context (self->display, context);
 
-       gtk_text_buffer_end_user_action (buffer);
+               if (!gtk_source_completion_context_get_empty (context))
+                       display_show (self->display);
+               else
+                       display_hide (self->display);
+       }
 }
 
 static void
-update_window_position (GtkSourceCompletion *completion)
+gtk_source_completion_update (GtkSourceCompletion           *self,
+                              GtkSourceCompletionActivation  activation)
 {
-       GtkSourceCompletionProvider *provider;
-       GtkSourceCompletionProposal *proposal;
+       GtkTextBuffer *buffer;
+       GtkTextMark *insert;
+       GtkTextIter begin;
+       GtkTextIter end;
        GtkTextIter iter;
-       gboolean iter_set = FALSE;
 
-       if (completion->view == NULL)
-       {
-               return;
-       }
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (self->context != NULL);
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self->context));
 
-       /* The model can be modified while there is no completion context, for
-        * example when the headers are shown or hidden. This triggers a signal
-        * to update the window position, but if there is no completion context,
-        * no need to update the window position (the window is normally hidden
-        * in this case). When a new population is done, this function will be
-        * called again, so no problem.
+       /*
+        * First, find the boundary for the word we are trying to complete. We might
+        * be able to refine a previous query instead of making a new one which can
+        * save on a lot of backend work.
         */
-       if (completion->context == NULL)
+       gtk_source_completion_compute_bounds (self, &begin, &end);
+
+       if (_gtk_source_completion_context_can_refilter (self->context, &begin, &end))
        {
+               GtkSourceCompletionList *display = _gtk_source_completion_get_display (self);
+
+               /*
+                * Make sure we update providers that have already delivered results
+                * even though some of them won't be ready yet.
+                */
+               _gtk_source_completion_context_refilter (self->context);
+
+               /*
+                * If we're waiting for the results still to come in, then just mark
+                * that we need to do post-processing rather than trying to refilter now.
+                */
+               if (self->waiting_for_results)
+               {
+                       self->needs_refilter = TRUE;
+                       return;
+               }
+
+               if (!gtk_source_completion_context_get_empty (self->context))
+                       display_show (display);
+               else
+                       display_hide (display);
+
                return;
        }
 
-       if (get_selected_proposal (completion, &provider, &proposal))
+       if (!gtk_source_completion_context_get_bounds (self->context, &begin, &end) ||
+           gtk_text_iter_equal (&begin, &end))
        {
-               GtkTextIter context_iter;
-               gboolean valid_context;
-
-               valid_context = gtk_source_completion_context_get_iter (completion->context,
-                                                                       &context_iter);
-
-               if (valid_context &&
-                   gtk_source_completion_provider_get_start_iter (provider,
-                                                                  completion->context,
-                                                                  proposal,
-                                                                  &iter))
+               if (activation == GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE)
                {
-                       iter_set = TRUE;
+                       gtk_source_completion_hide (self);
+                       return;
                }
 
-               g_object_unref (provider);
-               g_object_unref (proposal);
+               goto reset;
        }
 
-       if (!iter_set)
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->view));
+       insert = gtk_text_buffer_get_insert (buffer);
+       gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+
+       /*
+        * If our completion prefix bounds match the prefix that we looked
+        * at previously, we can possibly refilter the previous context instead
+        * of creating a new context.
+        */
+
+       /*
+        * The context uses GtkTextMark which should have been advanced as
+        * the user continued to type. So if @end matches @iter (our insert
+        * location), then we can possibly update the previous context by
+        * further refining the query to a subset of the result.
+        */
+       if (gtk_text_iter_equal (&iter, &end))
        {
-               GtkTextIter end_word;
-               get_word_iter (completion->buffer, &iter, &end_word);
+               gtk_source_completion_show (self);
+               return;
        }
 
-       gtk_source_completion_info_move_to_iter (completion->main_window,
-                                                GTK_TEXT_VIEW (completion->view),
-                                                &iter);
+reset:
+       gtk_source_completion_cancel (self);
+       gtk_source_completion_start (self, activation);
 }
 
 static void
-set_info_widget (GtkSourceCompletion *completion,
-                GtkWidget           *new_widget)
+gtk_source_completion_real_hide (GtkSourceCompletion *self)
 {
-       GtkWidget *cur_widget = gtk_window_get_child (GTK_WINDOW (completion->info_window));
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
 
-       if (cur_widget != new_widget)
+       if (self->display != NULL)
        {
-               gtk_window_set_child (GTK_WINDOW (completion->info_window), new_widget);
+               gtk_widget_hide (GTK_WIDGET (self->display));
        }
 }
 
 static void
-update_proposal_info_state (GtkSourceCompletion *completion)
+gtk_source_completion_real_show (GtkSourceCompletion *self)
 {
-       GtkSourceCompletionProvider *provider = NULL;
-       GtkSourceCompletionProposal *proposal = NULL;
-       GtkWidget *info_widget;
+       GtkSourceCompletionList *display;
 
-       if (!get_selected_proposal (completion, &provider, &proposal))
-       {
-               gtk_widget_set_sensitive (GTK_WIDGET (completion->info_button), FALSE);
-               return;
-       }
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
 
-       info_widget = gtk_source_completion_provider_get_info_widget (provider, proposal);
+       display = _gtk_source_completion_get_display (self);
 
-       if (info_widget != NULL)
+       if (self->context == NULL)
        {
-               set_info_widget (completion, info_widget);
-               gtk_widget_set_sensitive (GTK_WIDGET (completion->info_button), TRUE);
-
-               gtk_source_completion_provider_update_info (provider,
-                                                           proposal,
-                                                           info_widget);
+               gtk_source_completion_start (self, GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED);
        }
        else
        {
-               gchar *text = gtk_source_completion_proposal_get_info (proposal);
+               gtk_source_completion_update (self, GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED);
+       }
 
-               if (text != NULL)
-               {
-                       set_info_widget (completion, GTK_WIDGET (completion->default_info));
-                       gtk_widget_set_sensitive (GTK_WIDGET (completion->info_button), TRUE);
+       _gtk_source_completion_list_set_context (display, self->context);
 
-                       gtk_label_set_markup (completion->default_info, text);
-                       g_free (text);
-               }
-               else
-               {
-                       gtk_widget_set_sensitive (GTK_WIDGET (completion->info_button), FALSE);
-               }
-       }
+       if (!gtk_source_completion_context_get_empty (self->context))
+               display_show (display);
+       else
+               display_hide (display);
+}
+
+static gboolean
+gtk_source_completion_queued_update_cb (gpointer user_data)
+{
+  GtkSourceCompletion *self = user_data;
+
+  g_assert (GTK_SOURCE_IS_COMPLETION (self));
+
+  self->queued_update = 0;
+
+  if (self->context != NULL)
+    gtk_source_completion_update (self, GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+gtk_source_completion_queue_update (GtkSourceCompletion *self)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+
+       g_clear_handle_id (&self->queued_update, g_source_remove);
+
+       /*
+        * We hit this code path when the user has deleted text. We want to
+        * introduce just a bit of delay so that deleting under heavy key
+        * repeat will not stall doing lots of refiltering.
+        */
 
-       g_object_unref (provider);
-       g_object_unref (proposal);
+       self->queued_update =
+               g_timeout_add_full (G_PRIORITY_LOW,
+                                   16.7*2 + 1,
+                                   gtk_source_completion_queued_update_cb,
+                                   self,
+                                   NULL);
 }
 
 static void
-update_info_window_visibility (GtkSourceCompletion *completion)
+gtk_source_completion_notify_context_empty_cb (GtkSourceCompletion        *self,
+                                               GParamSpec                 *pspec,
+                                               GtkSourceCompletionContext *context)
 {
-       if (gtk_widget_get_sensitive (GTK_WIDGET (completion->info_button)) &&
-           gtk_toggle_button_get_active (completion->info_button))
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (pspec != NULL);
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+
+       if (context != self->context)
+               return;
+
+       if (gtk_source_completion_context_get_empty (context))
        {
-               gtk_widget_show (GTK_WIDGET (completion->info_window));
+               if (self->display != NULL)
+                       display_hide (self->display);
        }
        else
        {
-               gtk_widget_hide (GTK_WIDGET (completion->info_window));
+               GtkSourceCompletionList *display = _gtk_source_completion_get_display (self);
+               display_show (display);
        }
 }
 
 static void
-update_proposal_info (GtkSourceCompletion *completion)
+gtk_source_completion_buffer_delete_range_after_cb (GtkSourceCompletion *self,
+                                                   GtkTextIter         *begin,
+                                                   GtkTextIter         *end,
+                                                   GtkTextBuffer       *buffer)
 {
-       update_proposal_info_state (completion);
-       update_info_window_visibility (completion);
-}
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (GTK_SOURCE_IS_VIEW (self->view));
+       g_assert (begin != NULL);
+       g_assert (end != NULL);
+       g_assert (GTK_IS_TEXT_BUFFER (buffer));
 
-static void
-gtk_source_completion_show_default (GtkSourceCompletion *completion)
-{
-       if (completion->view == NULL)
+       if (self->context != NULL)
        {
-               return;
-       }
+               if (!gtk_source_completion_is_blocked (self))
+               {
+                       GtkTextIter b, e;
 
-       gtk_widget_show (GTK_WIDGET (completion->main_window));
+                       gtk_source_completion_context_get_bounds (self->context, &b, &e);
 
-       /* Do the autosize when the widget is visible. It doesn't work if it is
-        * done before.
-        */
-       gtk_tree_view_columns_autosize (completion->tree_view_proposals);
+                       /*
+                        * If they just backspaced all of the text, then we want to just hide
+                        * the completion window since that can get a bit intrusive.
+                        */
+                       if (gtk_text_iter_equal (&b, &e))
+                       {
+                               g_clear_handle_id (&self->queued_update, g_source_remove);
+                               gtk_source_completion_cancel (self);
+                               return;
+                       }
 
-       if (!completion->remember_info_visibility)
-       {
-               gtk_toggle_button_set_active (completion->info_button, FALSE);
+                       gtk_source_completion_queue_update (self);
+               }
        }
-
-       update_proposal_info (completion);
-
-       gtk_widget_grab_focus (GTK_WIDGET (completion->view));
 }
 
 static void
-gtk_source_completion_hide_default (GtkSourceCompletion *completion)
-{
-       gtk_widget_hide (GTK_WIDGET (completion->info_window));
-       gtk_widget_hide (GTK_WIDGET (completion->main_window));
-}
+gtk_source_completion_view_move_cursor_cb (GtkSourceCompletion *self,
+                                           GtkMovementStep      step,
+                                           gint                 count,
+                                           gboolean             extend_selection,
+                                           GtkSourceView       *view)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (GTK_SOURCE_IS_VIEW (view));
+
+       /* TODO: Should we keep the context alive while we begin a new one?
+        *       Or rather, how can we avoid the hide/show of the widget that
+        *       could result in flicker?
+        */
 
-static void
-gtk_source_completion_proposals_size_allocate (GtkSourceCompletion *completion,
-                                               GtkAllocation       *allocation,
-                                               GtkWidget           *widget)
-{
-       const gint horizontal_separator = 4; /* From _TREE_VIEW_HORIZONTAL_SEPARATOR */
-       GtkTreeViewColumn *column;
-       gint cell_offset = 0;
-       gint column_offset;
-       gint x_offset = 0;
-       gdouble x_translated;
-
-       if (!gtk_widget_get_realized (GTK_WIDGET (completion->tree_view_proposals)))
+       if (self->display != NULL &&
+           gtk_widget_get_visible (GTK_WIDGET (self->display)))
        {
-               return;
+               gtk_source_completion_cancel (self);
        }
+}
 
-       column = gtk_tree_view_get_column (completion->tree_view_proposals, 1);
-       column_offset = gtk_tree_view_column_get_x_offset (column);
-       gtk_tree_view_column_cell_get_position (column,
-                                               completion->cell_renderer_proposal,
-                                               &cell_offset,
-                                               NULL);
-
-       x_offset = column_offset + cell_offset + horizontal_separator;
-
-       gtk_tree_view_convert_bin_window_to_widget_coords (completion->tree_view_proposals,
-                                                          x_offset,
-                                                          0,
-                                                          &x_offset,
-                                                          NULL);
-       gtk_widget_translate_coordinates (GTK_WIDGET (completion->tree_view_proposals),
-                                         GTK_WIDGET (completion->main_window),
-                                         x_offset,
-                                         0,
-                                         &x_translated,
-                                         NULL);
-
-       _gtk_source_completion_info_set_xoffset (completion->main_window, -x_translated);
+static gboolean
+is_single_char (const gchar *text,
+                gint         len)
+{
+       if (len == 1)
+               return TRUE;
+       else if (len > 6)
+               return FALSE;
+       else
+               return g_utf8_strlen (text, len) == 1;
 }
 
 static void
-gtk_source_completion_activate_proposal (GtkSourceCompletion *completion)
+gtk_source_completion_buffer_insert_text_after_cb (GtkSourceCompletion *self,
+                                                   GtkTextIter         *iter,
+                                                   const gchar         *text,
+                                                   gint                 len,
+                                                   GtkTextBuffer       *buffer)
 {
-       GtkSourceCompletionProvider *provider = NULL;
-       GtkSourceCompletionProposal *proposal = NULL;
-       GtkTextIter insert_iter;
-       GtkTextIter context_iter;
-       gboolean valid_context;
-       gboolean activated;
-
-       if (completion->view == NULL)
+       GtkSourceCompletionActivation activation = GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE;
+       GtkTextIter begin;
+       GtkTextIter end;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (iter != NULL);
+       g_assert (text != NULL);
+       g_assert (len > 0);
+       g_assert (GTK_IS_TEXT_BUFFER (buffer));
+
+       g_clear_handle_id (&self->queued_update, g_source_remove);
+
+       if (gtk_source_completion_is_blocked (self) || !is_single_char (text, len))
        {
+               gtk_source_completion_cancel (self);
                return;
        }
 
-       if (!get_selected_proposal (completion, &provider, &proposal))
+       if (!gtk_source_completion_compute_bounds (self, &begin, &end))
        {
+               GtkTextIter cur = end;
+
+               if (gtk_text_iter_backward_char (&cur))
+               {
+                       gunichar ch = gtk_text_iter_get_char (&cur);
+
+                       for (guint i = 0; i < self->providers->len; i++)
+                       {
+                               GtkSourceCompletionProvider *provider = g_ptr_array_index (self->providers, 
i);
+
+                               if (gtk_source_completion_provider_is_trigger (provider, &end, ch))
+                               {
+                                       /*
+                                        * We got a trigger, but we failed to continue the bounds of a 
previous
+                                        * completion. We need to cancel the previous completion (if any) 
first
+                                        * and then try to start a new completion due to trigger.
+                                        */
+                                       gtk_source_completion_cancel (self);
+                                       activation = GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE;
+                                       goto do_completion;
+                               }
+                       }
+               }
+
+               gtk_source_completion_cancel (self);
                return;
        }
 
-       get_iter_at_insert (completion, &insert_iter);
+do_completion:
 
-       gtk_source_completion_block_interactive (completion);
+       if (self->context == NULL)
+               gtk_source_completion_start (self, activation);
+       else
+               gtk_source_completion_update (self, activation);
+}
 
-       activated = gtk_source_completion_provider_activate_proposal (provider, proposal, &insert_iter);
+static void
+gtk_source_completion_buffer_mark_set_cb (GtkSourceCompletion *self,
+                                          const GtkTextIter   *iter,
+                                          GtkTextMark         *mark,
+                                          GtkTextBuffer       *buffer)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (GTK_IS_TEXT_MARK (mark));
+       g_assert (GTK_IS_TEXT_BUFFER (buffer));
 
-       valid_context = (completion->context != NULL &&
-                        gtk_source_completion_context_get_iter (completion->context,
-                                                                &context_iter));
+       if (mark != gtk_text_buffer_get_insert (buffer))
+               return;
 
-       if (!activated && valid_context)
+       if (_gtk_source_completion_context_iter_invalidates (self->context, iter))
        {
-               GtkTextIter start_iter;
-               gchar *text = gtk_source_completion_proposal_get_text (proposal);
-
-               gboolean has_start = gtk_source_completion_provider_get_start_iter (provider,
-                                                                                   completion->context,
-                                                                                   proposal,
-                                                                                   &start_iter);
-
-               if (has_start)
-               {
-                       gtk_text_buffer_begin_user_action (completion->buffer);
-                       gtk_text_buffer_delete (completion->buffer, &start_iter, &insert_iter);
-                       gtk_text_buffer_insert (completion->buffer, &start_iter, text, -1);
-                       gtk_text_buffer_end_user_action (completion->buffer);
-               }
-               else
-               {
-                       replace_current_word (completion->buffer, text);
-               }
-
-               g_free (text);
-       }
-
-       gtk_source_completion_unblock_interactive (completion);
-
-       gtk_source_completion_hide (completion);
-
-       g_object_unref (provider);
-       g_object_unref (proposal);
-}
-
-static void
-update_info_position (GtkSourceCompletion *completion)
-{
-       GdkSurface *main_surface;
-       GdkSurface *info_surface;
-       GdkSurface *view_surface;
-       GdkPopupLayout *layout;
-       GtkRoot *root;
-       GdkRectangle geom;
-
-       if (!GTK_IS_NATIVE (completion->main_window) ||
-           !(main_surface = gtk_native_get_surface (GTK_NATIVE (completion->main_window))) ||
-           !GTK_IS_NATIVE (completion->info_window) ||
-           !(info_surface = gtk_native_get_surface (GTK_NATIVE (completion->info_window))) ||
-           !(root = gtk_widget_get_root (GTK_WIDGET (completion->view))) ||
-           !GTK_IS_NATIVE (root) ||
-           !(view_surface = gtk_native_get_surface (GTK_NATIVE (root))))
-       {
-               return;
-       }
-
-       geom.x = gdk_popup_get_position_x (GDK_POPUP (main_surface));
-       geom.y = gdk_popup_get_position_y (GDK_POPUP (main_surface));
-       geom.width = gdk_surface_get_width (main_surface);
-       geom.height = gdk_surface_get_height (main_surface);
-
-       layout = gdk_popup_layout_new (&geom, GDK_GRAVITY_NORTH_EAST, GDK_GRAVITY_NORTH_WEST);
-       gdk_popup_layout_set_anchor_hints (layout, GDK_ANCHOR_FLIP_X);
-       gdk_popup_present (GDK_POPUP (info_surface), geom.width, geom.height, layout);
-       gdk_popup_layout_unref (layout);
-}
-
-static GtkSourceCompletionProvider *
-get_visible_provider (GtkSourceCompletion *completion)
-{
-       GList *visible = gtk_source_completion_model_get_visible_providers (completion->model_proposals);
-
-       if (visible != NULL)
-       {
-               return GTK_SOURCE_COMPLETION_PROVIDER (visible->data);
-       }
-       else
-       {
-               return NULL;
-       }
-}
-
-static void
-get_num_visible_providers (GtkSourceCompletion *completion,
-                           guint               *num,
-                           guint               *current)
-{
-       GList *providers = gtk_source_completion_model_get_providers (completion->model_proposals);
-       GtkSourceCompletionProvider *visible = get_visible_provider (completion);
-
-       *num = g_list_length (providers);
-       *current = 0;
-
-       if (visible != NULL)
-       {
-               gint idx = g_list_index (providers, visible);
-               g_return_if_fail (idx != -1);
-
-               *current = idx + 1;
-       }
-
-       g_list_free (providers);
-}
-
-static void
-update_selection_label (GtkSourceCompletion *completion)
-{
-       guint pos;
-       guint num;
-       gchar *name;
-       gchar *selection_text;
-       GtkSourceCompletionProvider *visible;
-
-       get_num_visible_providers (completion, &num, &pos);
-
-       if (num <= 1)
-       {
-               /* At most one provider. All the proposals are shown. */
-               gtk_image_clear (completion->selection_image);
-               gtk_widget_hide (GTK_WIDGET (completion->selection_label));
-               return;
-       }
-
-       visible = get_visible_provider (completion);
-
-       if (visible == NULL)
-       {
-               /* Translators: "All" is used as a label in the status bar of the
-               popup, telling that all completion pages are shown. */
-               name = g_strdup_printf("<b>%s</b>", _("All"));
-
-               gtk_image_clear (completion->selection_image);
-       }
-       else
-       {
-               GdkTexture *texture;
-
-               gchar *temp_name = gtk_source_completion_provider_get_name (visible);
-               name = g_markup_escape_text (temp_name, -1);
-               g_free (temp_name);
-
-               texture = gtk_source_completion_provider_get_icon (visible);
-               gtk_image_set_from_paintable (completion->selection_image, GDK_PAINTABLE (texture));
-       }
-
-       selection_text = g_strdup_printf ("<small>%s (%d/%d)</small>", name, pos + 1, num + 1);
-       gtk_label_set_markup (completion->selection_label, selection_text);
-       gtk_widget_show (GTK_WIDGET (completion->selection_label));
-
-       g_free (selection_text);
-       g_free (name);
-}
-
-static gboolean
-get_next_iter (GtkSourceCompletion *completion,
-              gint                 num,
-              GtkTreeIter         *iter)
-{
-       GtkTreeSelection *selection;
-       gboolean has_selection;
-
-       selection = gtk_tree_view_get_selection (completion->tree_view_proposals);
-       has_selection = gtk_tree_selection_get_selected (selection, NULL, iter);
-
-       if (!has_selection)
-       {
-               return gtk_source_completion_model_first_proposal (completion->model_proposals,
-                                                                  iter);
-       }
-
-       while (num > 0)
-       {
-               if (!gtk_source_completion_model_next_proposal (completion->model_proposals, iter))
-               {
-                       return gtk_source_completion_model_last_proposal (completion->model_proposals,
-                                                                         iter);
-               }
-
-               num--;
-       }
-
-       return TRUE;
-}
-
-static gboolean
-get_previous_iter (GtkSourceCompletion *completion,
-                  gint                 num,
-                  GtkTreeIter         *iter)
-{
-       GtkTreeSelection *selection;
-       gboolean has_selection;
-
-       selection = gtk_tree_view_get_selection (completion->tree_view_proposals);
-       has_selection = gtk_tree_selection_get_selected (selection, NULL, iter);
-
-       if (!has_selection)
-       {
-               return gtk_source_completion_model_last_proposal (completion->model_proposals,
-                                                                 iter);
-       }
-
-       while (num > 0)
-       {
-               if (!gtk_source_completion_model_previous_proposal (completion->model_proposals,
-                                                                   iter))
-               {
-                       return gtk_source_completion_model_first_proposal (completion->model_proposals,
-                                                                          iter);
-               }
-
-               num--;
-       }
-
-       return TRUE;
-}
-
-static void
-gtk_source_completion_move_cursor (GtkSourceCompletion *completion,
-                                   GtkScrollStep        step,
-                                   gint                 num)
-{
-       GtkTreeIter iter;
-       gboolean ok;
-
-       if (step == GTK_SCROLL_ENDS)
-       {
-               if (num > 0)
-               {
-                       ok = gtk_source_completion_model_last_proposal (completion->model_proposals,
-                                                                       &iter);
-               }
-               else
-               {
-                       ok = gtk_source_completion_model_first_proposal (completion->model_proposals,
-                                                                        &iter);
-               }
-       }
-       else
-       {
-               if (step == GTK_SCROLL_PAGES)
-               {
-                       num *= completion->proposal_page_size;
-               }
-
-               if (num > 0)
-               {
-                       ok = get_next_iter (completion, num, &iter);
-               }
-               else
-               {
-                       ok = get_previous_iter (completion, -1 * num, &iter);
-               }
-       }
-
-       if (ok)
-       {
-               GtkTreeSelection *selection;
-
-               selection = gtk_tree_view_get_selection (completion->tree_view_proposals);
-               gtk_tree_selection_select_iter (selection, &iter);
-
-               scroll_to_iter (completion, &iter);
-       }
-}
-
-static GList *
-get_last_provider (GtkSourceCompletion *completion)
-{
-       GList *providers = gtk_source_completion_model_get_providers (completion->model_proposals);
-       GList *ret;
-
-       g_return_val_if_fail (providers != NULL, NULL);
-
-       if (providers->next == NULL)
-       {
-               ret = NULL;
-       }
-       else
-       {
-               ret = g_list_copy (g_list_last (providers));
-       }
-
-       g_list_free (providers);
-       return ret;
-}
-
-static GList *
-providers_cycle_forward (GList *all_providers,
-                        GList *position,
-                        gint   num)
-{
-       GList *l = position;
-       gint i;
-
-       if (all_providers == NULL || all_providers->next == NULL)
-       {
-               return NULL;
-       }
-
-       for (i = 0; i < num; i++)
-       {
-               l = l == NULL ? all_providers : l->next;
-       }
-
-       return l;
-}
-
-static GList *
-providers_cycle_backward (GList *all_providers,
-                         GList *position,
-                         gint   num)
-{
-       gint i;
-       GList *l = position;
-       GList *end = g_list_last (all_providers);
-
-       if (all_providers == NULL || all_providers->next == NULL)
-       {
-               return NULL;
-       }
-
-       for (i = 0; i < num; i++)
-       {
-               l = l == NULL ? end : l->prev;
-       }
-
-       return l;
-}
-
-static GList *
-get_next_provider (GtkSourceCompletion *completion,
-                  gint                 num)
-{
-       GList *providers;
-       GList *visible_providers;
-       GList *position;
-       GList *ret;
-
-       providers = gtk_source_completion_model_get_providers (completion->model_proposals);
-       visible_providers = gtk_source_completion_model_get_visible_providers (completion->model_proposals);
-
-       if (visible_providers == NULL)
-       {
-               position = NULL;
-       }
-       else
-       {
-               position = g_list_find (providers, visible_providers->data);
-       }
-
-       position = providers_cycle_forward (providers, position, num);
-
-       if (position == NULL)
-       {
-               ret = NULL;
-       }
-       else
-       {
-               ret = g_list_append (NULL, position->data);
-       }
-
-       g_list_free (providers);
-
-       return ret;
-}
-
-static GList *
-get_previous_provider (GtkSourceCompletion *completion,
-                      gint                 num)
-{
-       GList *providers;
-       GList *visible_providers;
-       GList *position;
-       GList *ret;
-
-       providers = gtk_source_completion_model_get_providers (completion->model_proposals);
-       visible_providers = gtk_source_completion_model_get_visible_providers (completion->model_proposals);
-
-       if (visible_providers == NULL)
-       {
-               position = NULL;
-       }
-       else
-       {
-               position = g_list_find (providers, visible_providers->data);
-       }
-
-       position = providers_cycle_backward (providers, position, num);
-
-       if (position == NULL)
-       {
-               ret = NULL;
-       }
-       else
-       {
-               ret = g_list_append (NULL, position->data);
-       }
-
-       g_list_free (providers);
-
-       return ret;
-}
-
-static void
-gtk_source_completion_move_page (GtkSourceCompletion *completion,
-                                 GtkScrollStep        step,
-                                 gint                 num)
-{
-       GList *visible_providers = NULL;
-
-       if (step == GTK_SCROLL_ENDS)
-       {
-               if (num > 0)
-               {
-                       visible_providers = get_last_provider (completion);
-               }
-               else
-               {
-                       visible_providers = NULL;
-               }
-       }
-       else
-       {
-               if (step == GTK_SCROLL_PAGES)
-               {
-                       num *= completion->provider_page_size;
-               }
-
-               if (num > 0)
-               {
-                       visible_providers = get_next_provider (completion, num);
-               }
-               else
-               {
-                       visible_providers = get_previous_provider (completion, -1 * num);
-               }
-       }
-
-       gtk_tree_view_set_model (completion->tree_view_proposals, NULL);
-       gtk_tree_view_columns_autosize (completion->tree_view_proposals);
-
-       gtk_source_completion_model_set_visible_providers (completion->model_proposals,
-                                                          visible_providers);
-
-       gtk_tree_view_set_model (completion->tree_view_proposals,
-                                GTK_TREE_MODEL (completion->model_proposals));
-
-       update_selection_label (completion);
-       check_first_selected (completion);
-
-       g_list_free (visible_providers);
-}
-
-/* Begins at 0. Returns -1 if no accelerators available for @iter. */
-static gint
-get_accel_at_iter (GtkSourceCompletion *completion,
-                   GtkTreeIter         *iter)
-{
-       GtkTreeIter it;
-       guint accel;
-
-       if (gtk_source_completion_model_iter_is_header (completion->model_proposals, iter))
-       {
-               return -1;
-       }
-
-       if (!gtk_source_completion_model_first_proposal (completion->model_proposals, &it))
-       {
-               g_return_val_if_reached (-1);
-       }
-
-       for (accel = 0; accel < completion->num_accelerators; accel++)
-       {
-               if (gtk_source_completion_model_iter_equal (completion->model_proposals,
-                                                           iter,
-                                                           &it))
-               {
-                       return accel;
-               }
-
-               if (!gtk_source_completion_model_next_proposal (completion->model_proposals, &it))
-               {
-                       return -1;
-               }
-       }
-
-       return -1;
-}
-
-static void
-render_proposal_accelerator_func (GtkTreeViewColumn   *column,
-                                  GtkCellRenderer     *cell,
-                                  GtkTreeModel        *model,
-                                  GtkTreeIter         *iter,
-                                  GtkSourceCompletion *completion)
-{
-       gint accel = get_accel_at_iter (completion, iter);
-       gchar *text = NULL;
-
-       if (accel != -1)
-       {
-               text = g_strdup_printf ("<small><b>%d</b></small>", (accel + 1) % 10);
-       }
-
-       g_object_set (cell, "markup", text, NULL);
-       g_free (text);
-}
-
-static gboolean
-activate_by_accelerator (GtkSourceCompletion *completion,
-                         gint                 num)
-{
-       GtkTreeSelection *selection;
-       GtkTreeIter iter;
-       gint i;
-
-       if (completion->num_accelerators == 0)
-       {
-               return FALSE;
-       }
-
-       num = num == 0 ? 9 : num - 1;
-
-       if (num < 0 || completion->num_accelerators <= (guint)num)
-       {
-               return FALSE;
-       }
-
-       if (!gtk_source_completion_model_first_proposal (completion->model_proposals, &iter))
-       {
-               return FALSE;
-       }
-
-       for (i = 0; i < num; i++)
-       {
-               if (!gtk_source_completion_model_next_proposal (completion->model_proposals, &iter))
-               {
-                       return FALSE;
-               }
-       }
-
-       selection = gtk_tree_view_get_selection (completion->tree_view_proposals);
-       gtk_tree_selection_select_iter (selection, &iter);
-       gtk_source_completion_activate_proposal (completion);
-
-       return TRUE;
-}
-
-static void
-selection_changed_cb (GtkTreeSelection    *selection,
-                     GtkSourceCompletion *completion)
-{
-       update_proposal_info (completion);
-
-       if (get_selected_proposal (completion, NULL, NULL))
-       {
-               update_window_position (completion);
-       }
-}
-
-static gboolean
-hide_completion_cb (GtkSourceCompletion *completion)
-{
-       gtk_source_completion_hide (completion);
-       return FALSE;
-}
-
-static gboolean
-view_key_press_event_cb (GtkEventController  *key,
-                         guint                keyval,
-                         guint                keycode,
-                         GdkModifierType      state,
-                         GtkSourceCompletion *completion)
-{
-#if 0
-       static gboolean mnemonic_keyval_set = FALSE;
-       static guint mnemonic_keyval = GDK_KEY_VoidSymbol;
-       GdkModifierType mod;
-       GtkBindingSet *binding_set;
-
-       g_assert (GTK_IS_EVENT_CONTROLLER_KEY (key));
-       g_assert (GTK_SOURCE_IS_COMPLETION (completion));
-
-       if (!gtk_widget_get_visible (GTK_WIDGET (completion->main_window)))
-       {
-               return FALSE;
-       }
-
-       if (G_UNLIKELY (!mnemonic_keyval_set))
-       {
-               const gchar *label_text = gtk_button_get_label (GTK_BUTTON (completion->info_button));
-               GtkWidget *label = gtk_label_new_with_mnemonic (label_text);
-               g_object_ref_sink (label);
-
-               mnemonic_keyval = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
-               mnemonic_keyval_set = TRUE;
-
-               g_object_unref (label);
-       }
-
-       mod = gtk_accelerator_get_default_mod_mask () & state;
-
-       /* Handle info button mnemonic */
-       if ((mod & GDK_MOD1_MASK) != 0 &&
-           keyval == mnemonic_keyval &&
-           gtk_widget_get_sensitive (GTK_WIDGET (completion->info_button)))
-       {
-               gtk_toggle_button_set_active (completion->info_button,
-                                             !gtk_toggle_button_get_active (completion->info_button));
-               return TRUE;
-       }
-
-       if ((mod & GDK_MOD1_MASK) != 0 &&
-           GDK_KEY_0 <= keyval &&
-           keyval <= GDK_KEY_9)
-       {
-               if (activate_by_accelerator (completion, keyval - GDK_KEY_0))
-               {
-                       return TRUE;
-               }
-       }
-
-       binding_set = gtk_binding_set_by_class (G_OBJECT_GET_CLASS (completion));
-
-       if (gtk_binding_set_activate (binding_set, keyval, state, G_OBJECT (completion)))
-       {
-               return TRUE;
-       }
-#endif
-
-       return FALSE;
-}
-
-static void
-buffer_mark_set_cb (GtkTextBuffer       *buffer,
-                    GtkTextIter         *iter,
-                    GtkTextMark         *mark,
-                    GtkSourceCompletion *completion)
-{
-       if (mark == gtk_text_buffer_get_insert (buffer))
-       {
-               gtk_source_completion_hide (completion);
-       }
-}
-
-static void
-update_transient_for_info (GObject             *window,
-                           GParamSpec          *spec,
-                           GtkSourceCompletion *completion)
-{
-       gtk_window_set_transient_for (GTK_WINDOW (completion->info_window),
-                                     gtk_window_get_transient_for (GTK_WINDOW (completion->main_window)));
-}
-
-static void
-replace_model (GtkSourceCompletion *completion)
-{
-       if (completion->model_proposals != NULL)
-       {
-               g_object_unref (completion->model_proposals);
-       }
-
-       completion->model_proposals = gtk_source_completion_model_new ();
-
-       gtk_source_completion_model_set_show_headers (completion->model_proposals,
-                                                     completion->show_headers);
-}
-
-/* Takes ownership of @providers and @context. */
-static void
-update_completion (GtkSourceCompletion        *completion,
-                   GList                      *providers,
-                   GtkSourceCompletionContext *context)
-{
-       GList *item;
-       GtkTextIter context_iter;
-       gboolean valid_context;
-
-       /* Copy the parameters, because they can be freed by reset_completion(). */
-       GList *providers_copy = g_list_copy (providers);
-       GtkSourceCompletionContext *context_copy = g_object_ref_sink (context);
-
-       /* Make sure to first cancel any running completion */
-       reset_completion (completion);
-
-       completion->context = context_copy;
-       completion->running_providers = g_list_copy (providers_copy);
-       completion->active_providers = g_list_copy (providers_copy);
-
-       /* Create a new CompletionModel */
-       gtk_tree_view_set_model (completion->tree_view_proposals, NULL);
-       gtk_tree_view_columns_autosize (completion->tree_view_proposals);
-
-       replace_model (completion);
-
-       valid_context = gtk_source_completion_context_get_iter (context_copy, &context_iter);
-
-       if (valid_context)
-       {
-               for (item = providers_copy; item != NULL; item = g_list_next (item))
-               {
-                       GtkSourceCompletionProvider *provider = item->data;
-                       gtk_source_completion_provider_populate (provider, context_copy);
-               }
-       }
-
-       g_list_free (providers_copy);
-}
-
-static gboolean
-auto_completion_final (GtkSourceCompletion *completion)
-{
-       /* Store and set to NULL because update_completion will cancel the last
-          completion, which will also remove the timeout source which in turn
-          would free these guys */
-       GtkSourceCompletionContext *context = completion->auto_completion_context;
-       GList *selection = completion->auto_completion_selection;
-
-       completion->auto_completion_context = NULL;
-       completion->auto_completion_selection = NULL;
-
-       update_completion (completion, selection, context);
-
-       g_list_free (selection);
-       g_object_unref (context);
-       return G_SOURCE_REMOVE;
-}
-
-static void
-auto_completion_destroy (GtkSourceCompletion *completion)
-{
-       if (completion->auto_completion_context != NULL)
-       {
-               g_object_unref (completion->auto_completion_context);
-               completion->auto_completion_context = NULL;
-       }
-
-       g_list_free (completion->auto_completion_selection);
-       completion->auto_completion_selection = NULL;
-}
-
-static void
-start_interactive_completion (GtkSourceCompletion *completion,
-                             GtkTextIter         *iter)
-{
-       GtkSourceCompletionContext *context;
-       GList *providers;
-       gint delay;
-
-       reset_completion (completion);
-
-       /* Create the context */
-       context = gtk_source_completion_create_context (completion, iter);
-       g_object_ref_sink (context);
-
-       g_object_set (context,
-                     "activation",
-                     GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE,
-                     NULL);
-
-       g_signal_emit (completion, signals[POPULATE_CONTEXT], 0, context);
-
-       /* Select providers */
-       providers = select_providers (completion->providers, context);
-
-       if (providers == NULL)
-       {
-               g_object_unref (context);
-               return;
-       }
-
-       /* Create the timeout */
-       delay = minimum_auto_complete_delay (completion, providers);
-       completion->auto_completion_context = context;
-       completion->auto_completion_selection = providers;
-
-       completion->show_timed_out_id =
-               g_timeout_add_full (G_PRIORITY_DEFAULT,
-                                   delay,
-                                   (GSourceFunc)auto_completion_final,
-                                   completion,
-                                   (GDestroyNotify)auto_completion_destroy);
-}
-
-static void
-update_active_completion (GtkSourceCompletion *completion,
-                         GtkTextIter         *new_iter)
-{
-       GList *selected_providers;
-
-       g_assert (completion->context != NULL);
-
-       g_object_set (completion->context,
-                     "iter", new_iter,
-                     NULL);
-
-       selected_providers = select_providers (completion->providers,
-                                              completion->context);
-
-       if (selected_providers != NULL)
-       {
-               update_completion (completion,
-                                  selected_providers,
-                                  completion->context);
-
-               g_list_free (selected_providers);
-       }
-       else
-       {
-               gtk_source_completion_hide (completion);
-       }
-}
-
-static void
-buffer_delete_range_cb (GtkTextBuffer       *buffer,
-                        GtkTextIter         *start,
-                        GtkTextIter         *end,
-                        GtkSourceCompletion *completion)
-{
-       if (completion->context != NULL)
-       {
-               update_active_completion (completion, start);
-       }
-}
-
-static void
-buffer_insert_text_cb (GtkTextBuffer       *buffer,
-                       GtkTextIter         *location,
-                       gchar               *text,
-                       gint                 len,
-                       GtkSourceCompletion *completion)
-{
-       if (completion->context != NULL)
-       {
-               update_active_completion (completion, location);
-       }
-       else
-       {
-               start_interactive_completion (completion, location);
-       }
-}
-
-static void
-update_bottom_bar_visibility (GtkSourceCompletion *completion)
-{
-       GList *providers;
-       guint nb_providers;
-
-       providers = gtk_source_completion_model_get_providers (completion->model_proposals);
-       nb_providers = g_list_length (providers);
-       g_list_free (providers);
-
-       if (nb_providers > 1)
-       {
-               gtk_widget_show (completion->bottom_bar);
-               return;
-       }
-
-       if (gtk_source_completion_model_has_info (completion->model_proposals))
-       {
-               gtk_widget_show (completion->bottom_bar);
-       }
-       else
-       {
-               gtk_widget_hide (completion->bottom_bar);
-       }
-}
-
-static void
-style_context_changed (GtkStyleContext     *style_context,
-                      GtkSourceCompletion *completion)
-{
-       PangoFontDescription *font_desc;
-       PangoContext *context;
-
-       if (completion->view == NULL)
-       {
-               return;
-       }
-
-       context = gtk_widget_get_pango_context (GTK_WIDGET (completion->view));
-       font_desc = pango_context_get_font_description (context);
-
-       /*
-        * Work around issue where when a proposal provides "<b>markup</b>" and
-        * the weight is set in the font description, the <b> markup will not
-        * have it's weight respected. This seems to be happening because the
-        * weight mask is getting set in pango_font_description_from_string()
-        * even if the the value is set to normal. That matter is complicated
-        * because PangoAttrFontDesc and PangoAttrWeight will both have the
-        * same starting offset in the PangoLayout.
-        * https://bugzilla.gnome.org/show_bug.cgi?id=755968
-        */
-       if (PANGO_WEIGHT_NORMAL == pango_font_description_get_weight (font_desc))
-       {
-               font_desc = pango_font_description_copy (font_desc);
-               pango_font_description_unset_fields (font_desc, PANGO_FONT_MASK_WEIGHT);
-               g_object_set (completion->cell_renderer_proposal,
-                             "font-desc", font_desc,
-                             NULL);
-               pango_font_description_free (font_desc);
-       }
-       else
-       {
-               g_object_set (completion->cell_renderer_proposal,
-                             "font-desc", font_desc,
-                             NULL);
-       }
-}
-
-static void
-populating_done (GtkSourceCompletion        *completion,
-                 GtkSourceCompletionContext *context)
-{
-       if (gtk_source_completion_model_is_empty (completion->model_proposals, TRUE))
-       {
-               gtk_source_completion_hide (completion);
-               return;
-       }
-
-       gtk_tree_view_set_model (completion->tree_view_proposals,
-                                GTK_TREE_MODEL (completion->model_proposals));
-
-       update_selection_label (completion);
-       update_bottom_bar_visibility (completion);
-
-       if (!check_first_selected (completion))
-       {
-               /* Update the window position only if the first proposal is not
-                * selected, because if it is selected, the window position will
-                * already be updated.
-                */
-               update_window_position (completion);
-       }
-
-       if (!gtk_widget_get_visible (GTK_WIDGET (completion->main_window)))
-       {
-               g_signal_emit (completion, signals[SHOW], 0);
-       }
-}
-
-static void
-gtk_source_completion_dispose (GObject *object)
-{
-       GtkSourceCompletion *completion = GTK_SOURCE_COMPLETION (object);
-
-       reset_completion (completion);
-
-       if (completion->view != NULL)
-       {
-               g_object_remove_weak_pointer (G_OBJECT (completion->view),
-                                             (gpointer *)&completion->view);
-
-               completion->view = NULL;
-       }
-
-       g_clear_object (&completion->buffer);
-       g_clear_object (&completion->default_info);
-       g_clear_object (&completion->model_proposals);
-
-       if (completion->info_window != NULL)
-       {
-               gtk_window_destroy (GTK_WINDOW (completion->info_window));
-               completion->info_window = NULL;
-       }
-
-       if (completion->main_window != NULL)
-       {
-               gtk_window_destroy (GTK_WINDOW (completion->main_window));
-               completion->main_window = NULL;
-       }
-
-       g_list_free_full (completion->providers, g_object_unref);
-       completion->providers = NULL;
-
-       G_OBJECT_CLASS (gtk_source_completion_parent_class)->dispose (object);
-}
-
-/* Unconditionnally block interactive completion, without taking into account
- * priv->block_interactive_num.
- * g_signal_handlers_block_by_func() has a counter too, so you may think that
- * block_interactive_num is useless. But it is useful when the buffer changes,
- * to keep the signal handler blocked on the new buffer.
- */
-static void
-block_interactive (GtkSourceCompletion *completion)
-{
-       g_signal_handlers_block_by_func (completion->buffer,
-                                        buffer_insert_text_cb,
-                                        completion);
-
-       g_signal_handlers_block_by_func (completion->buffer,
-                                        buffer_delete_range_cb,
-                                        completion);
-}
-
-static void
-connect_buffer (GtkSourceCompletion *completion)
-{
-       GtkTextBuffer *new_buffer = NULL;
-
-       if (completion->view != NULL)
-       {
-               new_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (completion->view));
-       }
-
-       if (completion->buffer == new_buffer)
-       {
-               return;
-       }
-
-       if (completion->buffer != NULL)
-       {
-               g_signal_handlers_disconnect_by_func (completion->buffer,
-                                                     buffer_mark_set_cb,
-                                                     completion);
-
-               g_signal_handlers_disconnect_by_func (completion->buffer,
-                                                     gtk_source_completion_block_interactive,
-                                                     completion);
-
-               g_signal_handlers_disconnect_by_func (completion->buffer,
-                                                     gtk_source_completion_unblock_interactive,
-                                                     completion);
-
-               g_signal_handlers_disconnect_by_func (completion->buffer,
-                                                     buffer_delete_range_cb,
-                                                     completion);
-
-               g_signal_handlers_disconnect_by_func (completion->buffer,
-                                                     buffer_insert_text_cb,
-                                                     completion);
-
-               reset_completion (completion);
-
-               g_object_unref (completion->buffer);
-       }
-
-       completion->buffer = new_buffer;
-
-       if (new_buffer == NULL)
-       {
-               return;
-       }
-
-       g_object_ref (completion->buffer);
-
-       g_signal_connect_object (new_buffer,
-                                "mark-set",
-                                G_CALLBACK (buffer_mark_set_cb),
-                                completion,
-                                G_CONNECT_AFTER);
-
-       g_signal_connect_object (new_buffer,
-                                "undo",
-                                G_CALLBACK (gtk_source_completion_block_interactive),
-                                completion,
-                                G_CONNECT_SWAPPED);
-
-       g_signal_connect_object (new_buffer,
-                                "undo",
-                                G_CALLBACK (gtk_source_completion_unblock_interactive),
-                                completion,
-                                G_CONNECT_SWAPPED | G_CONNECT_AFTER);
-
-       g_signal_connect_object (new_buffer,
-                                "redo",
-                                G_CALLBACK (gtk_source_completion_block_interactive),
-                                completion,
-                                G_CONNECT_SWAPPED);
-
-       g_signal_connect_object (new_buffer,
-                                "redo",
-                                G_CALLBACK (gtk_source_completion_unblock_interactive),
-                                completion,
-                                G_CONNECT_SWAPPED | G_CONNECT_AFTER);
-
-       g_signal_connect_object (new_buffer,
-                                "delete-range",
-                                G_CALLBACK (buffer_delete_range_cb),
-                                completion,
-                                G_CONNECT_AFTER);
-
-       g_signal_connect_object (new_buffer,
-                                "insert-text",
-                                G_CALLBACK (buffer_insert_text_cb),
-                                completion,
-                                G_CONNECT_AFTER);
-
-       if (completion->block_interactive_num > 0)
-       {
-               block_interactive (completion);
-       }
-}
-
-static void
-connect_view (GtkSourceCompletion *completion,
-             GtkSourceView       *view)
-{
-       GtkEventController *key;
-       GtkEventController *focus;
-       GtkGesture *click;
-
-       g_assert (completion->view == NULL);
-       completion->view = view;
-
-       key = gtk_event_controller_key_new ();
-       gtk_widget_add_controller (GTK_WIDGET (view), key);
-
-       click = gtk_gesture_click_new ();
-       gtk_widget_add_controller (GTK_WIDGET (view), GTK_EVENT_CONTROLLER (click));
-
-       focus = gtk_event_controller_focus_new ();
-       gtk_widget_add_controller (GTK_WIDGET (view), GTK_EVENT_CONTROLLER (focus));
-
-       g_object_add_weak_pointer (G_OBJECT (view),
-                                  (gpointer *)&completion->view);
-
-       g_signal_connect_object (click,
-                                "pressed",
-                                G_CALLBACK (hide_completion_cb),
-                                completion,
-                                G_CONNECT_SWAPPED);
-
-       g_signal_connect_object (focus,
-                                "leave",
-                                G_CALLBACK (hide_completion_cb),
-                                completion,
-                                G_CONNECT_SWAPPED);
-
-       g_signal_connect_object (key,
-                                "key-pressed",
-                                G_CALLBACK (view_key_press_event_cb),
-                                completion,
-                                0);
-
-       g_signal_connect_object (completion->view,
-                                "paste-clipboard",
-                                G_CALLBACK (gtk_source_completion_block_interactive),
-                                completion,
-                                G_CONNECT_SWAPPED);
-
-       g_signal_connect_object (completion->view,
-                                "paste-clipboard",
-                                G_CALLBACK (gtk_source_completion_unblock_interactive),
-                                completion,
-                                G_CONNECT_SWAPPED | G_CONNECT_AFTER);
-
-       connect_buffer (completion);
-
-       g_signal_connect_object (completion->view,
-                                "notify::buffer",
-                                G_CALLBACK (connect_buffer),
-                                completion,
-                                G_CONNECT_SWAPPED);
-}
-
-static void
-gtk_source_completion_get_property (GObject    *object,
-                                   guint       prop_id,
-                                   GValue     *value,
-                                   GParamSpec *pspec)
-{
-       GtkSourceCompletion *completion;
-
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (object));
-
-       completion = GTK_SOURCE_COMPLETION (object);
-
-       switch (prop_id)
-       {
-               case PROP_VIEW:
-                       g_value_set_object (value, completion->view);
-                       break;
-               case PROP_REMEMBER_INFO_VISIBILITY:
-                       g_value_set_boolean (value, completion->remember_info_visibility);
-                       break;
-               case PROP_SELECT_ON_SHOW:
-                       g_value_set_boolean (value, completion->select_on_show);
-                       break;
-               case PROP_SHOW_HEADERS:
-                       g_value_set_boolean (value, completion->show_headers);
-                       break;
-               case PROP_SHOW_ICONS:
-                       g_value_set_boolean (value, completion->show_icons);
-                       break;
-               case PROP_ACCELERATORS:
-                       g_value_set_uint (value, completion->num_accelerators);
-                       break;
-               case PROP_AUTO_COMPLETE_DELAY:
-                       g_value_set_uint (value, completion->auto_complete_delay);
-                       break;
-               case PROP_PROPOSAL_PAGE_SIZE:
-                       g_value_set_uint (value, completion->proposal_page_size);
-                       break;
-               case PROP_PROVIDER_PAGE_SIZE:
-                       g_value_set_uint (value, completion->provider_page_size);
-                       break;
-               default:
-                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-                       break;
-       }
-}
-
-static void
-gtk_source_completion_set_property (GObject      *object,
-                                   guint         prop_id,
-                                   const GValue *value,
-                                   GParamSpec   *pspec)
-{
-       GtkSourceCompletion *completion;
-
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (object));
-
-       completion = GTK_SOURCE_COMPLETION (object);
-
-       switch (prop_id)
-       {
-               case PROP_VIEW:
-                       connect_view (completion, g_value_get_object (value));
-                       break;
-               case PROP_REMEMBER_INFO_VISIBILITY:
-                       completion->remember_info_visibility = g_value_get_boolean (value);
-                       break;
-               case PROP_SELECT_ON_SHOW:
-                       completion->select_on_show = g_value_get_boolean (value);
-                       break;
-               case PROP_SHOW_HEADERS:
-                       completion->show_headers = g_value_get_boolean (value);
-
-                       if (completion->model_proposals != NULL)
-                       {
-                               gtk_source_completion_model_set_show_headers (completion->model_proposals,
-                                                                             completion->show_headers);
-                       }
-                       break;
-               case PROP_SHOW_ICONS:
-                       completion->show_icons = g_value_get_boolean (value);
-                       break;
-               case PROP_ACCELERATORS:
-                       completion->num_accelerators = g_value_get_uint (value);
-                       break;
-               case PROP_AUTO_COMPLETE_DELAY:
-                       completion->auto_complete_delay = g_value_get_uint (value);
-                       break;
-               case PROP_PROPOSAL_PAGE_SIZE:
-                       completion->proposal_page_size = g_value_get_uint (value);
-                       break;
-               case PROP_PROVIDER_PAGE_SIZE:
-                       completion->provider_page_size = g_value_get_uint (value);
-                       break;
-               default:
-                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-                       break;
-       }
-}
-
-static gboolean
-selection_func (GtkTreeSelection    *selection,
-                GtkTreeModel        *model,
-                GtkTreePath         *path,
-                gboolean             path_currently_selected,
-                GtkSourceCompletion *completion)
-{
-       GtkTreeIter iter;
-
-       gtk_tree_model_get_iter (model, &iter, path);
-
-       if (gtk_source_completion_model_iter_is_header (completion->model_proposals,
-                                                       &iter))
-       {
-               /* A header must never be selected */
-               g_return_val_if_fail (!path_currently_selected, TRUE);
-               return FALSE;
-       }
-       else
-       {
-               return TRUE;
-       }
-}
-
-static void
-accelerators_notify_cb (GtkSourceCompletion *completion,
-                       GParamSpec          *pspec,
-                       GtkTreeViewColumn   *column)
-{
-       gtk_tree_view_column_set_visible (column, completion->num_accelerators > 0);
-}
-
-static void
-cell_icon_func (GtkTreeViewColumn *column,
-                GtkCellRenderer   *cell,
-                GtkTreeModel      *model,
-                GtkTreeIter       *iter,
-                gpointer           data)
-{
-       GdkTexture *texture;
-       gchar *icon_name;
-       GIcon *gicon;
-       gboolean set = FALSE;
-
-       gtk_tree_model_get (model, iter,
-                           GTK_SOURCE_COMPLETION_MODEL_COLUMN_ICON, &texture,
-                           GTK_SOURCE_COMPLETION_MODEL_COLUMN_ICON_NAME, &icon_name,
-                           GTK_SOURCE_COMPLETION_MODEL_COLUMN_GICON, &gicon,
-                           -1);
-
-       if (texture != NULL)
-       {
-               g_object_set (cell, "texture", texture, NULL);
-               g_object_unref (texture);
-               set = TRUE;
-       }
-
-       if (icon_name != NULL)
-       {
-               g_object_set (cell, "icon-name", icon_name, NULL);
-               g_free (icon_name);
-               set = TRUE;
-       }
-
-       if (gicon != NULL)
-       {
-               g_object_set (cell, "gicon", gicon, NULL);
-               g_object_unref (gicon);
-               set = TRUE;
-       }
-
-       if (!set)
-       {
-               g_object_set (cell, "icon-name", NULL, NULL);
-       }
-}
-
-static void
-init_tree_view (GtkSourceCompletion *completion,
-               GtkBuilder          *builder)
-{
-       GtkTreeSelection *selection;
-       GtkTreeViewColumn *column;
-       GtkCellRenderer *cell_renderer;
-       GtkStyleContext *style_context;
-       GdkRGBA foreground_color;
-
-       completion->insensitive = GTK_TREE_VIEW (gtk_builder_get_object (builder, "insensitive"));
-       completion->tree_view_proposals = GTK_TREE_VIEW (gtk_builder_get_object (builder, 
"tree_view_proposals"));
-
-#if 0
-       g_signal_connect_swapped (completion->tree_view_proposals,
-                                 "row-activated",
-                                 G_CALLBACK (gtk_source_completion_activate_proposal),
-                                 completion);
-
-       g_signal_connect_swapped (completion->tree_view_proposals,
-                                 "size-allocate",
-                                 G_CALLBACK (gtk_source_completion_proposals_size_allocate),
-                                 completion);
-#endif
-
-       /* Selection */
-
-       selection = gtk_tree_view_get_selection (completion->tree_view_proposals);
-
-       gtk_tree_selection_set_select_function (selection,
-                                               (GtkTreeSelectionFunc)selection_func,
-                                               completion,
-                                               NULL);
-
-       g_signal_connect (selection,
-                         "changed",
-                         G_CALLBACK (selection_changed_cb),
-                         completion);
-
-       /* Icon cell renderer */
-
-       cell_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (builder, "cell_renderer_icon"));
-
-       column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (builder, "tree_view_column_icon"));
-
-       /* We use a cell function instead of plain attributes for the icon since
-        * the pixbuf renderer will not renderer any icon if pixbuf is set to NULL.
-        * See https://bugzilla.gnome.org/show_bug.cgi?id=753510
-        */
-       gtk_tree_view_column_set_cell_data_func (column,
-                                                cell_renderer,
-                                                cell_icon_func,
-                                                NULL,
-                                                NULL);
-
-       gtk_tree_view_column_set_attributes (column, cell_renderer,
-                                            "cell-background-set", 
GTK_SOURCE_COMPLETION_MODEL_COLUMN_IS_HEADER,
-                                            NULL);
-
-       /* Get styling for insensitive cell background. Ideally this will all
-        * go away with a widget'ized form of completion before GTK 4.
-        */
-       style_context = gtk_widget_get_style_context (GTK_WIDGET (completion->insensitive));
-       gtk_style_context_get_color (style_context, &foreground_color);
-
-       g_object_bind_property (completion, "show-icons",
-                               cell_renderer, "visible",
-                               G_BINDING_SYNC_CREATE);
-
-       /* Proposal text cell renderer */
-
-       cell_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (builder, "cell_renderer_proposal"));
-       completion->cell_renderer_proposal = cell_renderer;
-
-       column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (builder, "tree_view_column_proposal"));
-
-       gtk_tree_view_column_set_attributes (column, cell_renderer,
-                                            "markup", GTK_SOURCE_COMPLETION_MODEL_COLUMN_MARKUP,
-                                            "cell-background-set", 
GTK_SOURCE_COMPLETION_MODEL_COLUMN_IS_HEADER,
-                                            "foreground-set", GTK_SOURCE_COMPLETION_MODEL_COLUMN_IS_HEADER,
-                                            NULL);
-
-       g_object_set (cell_renderer,
-                     "foreground-rgba", &foreground_color,
-                     NULL);
-
-       /* Accelerators cell renderer */
-
-       column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (builder, "tree_view_column_accelerator"));
-
-       cell_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (builder, "cell_renderer_accelerator"));
-
-       g_object_set (cell_renderer,
-                     "foreground-rgba", &foreground_color,
-                     NULL);
-
-       gtk_tree_view_column_set_cell_data_func (column,
-                                                cell_renderer,
-                                                (GtkTreeCellDataFunc)render_proposal_accelerator_func,
-                                                completion,
-                                                NULL);
+               gtk_source_completion_cancel (self);
+       }
+}
 
-       g_signal_connect_object (completion,
-                                "notify::accelerators",
-                                G_CALLBACK (accelerators_notify_cb),
-                                column,
-                                0);
+GtkSourceCompletion *
+_gtk_source_completion_new (GtkSourceView *view)
+{
+       return g_object_new (GTK_SOURCE_TYPE_COMPLETION,
+                            "view", view,
+                            NULL);
 }
 
 static void
-init_main_window (GtkSourceCompletion *completion,
-                 GtkBuilder          *builder)
+gtk_source_completion_set_view (GtkSourceCompletion *self,
+                                GtkSourceView       *view)
 {
-       if (completion->view == NULL)
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (GTK_SOURCE_IS_VIEW (view));
+
+       if (g_set_weak_pointer (&self->view, view))
        {
-               return;
+               gtk_source_signal_group_set_target (self->view_signals, view);
+               g_object_bind_property (view, "buffer",
+                                       self->buffer_signals, "target",
+                                       G_BINDING_SYNC_CREATE);
        }
-
-       completion->main_window = GTK_SOURCE_COMPLETION_INFO (gtk_builder_get_object (builder, 
"main_window"));
-       completion->info_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "info_button"));
-       completion->selection_image = GTK_IMAGE (gtk_builder_get_object (builder, "selection_image"));
-       completion->selection_label = GTK_LABEL (gtk_builder_get_object (builder, "selection_label"));
-       completion->bottom_bar = GTK_WIDGET (gtk_builder_get_object (builder, "bottom_bar"));
-
-       g_object_set (completion->main_window,
-                     "margin-top", 0,
-                     "margin-bottom", 0,
-                     "margin-start", 0,
-                     "margin-end", 0,
-                     NULL);
-
-#if 0
-       g_signal_connect_swapped (completion->main_window,
-                                 "size-allocate",
-                                 G_CALLBACK (update_window_position),
-                                 completion);
-#endif
-
-       gtk_window_set_hide_on_close (GTK_WINDOW (completion->main_window),
-                                     TRUE);
-
-       g_signal_connect (completion->main_window,
-                         "notify::transient-for",
-                         G_CALLBACK (update_transient_for_info),
-                         completion);
-
-       g_signal_connect_swapped (completion->info_button,
-                                 "toggled",
-                                 G_CALLBACK (update_info_window_visibility),
-                                 completion);
 }
 
 static void
-init_info_window (GtkSourceCompletion *completion)
+on_buffer_signals_bind (GtkSourceCompletion  *self,
+                        GtkSourceBuffer      *buffer,
+                        GtkSourceSignalGroup *signals_)
 {
-       completion->info_window = gtk_source_completion_info_new ();
-       g_object_ref_sink (completion->info_window);
+       GtkTextIter where;
 
-#if 0
-       g_signal_connect_swapped (completion->info_window,
-                                 "size-allocate",
-                                 G_CALLBACK (update_info_position),
-                                 completion);
-#endif
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
+       g_assert (GTK_SOURCE_IS_BUFFER (buffer));
+       g_assert (GTK_SOURCE_IS_SIGNAL_GROUP (signals_));
 
-       /* Default info widget */
+       if (self->disposed)
+               return;
 
-       completion->default_info = GTK_LABEL (gtk_label_new (NULL));
-       g_object_ref_sink (completion->default_info);
+       gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &where);
+       self->completion_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer),
+                                                            NULL,
+                                                            &where,
+                                                            TRUE);
 
-       gtk_widget_show (GTK_WIDGET (completion->default_info));
+       if (self->display != NULL)
+       {
+               _gtk_source_assistant_set_mark (GTK_SOURCE_ASSISTANT (self->display),
+                                               self->completion_mark);
+       }
 }
 
-void
-_gtk_source_completion_css_changed (GtkSourceCompletion *completion,
-                                    GtkCssStyleChange   *change)
+static void
+gtk_source_completion_dispose (GObject *object)
 {
-       GtkStyleContext *style_context;
+       GtkSourceCompletion *self = (GtkSourceCompletion *)object;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION (self));
 
-       g_assert (GTK_SOURCE_IS_COMPLETION (completion));
+       self->disposed = TRUE;
 
-       if (completion->view == NULL)
+       gtk_source_signal_group_set_target (self->context_signals, NULL);
+       gtk_source_signal_group_set_target (self->buffer_signals, NULL);
+       gtk_source_signal_group_set_target (self->view_signals, NULL);
+
+       if (self->display != NULL)
        {
-               return;
+               _gtk_source_assistant_destroy (GTK_SOURCE_ASSISTANT (self->display));
+               self->display = NULL;
        }
 
-       style_context = gtk_widget_get_style_context (GTK_WIDGET (completion->view));
+       g_clear_object (&self->context);
+       g_clear_object (&self->cancellable);
+
+       if (self->providers->len > 0)
+       {
+               g_ptr_array_remove_range (self->providers, 0, self->providers->len);
+       }
 
-       style_context_changed (style_context, completion);
+       G_OBJECT_CLASS (gtk_source_completion_parent_class)->dispose (object);
 }
 
 static void
-gtk_source_completion_constructed (GObject *object)
+gtk_source_completion_finalize (GObject *object)
 {
-       GtkSourceCompletion *completion = GTK_SOURCE_COMPLETION (object);
-       GError *error = NULL;
-       GtkBuilder *builder = gtk_builder_new ();
+       GtkSourceCompletion *self = (GtkSourceCompletion *)object;
+
+       g_clear_weak_pointer (&self->view);
 
-       gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
+       G_OBJECT_CLASS (gtk_source_completion_parent_class)->finalize (object);
+}
 
-       gtk_builder_add_from_resource (builder,
-                                      "/org/gnome/gtksourceview/ui/gtksourcecompletion.ui",
-                                      &error);
+static void
+gtk_source_completion_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+       GtkSourceCompletion *self = GTK_SOURCE_COMPLETION (object);
 
-       if (error != NULL)
+       switch (prop_id)
        {
-               g_error ("Error while loading the completion UI: %s", error->message);
+       case PROP_SELECT_ON_SHOW:
+               g_value_set_boolean (value, _gtk_source_completion_get_select_on_show (self));
+               break;
+
+       case PROP_VIEW:
+               g_value_set_object (value, self->view);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        }
+}
 
-       init_tree_view (completion, builder);
-       init_main_window (completion, builder);
-       init_info_window (completion);
+static void
+gtk_source_completion_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+       GtkSourceCompletion *self = GTK_SOURCE_COMPLETION (object);
 
-       _gtk_source_completion_css_changed (completion, NULL);
+       switch (prop_id)
+       {
+       case PROP_SELECT_ON_SHOW:
+               gtk_source_completion_set_select_on_show (self, g_value_get_boolean (value));
+               break;
 
-       g_object_unref (builder);
+       case PROP_VIEW:
+               gtk_source_completion_set_view (self, g_value_get_object (value));
+               break;
 
-       G_OBJECT_CLASS (gtk_source_completion_parent_class)->constructed (object);
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
 }
 
 static void
@@ -2232,817 +906,623 @@ gtk_source_completion_class_init (GtkSourceCompletionClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+       object_class->dispose = gtk_source_completion_dispose;
+       object_class->finalize = gtk_source_completion_finalize;
        object_class->get_property = gtk_source_completion_get_property;
        object_class->set_property = gtk_source_completion_set_property;
-       object_class->dispose = gtk_source_completion_dispose;
-       object_class->constructed = gtk_source_completion_constructed;
 
        /**
-        * GtkSourceCompletion:view:
+        * GtkSourceCompletion:buffer:
+        *
+        * The #GtkTextBuffer for the #GtkSourceCompletion:view.
+        * This is a convenience property for providers.
         *
-        * The #GtkSourceView bound to the completion object.
+        * Since: 5.0
         */
-       g_object_class_install_property (object_class,
-                                        PROP_VIEW,
-                                        g_param_spec_object ("view",
-                                                             "View",
-                                                             "The GtkSourceView bound to the completion",
-                                                             GTK_SOURCE_TYPE_VIEW,
-                                                             G_PARAM_READWRITE |
-                                                             G_PARAM_CONSTRUCT_ONLY |
-                                                             G_PARAM_STATIC_STRINGS));
+       properties [PROP_BUFFER] =
+               g_param_spec_object ("buffer",
+                                    "Buffer",
+                                    "The buffer for the view",
+                                    GTK_TYPE_TEXT_VIEW,
+                                    G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
        /**
-        * GtkSourceCompletion:remember-info-visibility:
+        * GtkSourceCompletion:page-size:
         *
-        * Determines whether the visibility of the info window should be
-        * saved when the completion is hidden, and restored when the completion
-        * is shown again.
-        */
-       g_object_class_install_property (object_class,
-                                        PROP_REMEMBER_INFO_VISIBILITY,
-                                        g_param_spec_boolean ("remember-info-visibility",
-                                                              "Remember Info Visibility",
-                                                              "Remember the last info window visibility 
state",
-                                                              FALSE,
-                                                              G_PARAM_READWRITE |
-                                                              G_PARAM_CONSTRUCT |
-                                                              G_PARAM_STATIC_STRINGS));
-       /**
-        * GtkSourceCompletion:select-on-show:
+        * The number of rows to display to the user before scrolling.
         *
-        * Determines whether the first proposal should be selected when the
-        * completion is first shown.
+        * Since: 5.0
         */
-       g_object_class_install_property (object_class,
-                                        PROP_SELECT_ON_SHOW,
-                                        g_param_spec_boolean ("select-on-show",
-                                                              "Select on Show",
-                                                              "Select first proposal when completion is 
shown",
-                                                              TRUE,
-                                                              G_PARAM_READWRITE |
-                                                              G_PARAM_CONSTRUCT |
-                                                              G_PARAM_STATIC_STRINGS));
+       properties [PROP_PAGE_SIZE] =
+               g_param_spec_uint ("page-size",
+                                  "Number of Rows",
+                                  "Number of rows to display to the user",
+                                  1, 32, DEFAULT_PAGE_SIZE,
+                                  G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
 
        /**
-        * GtkSourceCompletion:show-headers:
+        * GtkSourceCompletion:select-on-show:
         *
-        * Determines whether provider headers should be shown in the proposal
-        * list. It can be useful to disable when there is only one provider.
+        * Determines whether the first proposal should be selected when the completion
+        * is first shown.
         */
-       g_object_class_install_property (object_class,
-                                        PROP_SHOW_HEADERS,
-                                        g_param_spec_boolean ("show-headers",
-                                                              "Show Headers",
-                                                              "Show provider headers when proposals from 
multiple providers are available",
-                                                              TRUE,
-                                                              G_PARAM_READWRITE |
-                                                              G_PARAM_CONSTRUCT |
-                                                              G_PARAM_STATIC_STRINGS));
+       properties [PROP_SELECT_ON_SHOW] =
+               g_param_spec_boolean ("select-on-show",
+                                     "Select on Show",
+                                     "Select on Show",
+                                     FALSE,
+                                     (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
        /**
-        * GtkSourceCompletion:show-icons:
+        * GtkSourceCompletion:view:
         *
-        * Determines whether provider and proposal icons should be shown in
-        * the completion popup.
-        */
-       g_object_class_install_property (object_class,
-                                        PROP_SHOW_ICONS,
-                                        g_param_spec_boolean ("show-icons",
-                                                              "Show Icons",
-                                                              "Show provider and proposal icons in the 
completion popup",
-                                                              TRUE,
-                                                              G_PARAM_READWRITE |
-                                                              G_PARAM_CONSTRUCT |
-                                                              G_PARAM_STATIC_STRINGS));
-
-       /**
-        * GtkSourceCompletion:accelerators:
+        * The "view" property is the #GtkTextView for which this #GtkSourceCompletion
+        * is providing completion features.
         *
-        * Number of keyboard accelerators to show for the first proposals. For
-        * example, to activate the first proposal, the user can press
-        * <keycombo><keycap>Alt</keycap><keycap>1</keycap></keycombo>.
+        * Since: 5.0
         */
-       g_object_class_install_property (object_class,
-                                        PROP_ACCELERATORS,
-                                        g_param_spec_uint ("accelerators",
-                                                           "Accelerators",
-                                                           "Number of proposal accelerators to show",
-                                                           0,
-                                                           10,
-                                                           5,
-                                                           G_PARAM_READWRITE |
-                                                           G_PARAM_CONSTRUCT |
-                                                           G_PARAM_STATIC_STRINGS));
+       properties [PROP_VIEW] =
+       g_param_spec_object ("view",
+                            "View",
+                            "The text view for which to provide completion",
+                            GTK_SOURCE_TYPE_VIEW,
+                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+       g_object_class_install_properties (object_class, N_PROPS, properties);
 
        /**
-        * GtkSourceCompletion:auto-complete-delay:
+        * GtkSourceCompletion::provider-added:
+        * @self: an #ideCompletion
+        * @provider: an #GtkSourceCompletionProvider
+        *
+        * The "provided-added" signal is emitted when a new provider is
+        * added to the completion.
         *
-        * Determines the popup delay (in milliseconds) at which the completion
-        * will be shown for interactive completion.
+        * Since: 5.0
         */
-       g_object_class_install_property (object_class,
-                                        PROP_AUTO_COMPLETE_DELAY,
-                                        g_param_spec_uint ("auto-complete-delay",
-                                                           "Auto Complete Delay",
-                                                           "Completion popup delay for interactive 
completion",
-                                                           0,
-                                                           G_MAXUINT,
-                                                           250,
-                                                           G_PARAM_READWRITE |
-                                                           G_PARAM_CONSTRUCT |
-                                                           G_PARAM_STATIC_STRINGS));
+       signals [PROVIDER_ADDED] =
+               g_signal_new ("provider-added",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0, NULL, NULL,
+                             g_cclosure_marshal_VOID__OBJECT,
+                             G_TYPE_NONE, 1, GTK_SOURCE_TYPE_COMPLETION_PROVIDER);
+       g_signal_set_va_marshaller (signals [PROVIDER_ADDED],
+                                   G_TYPE_FROM_CLASS (klass),
+                                   g_cclosure_marshal_VOID__OBJECTv);
 
        /**
-        * GtkSourceCompletion:provider-page-size:
+        * GtkSourceCompletion::provider-removed:
+        * @self: an #ideCompletion
+        * @provider: an #GtkSourceCompletionProvider
         *
-        * The scroll page size of the provider pages in the completion window.
+        * The "provided-removed" signal is emitted when a provider has
+        * been removed from the completion.
         *
-        * See the #GtkSourceCompletion::move-page signal.
+        * Since: 5.0
         */
-       g_object_class_install_property (object_class,
-                                        PROP_PROVIDER_PAGE_SIZE,
-                                        g_param_spec_uint ("provider-page-size",
-                                                           "Provider Page Size",
-                                                           "Provider scrolling page size",
-                                                           1,
-                                                           G_MAXUINT,
-                                                           5,
-                                                           G_PARAM_READWRITE |
-                                                           G_PARAM_CONSTRUCT |
-                                                           G_PARAM_STATIC_STRINGS));
+       signals [PROVIDER_REMOVED] =
+               g_signal_new ("provider-removed",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0, NULL, NULL,
+                             g_cclosure_marshal_VOID__OBJECT,
+                             G_TYPE_NONE, 1, GTK_SOURCE_TYPE_COMPLETION_PROVIDER);
+       g_signal_set_va_marshaller (signals [PROVIDER_REMOVED],
+                                   G_TYPE_FROM_CLASS (klass),
+                                   g_cclosure_marshal_VOID__OBJECTv);
 
        /**
-        * GtkSourceCompletion:proposal-page-size:
+        * GtkSourceCompletion::hide:
+        * @self: an #GtkSourceCompletion
         *
-        * The scroll page size of the proposals in the completion window. In
-        * other words, when <keycap>PageDown</keycap> or
-        * <keycap>PageUp</keycap> is pressed, the selected
-        * proposal becomes the one which is located one page size backward or
-        * forward.
+        * The "hide" signal is emitted when the completion window should
+        * be hidden.
         *
-        * See also the #GtkSourceCompletion::move-cursor signal.
+        * Since: 5.0
         */
-       g_object_class_install_property (object_class,
-                                        PROP_PROPOSAL_PAGE_SIZE,
-                                        g_param_spec_uint ("proposal-page-size",
-                                                           "Proposal Page Size",
-                                                           "Proposal scrolling page size",
-                                                           1,
-                                                           G_MAXUINT,
-                                                           5,
-                                                           G_PARAM_READWRITE |
-                                                           G_PARAM_CONSTRUCT |
-                                                           G_PARAM_STATIC_STRINGS));
+       signals [HIDE] =
+               g_signal_new_class_handler ("hide",
+                                           G_TYPE_FROM_CLASS (klass),
+                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                           G_CALLBACK (gtk_source_completion_real_hide),
+                                           NULL, NULL,
+                                           g_cclosure_marshal_VOID__VOID,
+                                           G_TYPE_NONE, 0);
+       g_signal_set_va_marshaller (signals [HIDE],
+                                   G_TYPE_FROM_CLASS (klass),
+                                   g_cclosure_marshal_VOID__VOIDv);
 
        /**
         * GtkSourceCompletion::show:
-        * @completion: The #GtkSourceCompletion who emits the signal
+        * @self: an #GtkSourceCompletion
         *
-        * Emitted when the completion window is shown. The default handler
-        * will actually show the window.
+        * The "show" signal is emitted when the completion window should
+        * be shown.
+        *
+        * Since: 5.0
         */
-       signals[SHOW] =
+       signals [SHOW] =
                g_signal_new_class_handler ("show",
-                                           G_TYPE_FROM_CLASS (klass),
-                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                           G_CALLBACK (gtk_source_completion_show_default),
-                                           NULL, NULL, NULL, G_TYPE_NONE, 0);
-
+                                           G_TYPE_FROM_CLASS (klass),
+                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                           G_CALLBACK (gtk_source_completion_real_show),
+                                           NULL, NULL,
+                                           g_cclosure_marshal_VOID__VOID,
+                                           G_TYPE_NONE, 0);
+       g_signal_set_va_marshaller (signals [SHOW],
+                                   G_TYPE_FROM_CLASS (klass),
+                                   g_cclosure_marshal_VOID__VOIDv);
+}
 
-       /**
-        * GtkSourceCompletion::hide:
-        * @completion: The #GtkSourceCompletion who emits the signal
-        *
-        * Emitted when the completion window is hidden. The default handler
-        * will actually hide the window.
-        */
-       signals[HIDE] =
-               g_signal_new_class_handler ("hide",
-                                           G_TYPE_FROM_CLASS (klass),
-                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                           G_CALLBACK (gtk_source_completion_hide_default),
-                                           NULL, NULL, NULL, G_TYPE_NONE, 0);
+static void
+gtk_source_completion_init (GtkSourceCompletion *self)
+{
+       self->cancellable = g_cancellable_new ();
+       self->providers = g_ptr_array_new_with_free_func (g_object_unref);
+       self->buffer_signals = gtk_source_signal_group_new (GTK_TYPE_TEXT_BUFFER);
+       self->context_signals = gtk_source_signal_group_new (GTK_SOURCE_TYPE_COMPLETION_CONTEXT);
+       self->view_signals = gtk_source_signal_group_new (GTK_SOURCE_TYPE_VIEW);
+       self->page_size = DEFAULT_PAGE_SIZE;
 
-       /**
-        * GtkSourceCompletion::populate-context:
-        * @completion: The #GtkSourceCompletion who emits the signal
-        * @context: The #GtkSourceCompletionContext for the current completion
-        *
-        * Emitted just before starting to populate the completion with providers.
-        * You can use this signal to add additional attributes in the context.
+       /*
+        * We want to be notified when the context switches from no results to
+        * having results (or vice-versa, when we've filtered to the point of
+        * no results).
         */
-       signals[POPULATE_CONTEXT] =
-               g_signal_new_class_handler ("populate-context",
-                                           G_TYPE_FROM_CLASS (klass),
-                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                           NULL,
-                                           NULL, NULL, NULL,
-                                           G_TYPE_NONE,
-                                           1,
-                                           GTK_SOURCE_TYPE_COMPLETION_CONTEXT);
-
-       /* Actions */
+       gtk_source_signal_group_connect_object (self->context_signals,
+                                               "notify::empty",
+                                               G_CALLBACK (gtk_source_completion_notify_context_empty_cb),
+                                               self,
+                                               G_CONNECT_SWAPPED);
 
-       /**
-        * GtkSourceCompletion::move-cursor:
-        * @completion: The #GtkSourceCompletion who emits the signal
-        * @step: The #GtkScrollStep by which to move the cursor
-        * @num: The amount of steps to move the cursor
-        *
-        * The #GtkSourceCompletion::move-cursor signal is a keybinding
-        * signal which gets emitted when the user initiates a cursor
-        * movement.
-        *
-        * The <keycap>Up</keycap>, <keycap>Down</keycap>,
-        * <keycap>PageUp</keycap>, <keycap>PageDown</keycap>,
-        * <keycap>Home</keycap> and <keycap>End</keycap> keys are bound to the
-        * normal behavior expected by those keys.
-        *
-        * When @step is equal to %GTK_SCROLL_PAGES, the page size is defined by
-        * the #GtkSourceCompletion:proposal-page-size property. It is used for
-        * the <keycap>PageDown</keycap> and <keycap>PageUp</keycap> keys.
-        *
-        * Applications should not connect to it, but may emit it with
-        * g_signal_emit_by_name() if they need to control the cursor
-        * programmatically.
+       /*
+        * We need to know when the buffer inserts or deletes text so that we
+        * possibly start showing the results, or update our previous completion
+        * request.
         */
-       signals[MOVE_CURSOR] =
-               g_signal_new_class_handler ("move-cursor",
-                                           G_TYPE_FROM_CLASS (klass),
-                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                           G_CALLBACK (gtk_source_completion_move_cursor),
-                                           NULL, NULL,
-                                           _gtk_source_marshal_VOID__ENUM_INT,
-                                           G_TYPE_NONE,
-                                           2,
-                                           GTK_TYPE_SCROLL_STEP,
-                                           G_TYPE_INT);
-       g_signal_set_va_marshaller (signals [MOVE_CURSOR],
-                                   G_TYPE_FROM_CLASS (klass),
-                                   _gtk_source_marshal_VOID__ENUM_INTv);
+       g_signal_connect_object (self->buffer_signals,
+                                "bind",
+                                G_CALLBACK (on_buffer_signals_bind),
+                                self,
+                                G_CONNECT_SWAPPED);
+       gtk_source_signal_group_connect_object (self->buffer_signals,
+                                               "delete-range",
+                                               G_CALLBACK 
(gtk_source_completion_buffer_delete_range_after_cb),
+                                               self,
+                                               G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+       gtk_source_signal_group_connect_object (self->buffer_signals,
+                                               "insert-text",
+                                               G_CALLBACK 
(gtk_source_completion_buffer_insert_text_after_cb),
+                                               self,
+                                               G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+       gtk_source_signal_group_connect_object (self->buffer_signals,
+                                               "mark-set",
+                                               G_CALLBACK (gtk_source_completion_buffer_mark_set_cb),
+                                               self,
+                                               G_CONNECT_SWAPPED);
 
-       /**
-        * GtkSourceCompletion::move-page:
-        * @completion: The #GtkSourceCompletion who emits the signal
-        * @step: The #GtkScrollStep by which to move the page
-        * @num: The amount of steps to move the page
-        *
-        * The #GtkSourceCompletion::move-page signal is a keybinding
-        * signal which gets emitted when the user initiates a page
-        * movement (i.e. switches between provider pages).
-        *
-        * <keycombo><keycap>Control</keycap><keycap>Left</keycap></keycombo>
-        * is for going to the previous provider.
-        * <keycombo><keycap>Control</keycap><keycap>Right</keycap></keycombo>
-        * is for going to the next provider.
-        * <keycombo><keycap>Control</keycap><keycap>Home</keycap></keycombo>
-        * is for displaying all the providers.
-        * <keycombo><keycap>Control</keycap><keycap>End</keycap></keycombo>
-        * is for going to the last provider.
-        *
-        * When @step is equal to #GTK_SCROLL_PAGES, the page size is defined by
-        * the #GtkSourceCompletion:provider-page-size property.
-        *
-        * Applications should not connect to it, but may emit it with
-        * g_signal_emit_by_name() if they need to control the page selection
-        * programmatically.
+       /*
+        * We track some events on the view that owns our GtkSourceCompletion instance so
+        * that we can hide the window when it definitely should not be displayed.
         */
-       signals[MOVE_PAGE] =
-               g_signal_new_class_handler ("move-page",
-                                           G_TYPE_FROM_CLASS (klass),
-                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                           G_CALLBACK (gtk_source_completion_move_page),
-                                           NULL, NULL,
-                                           _gtk_source_marshal_VOID__ENUM_INT,
-                                           G_TYPE_NONE,
-                                           2,
-                                           GTK_TYPE_SCROLL_STEP,
-                                           G_TYPE_INT);
-       g_signal_set_va_marshaller (signals [MOVE_PAGE],
-                                   G_TYPE_FROM_CLASS (klass),
-                                   _gtk_source_marshal_VOID__ENUM_INTv);
+       gtk_source_signal_group_connect_object (self->view_signals,
+                                               "move-cursor",
+                                               G_CALLBACK (gtk_source_completion_view_move_cursor_cb),
+                                               self,
+                                               G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+       gtk_source_signal_group_connect_object (self->view_signals,
+                                               "paste-clipboard",
+                                               G_CALLBACK (gtk_source_completion_block_interactive),
+                                               self,
+                                               G_CONNECT_SWAPPED);
+       gtk_source_signal_group_connect_object (self->view_signals,
+                                               "paste-clipboard",
+                                               G_CALLBACK (gtk_source_completion_unblock_interactive),
+                                               self,
+                                               G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+}
 
-       /**
-        * GtkSourceCompletion::activate-proposal:
-        * @completion: The #GtkSourceCompletion who emits the signal
-        *
-        * The #GtkSourceCompletion::activate-proposal signal is a
-        * keybinding signal which gets emitted when the user initiates
-        * a proposal activation.
-        *
-        * Applications should not connect to it, but may emit it with
-        * g_signal_emit_by_name() if they need to control the proposal
-        * activation programmatically.
-        */
-       signals[ACTIVATE_PROPOSAL] =
-               g_signal_new_class_handler ("activate-proposal",
-                                           G_TYPE_FROM_CLASS (klass),
-                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                           G_CALLBACK (gtk_source_completion_activate_proposal),
-                                           NULL, NULL, NULL, G_TYPE_NONE, 0);
-
-#if 0
-
-       /* XXX: We cannot do keybindings with GObject's any more. This will
-        * need to be redesigned to keep events happening either from the
-        * completion info class or from the sourceview itself.
-        */
+/**
+ * gtk_source_completion_get_view:
+ * @self: a #GtkSourceCompletion
+ *
+ * Gets the #GtkSourceView that owns the #GtkSourceCompletion.
+ *
+ * Returns: (transfer none): A #GtkSourceView
+ *
+ * Since: 5.0
+ */
+GtkSourceView *
+gtk_source_completion_get_view (GtkSourceCompletion *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (self), NULL);
 
-       /* Key bindings */
-       binding_set = gtk_binding_set_by_class (klass);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Down,
-                                     0,
-                                     "move-cursor",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_STEPS,
-                                     G_TYPE_INT, 1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Page_Down,
-                                     0,
-                                     "move-cursor",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_PAGES,
-                                     G_TYPE_INT, 1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Up,
-                                     0,
-                                     "move-cursor",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_STEPS,
-                                     G_TYPE_INT, -1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Page_Up,
-                                     0,
-                                     "move-cursor",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_PAGES,
-                                     G_TYPE_INT, -1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Home,
-                                     0,
-                                     "move-cursor",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_ENDS,
-                                     G_TYPE_INT, -1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_End,
-                                     0,
-                                     "move-cursor",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_ENDS,
-                                     G_TYPE_INT, 1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Escape,
-                                     0,
-                                     "hide",
-                                     0);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Return,
-                                     0,
-                                     "activate-proposal",
-                                     0);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Tab,
-                                     0,
-                                     "activate-proposal",
-                                     0);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Left,
-                                     GDK_CONTROL_MASK,
-                                     "move-page",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_STEPS,
-                                     G_TYPE_INT, -1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Right,
-                                     GDK_CONTROL_MASK,
-                                     "move-page",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_STEPS,
-                                     G_TYPE_INT, 1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_Home,
-                                     GDK_CONTROL_MASK,
-                                     "move-page",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_ENDS,
-                                     G_TYPE_INT, -1);
-
-       gtk_binding_entry_add_signal (binding_set,
-                                     GDK_KEY_End,
-                                     GDK_CONTROL_MASK,
-                                     "move-page",
-                                     2,
-                                     GTK_TYPE_SCROLL_STEP, GTK_SCROLL_ENDS,
-                                     G_TYPE_INT, 1);
-#endif
-
-       g_type_ensure (GTK_SOURCE_TYPE_COMPLETION_INFO);
+       return self->view;
 }
 
-static void
-gtk_source_completion_init (GtkSourceCompletion *completion)
+/**
+ * gtk_source_completion_get_buffer:
+ * @self: a #GtkSourceCompletion
+ *
+ * Gets the connected #GtkSourceView's #GtkSourceBuffer
+ *
+ * Returns: (transfer none): A #GtkSourceBuffer
+ *
+ * Since: 5.0
+ */
+GtkSourceBuffer *
+gtk_source_completion_get_buffer (GtkSourceCompletion *self)
 {
-       completion = gtk_source_completion_get_instance_private (completion);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (self), NULL);
+
+       return GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->view)));
 }
 
+/**
+ * gtk_source_completion_add_provider:
+ * @self: an #GtkSourceCompletion
+ * @provider: an #GtkSourceCompletionProvider
+ *
+ * Adds an #GtkSourceCompletionProvider to the list of providers to be queried
+ * for completion results.
+ *
+ * Since: 5.0
+ */
 void
-_gtk_source_completion_add_proposals (GtkSourceCompletion         *completion,
-                                      GtkSourceCompletionContext  *context,
-                                      GtkSourceCompletionProvider *provider,
-                                      GList                       *proposals,
-                                      gboolean                     finished)
+gtk_source_completion_add_provider (GtkSourceCompletion         *self,
+                                    GtkSourceCompletionProvider *provider)
 {
-       GList *item;
-
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (completion));
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
        g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
-       g_return_if_fail (completion->context == context);
-
-       item = g_list_find (completion->running_providers, provider);
-       g_return_if_fail (item != NULL);
 
-       gtk_source_completion_model_add_proposals (completion->model_proposals,
-                                                  provider,
-                                                  proposals);
-
-       if (finished)
-       {
-               /* Remove provider from list of running providers */
-               completion->running_providers =
-                       g_list_delete_link (completion->running_providers,
-                                           item);
-
-               if (completion->running_providers == NULL)
-               {
-                       populating_done (completion, context);
-               }
-       }
+       g_ptr_array_add (self->providers, g_object_ref (provider));
+       g_signal_emit (self, signals [PROVIDER_ADDED], 0, provider);
 }
 
 /**
- * gtk_source_completion_start:
- * @completion: a #GtkSourceCompletion.
- * @providers: (element-type GtkSource.CompletionProvider) (nullable):
- * a list of #GtkSourceCompletionProvider, or %NULL.
- * @context: (transfer floating): The #GtkSourceCompletionContext
- * with which to start the completion.
- *
- * Starts a new completion with the specified #GtkSourceCompletionContext and
- * a list of potential candidate providers for completion.
- *
- * It can be convenient for showing a completion on-the-fly, without the need to
- * add or remove providers to the #GtkSourceCompletion.
+ * gtk_source_completion_remove_provider:
+ * @self: an #GtkSourceCompletion
+ * @provider: an #GtkSourceCompletionProvider
  *
- * Another solution is to add providers with
- * gtk_source_completion_add_provider(), and implement
- * gtk_source_completion_provider_match() for each provider.
+ * Removes an #GtkSourceCompletionProvider previously added with
+ * gtk_source_completion_add_provider().
  *
- * Returns: %TRUE if it was possible to the show completion window.
+ * Since: 5.0
  */
-gboolean
-gtk_source_completion_start (GtkSourceCompletion        *completion,
-                            GList                      *providers,
-                            GtkSourceCompletionContext *context)
+void
+gtk_source_completion_remove_provider (GtkSourceCompletion         *self,
+                                       GtkSourceCompletionProvider *provider)
 {
-       GList *selected_providers;
-
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), FALSE);
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), FALSE);
-
-       if (completion->view == NULL)
-       {
-               return FALSE;
-       }
+       GtkSourceCompletionProvider *hold = NULL;
 
-       /* Make sure to clear any active completion */
-       reset_completion (completion);
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
 
-       /* We need to take owenership of the context right before doing
-          anything so we don't leak it or get a crash emitting the signal */
-       g_object_ref_sink (context);
+       hold = g_object_ref (provider);
 
-       if (providers == NULL)
+       if (g_ptr_array_remove (self->providers, provider))
        {
-               g_object_unref (context);
-
-               return FALSE;
+               g_signal_emit (self, signals [PROVIDER_REMOVED], 0, hold);
        }
 
-       /* Populate the context */
-       g_signal_emit (completion, signals[POPULATE_CONTEXT], 0, context);
-
-       /* From the providers, select the ones that match the context */
-       selected_providers = select_providers (providers, context);
+       g_clear_object (&hold);
+}
 
-       if (selected_providers == NULL)
-       {
-               g_object_unref (context);
-               gtk_source_completion_hide (completion);
-               return FALSE;
-       }
+/**
+ * gtk_source_completion_show:
+ * @self: an #GtkSourceCompletion
+ *
+ * Emits the "show" signal.
+ *
+ * When the "show" signal is emitted, the completion window will be
+ * displayed if there are any results available.
+ *
+ * Since: 5.0
+ */
+void
+gtk_source_completion_show (GtkSourceCompletion *self)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
 
-       update_completion (completion, selected_providers, context);
-       g_list_free (selected_providers);
-       g_object_unref (context);
+       if (gtk_source_completion_is_blocked (self))
+               return;
 
-       return TRUE;
+       self->showing++;
+       if (self->showing == 1)
+               g_signal_emit (self, signals [SHOW], 0);
+       self->showing--;
 }
 
 /**
- * gtk_source_completion_get_providers:
- * @completion: a #GtkSourceCompletion.
+ * gtk_source_completion_hide:
+ * @self: an #GtkSourceCompletion
+ *
+ * Emits the "hide" signal.
  *
- * Get list of providers registered on @completion. The returned list is owned
- * by the completion and should not be freed.
+ * When the "hide" signal is emitted, the completion window will be
+ * dismissed.
  *
- * Returns: (element-type GtkSource.CompletionProvider) (transfer none):
- * list of #GtkSourceCompletionProvider.
+ * Since: 5.0
  */
-GList *
-gtk_source_completion_get_providers (GtkSourceCompletion *completion)
+void
+gtk_source_completion_hide (GtkSourceCompletion *self)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), NULL);
-       return completion->providers;
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
+
+       g_signal_emit (self, signals [HIDE], 0);
 }
 
-GQuark
-gtk_source_completion_error_quark (void)
+void
+gtk_source_completion_block_interactive (GtkSourceCompletion *self)
 {
-       static GQuark quark = 0;
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
 
-       if (G_UNLIKELY (quark == 0))
-       {
-               quark = g_quark_from_static_string ("gtk-source-completion-error-quark");
-       }
+       self->block_count++;
 
-       return quark;
+       gtk_source_completion_cancel (self);
 }
 
-/**
- * _gtk_source_completion_new:
- * @view: a #GtkSourceView.
- *
- * Creates a new #GtkSourceCompletion associated with @view.
- *
- * Returns: a new #GtkSourceCompletion.
- */
-GtkSourceCompletion *
-_gtk_source_completion_new (GtkSourceView *view)
+void
+gtk_source_completion_unblock_interactive (GtkSourceCompletion *self)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
 
-       return g_object_new (GTK_SOURCE_TYPE_COMPLETION,
-                            "view", view,
-                            NULL);
+       self->block_count--;
 }
 
-/**
- * gtk_source_completion_add_provider:
- * @completion: a #GtkSourceCompletion.
- * @provider: a #GtkSourceCompletionProvider.
- * @error: a #GError.
- *
- * Add a new #GtkSourceCompletionProvider to the completion object. This will
- * add a reference @provider, so make sure to unref your own copy when you
- * no longer need it.
- *
- * Returns: %TRUE if @provider was successfully added, otherwise if @error
- *          is provided, it will be set with the error and %FALSE is returned.
- */
-gboolean
-gtk_source_completion_add_provider (GtkSourceCompletion          *completion,
-                                   GtkSourceCompletionProvider  *provider,
-                                   GError                      **error)
+void
+gtk_source_completion_set_page_size (GtkSourceCompletion *self,
+                                     guint                page_size)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), FALSE);
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), FALSE);
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
+       g_return_if_fail (page_size > 0);
+       g_return_if_fail (page_size <= 32);
 
-       if (g_list_find (completion->providers, provider) != NULL)
+       if (self->page_size != page_size)
        {
-               if (error != NULL)
-               {
-                       g_set_error (error,
-                                    GTK_SOURCE_COMPLETION_ERROR,
-                                    GTK_SOURCE_COMPLETION_ERROR_ALREADY_BOUND,
-                                    "Provider is already bound to this completion object");
-               }
-
-               return FALSE;
+               self->page_size = page_size;
+               if (self->display != NULL)
+                       _gtk_source_completion_list_set_n_rows (self->display, page_size);
+               g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PAGE_SIZE]);
        }
+}
+
+guint
+gtk_source_completion_get_page_size (GtkSourceCompletion *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (self), 0);
+
+       return self->page_size;
+}
+
+void
+_gtk_source_completion_activate (GtkSourceCompletion         *self,
+                                 GtkSourceCompletionContext  *context,
+                                 GtkSourceCompletionProvider *provider,
+                                 GtkSourceCompletionProposal *proposal)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal));
+
+       self->block_count++;
+
+       gtk_source_completion_provider_activate (provider, context, proposal);
+       gtk_source_completion_hide (self);
+       g_clear_object (&self->context);
+       _gtk_source_completion_list_set_context (self->display, NULL);
 
-       completion->providers = g_list_append (completion->providers,
-                                                    g_object_ref (provider));
+       self->block_count--;
+}
+
+GtkSourceCompletionList *
+_gtk_source_completion_get_display (GtkSourceCompletion *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (self), NULL);
 
-       if (error != NULL)
+       if (self->display == NULL)
        {
-               *error = NULL;
+               self->display = _gtk_source_completion_list_new ();
+               _gtk_source_completion_list_set_n_rows (self->display, self->page_size);
+               _gtk_source_completion_list_set_font_desc (self->display, self->font_desc);
+               _gtk_source_assistant_set_mark (GTK_SOURCE_ASSISTANT (self->display),
+                                               self->completion_mark);
+               _gtk_source_view_add_assistant (self->view,
+                                               GTK_SOURCE_ASSISTANT (self->display));
+               _gtk_source_completion_list_set_context (self->display, self->context);
        }
 
-       return TRUE;
+       return self->display;
 }
 
 /**
- * gtk_source_completion_remove_provider:
- * @completion: a #GtkSourceCompletion.
- * @provider: a #GtkSourceCompletionProvider.
- * @error: a #GError.
+ * gtk_source_completion_fuzzy_match:
+ * @haystack: (nullable): the string to be searched.
+ * @casefold_needle: A g_utf8_casefold() version of the needle.
+ * @priority: (out) (allow-none): An optional location for the score of the match
+ *
+ * This helper function can do a fuzzy match for you giving a haystack and
+ * casefolded needle. Casefold your needle using g_utf8_casefold() before
+ * running the query.
  *
- * Remove @provider from the completion.
+ * Score will be set with the score of the match upon success. Otherwise,
+ * it will be set to zero.
  *
- * Returns: %TRUE if @provider was successfully removed, otherwise if @error
- *          is provided, it will be set with the error and %FALSE is returned.
+ * Returns: %TRUE if @haystack matched @casefold_needle, otherwise %FALSE.
+ *
+ * Since: 5.0
  */
 gboolean
-gtk_source_completion_remove_provider (GtkSourceCompletion          *completion,
-                                      GtkSourceCompletionProvider  *provider,
-                                      GError                      **error)
+gtk_source_completion_fuzzy_match (const char *haystack,
+                                   const char *casefold_needle,
+                                   guint      *priority)
 {
-       GList *item;
-
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), FALSE);
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), FALSE);
-
-       item = g_list_find (completion->providers, provider);
-
-       if (item == NULL)
-       {
-               if (error != NULL)
-               {
-                       g_set_error (error,
-                                    GTK_SOURCE_COMPLETION_ERROR,
-                                    GTK_SOURCE_COMPLETION_ERROR_NOT_BOUND,
-                                    "Provider is not bound to this completion object");
-               }
+       gint real_score = 0;
 
+       if (haystack == NULL || haystack[0] == 0)
                return FALSE;
-       }
 
-       completion->providers = g_list_delete_link (completion->providers, item);
+       for (; *casefold_needle; casefold_needle = g_utf8_next_char (casefold_needle))
+       {
+               gunichar ch = g_utf8_get_char (casefold_needle);
+               gunichar chup = g_unichar_toupper (ch);
+               const gchar *tmp;
+               const gchar *downtmp;
+               const gchar *uptmp;
+
+               /*
+               * Note that the following code is not really correct. We want
+               * to be relatively fast here, but we also don't want to convert
+               * strings to casefolded versions for querying on each compare.
+               * So we use the casefold version and compare with upper. This
+               * works relatively well since we are usually dealing with ASCII
+               * for function names and symbols.
+               */
+
+               downtmp = strchr (haystack, ch);
+               uptmp = strchr (haystack, chup);
+
+               if (downtmp && uptmp)
+                       tmp = MIN (downtmp, uptmp);
+               else if (downtmp)
+                       tmp = downtmp;
+               else if (uptmp)
+                       tmp = uptmp;
+               else
+                       return FALSE;
+
+               /*
+                * Here we calculate the cost of this character into the score.
+                * If we matched exactly on the next character, the cost is ZERO.
+                * However, if we had to skip some characters, we have a cost
+                * of 2*distance to the character. This is necessary so that
+                * when we add the cost of the remaining haystack, strings which
+                * exhausted @casefold_needle score lower (higher priority) than
+                * strings which had to skip characters but matched the same
+                * number of characters in the string.
+                */
+               real_score += (tmp - haystack) * 2;
 
-       g_object_unref (provider);
+               /* Add extra cost if we matched by using toupper */
+               if ((gunichar)*haystack == chup)
+                       real_score += 1;
 
-       if (error != NULL)
-       {
-               *error = NULL;
+               /*
+                * * Now move past our matching character so we cannot match
+                * * it a second time.
+                * */
+               haystack = tmp + 1;
        }
 
+       if (priority != NULL)
+               *priority = real_score + strlen (haystack);
+
        return TRUE;
 }
 
-/**
- * gtk_source_completion_hide:
- * @completion: a #GtkSourceCompletion.
- *
- * Hides the completion if it is active (visible).
- */
-void
-gtk_source_completion_hide (GtkSourceCompletion *completion)
+static void
+add_attributes (PangoAttrList **attrs,
+                guint           begin,
+                guint           end)
 {
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (completion));
+       PangoAttribute *attr;
 
-       reset_completion (completion);
-
-       if (gtk_widget_get_visible (GTK_WIDGET (completion->main_window)))
+       if (*attrs == NULL)
        {
-               g_signal_emit (completion, signals[HIDE], 0);
+               *attrs = pango_attr_list_new ();
        }
-}
 
-/**
- * gtk_source_completion_get_view:
- * @completion: a #GtkSourceCompletion.
- *
- * The #GtkSourceView associated with @completion, or %NULL if the view has been
- * destroyed.
- *
- * Returns: (nullable) (transfer none): The #GtkSourceView associated with
- * @completion, or %NULL.
- */
-GtkSourceView *
-gtk_source_completion_get_view (GtkSourceCompletion *completion)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), NULL);
+       attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
+       attr->start_index = begin;
+       attr->end_index = end;
+       pango_attr_list_insert (*attrs, g_steal_pointer (&attr));
 
-       return completion->view;
+       attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+       attr->start_index = begin;
+       attr->end_index = end;
+       pango_attr_list_insert (*attrs, g_steal_pointer (&attr));
 }
 
 /**
- * gtk_source_completion_create_context:
- * @completion: a #GtkSourceCompletion.
- * @position: (nullable): a #GtkTextIter, or %NULL.
+ * gtk_source_completion_fuzzy_highlight:
+ * @haystack: the string to be highlighted
+ * @casefold_query: the typed-text used to highlight @haystack
+ * @attrs: a #PangoAttrList to add the attributes to
+ *
+ * This will add &lt;b&gt; tags around matched characters in @haystack
+ * based on @casefold_query.
  *
- * Create a new #GtkSourceCompletionContext for @completion. The position where
- * the completion occurs can be specified by @position. If @position is %NULL,
- * the current cursor position will be used.
+ * Returns: (transfer full) (nullable): a #PangoAttrList or %NULL
  *
- * Returns: (transfer floating): a new #GtkSourceCompletionContext.
- * The reference being returned is a 'floating' reference,
- * so if you invoke gtk_source_completion_start() with this context
- * you don't need to unref it.
+ * Since: 5.0
  */
-GtkSourceCompletionContext *
-gtk_source_completion_create_context (GtkSourceCompletion *completion,
-                                      GtkTextIter         *position)
+PangoAttrList *
+gtk_source_completion_fuzzy_highlight (const char *haystack,
+                                       const char *casefold_query)
 {
-       GtkTextIter iter;
-
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), NULL);
+       const char *real_haystack = haystack;
+       PangoAttrList *attrs = NULL;
+       gunichar str_ch;
+       gunichar match_ch;
+       gboolean element_open = FALSE;
+       guint begin = 0;
+       guint end = 0;
 
-       if (completion->view == NULL)
+       if (haystack == NULL || casefold_query == NULL)
        {
                return NULL;
        }
 
-       if (position == NULL)
-       {
-               get_iter_at_insert (completion, &iter);
-       }
-       else
+       for (; *haystack; haystack = g_utf8_next_char (haystack))
        {
-               iter = *position;
-       }
-
-       return _gtk_source_completion_context_new (completion, &iter);
-}
+               str_ch = g_utf8_get_char (haystack);
+               match_ch = g_utf8_get_char (casefold_query);
 
-/**
- * gtk_source_completion_block_interactive:
- * @completion: a #GtkSourceCompletion.
- *
- * Block interactive completion. This can be used to disable interactive
- * completion when inserting or deleting text from the buffer associated with
- * the completion. Use gtk_source_completion_unblock_interactive() to enable
- * interactive completion again.
- *
- * This function may be called multiple times. It will continue to block
- * interactive completion until gtk_source_completion_unblock_interactive()
- * has been called the same number of times.
- */
-void
-gtk_source_completion_block_interactive (GtkSourceCompletion *completion)
-{
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (completion));
+               if ((str_ch == match_ch) || (g_unichar_tolower (str_ch) == g_unichar_tolower (match_ch)))
+               {
+                       if (!element_open)
+                       {
+                               begin = haystack - real_haystack;
+                               element_open = TRUE;
+                       }
 
-       if (completion->view == NULL)
-       {
-               return;
+                       /* TODO: We could seek to the next char and append in a batch. */
+                       casefold_query = g_utf8_next_char (casefold_query);
+               }
+               else
+               {
+                       if (element_open)
+                       {
+                               end = haystack - real_haystack;
+                               add_attributes (&attrs, begin, end);
+                               element_open = FALSE;
+                       }
+               }
        }
 
-       if (completion->block_interactive_num == 0)
+       if (element_open)
        {
-               block_interactive (completion);
+               end = haystack - real_haystack;
+               add_attributes (&attrs, begin, end);
        }
 
-       completion->block_interactive_num++;
+       return g_steal_pointer (&attrs);
 }
 
-/**
- * gtk_source_completion_unblock_interactive:
- * @completion: a #GtkSourceCompletion.
- *
- * Unblock interactive completion. This can be used after using
- * gtk_source_completion_block_interactive() to enable interactive completion
- * again.
- */
 void
-gtk_source_completion_unblock_interactive (GtkSourceCompletion *completion)
+_gtk_source_completion_css_changed (GtkSourceCompletion *self,
+                                    GtkCssStyleChange   *change)
 {
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (completion));
-
-       if (completion->view == NULL)
-       {
-               return;
-       }
-
-       if (completion->block_interactive_num == 1)
-       {
-               g_signal_handlers_unblock_by_func (completion->buffer,
-                                                  buffer_insert_text_cb,
-                                                  completion);
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION (self));
+       g_return_if_fail (change != NULL);
 
-               g_signal_handlers_unblock_by_func (completion->buffer,
-                                                  buffer_delete_range_cb,
-                                                  completion);
-       }
+       g_clear_pointer (&self->font_desc, pango_font_description_free);
+       self->font_desc = create_font_description (self);
 
-       if (completion->block_interactive_num > 0)
+       if (self->display != NULL)
        {
-               completion->block_interactive_num--;
+               _gtk_source_completion_list_set_font_desc (self->display, self->font_desc);
        }
 }
diff --git a/gtksourceview/gtksourcecompletion.h b/gtksourceview/gtksourcecompletion.h
index f314a816..01fcc742 100644
--- a/gtksourceview/gtksourcecompletion.h
+++ b/gtksourceview/gtksourcecompletion.h
@@ -1,8 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2007 - 2009 Jesús Barbero Rodríguez <chuchiperriman gmail com>
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,79 +15,52 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #pragma once
 
-#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
-#error "Only <gtksourceview/gtksource.h> can be included directly."
-#endif
-
 #include <gtk/gtk.h>
 
 #include "gtksourcetypes.h"
 
 G_BEGIN_DECLS
 
-/*
- * Type checking and casting macros
- */
 #define GTK_SOURCE_TYPE_COMPLETION (gtk_source_completion_get_type())
 
-GTK_SOURCE_AVAILABLE_IN_ALL
+GTK_SOURCE_AVAILABLE_IN_5_0
 G_DECLARE_FINAL_TYPE (GtkSourceCompletion, gtk_source_completion, GTK_SOURCE, COMPLETION, GObject)
 
-/**
- * GTK_SOURCE_COMPLETION_ERROR:
- *
- * Error domain for the completion. Errors in this domain will be from the
- * #GtkSourceCompletionError enumeration. See #GError for more information on
- * error domains.
- */
-#define GTK_SOURCE_COMPLETION_ERROR (gtk_source_completion_error_quark())
-
-/**
- * GtkSourceCompletionError:
- * @GTK_SOURCE_COMPLETION_ERROR_ALREADY_BOUND: The #GtkSourceCompletionProvider
- * is already bound to the #GtkSourceCompletion object.
- * @GTK_SOURCE_COMPLETION_ERROR_NOT_BOUND: The #GtkSourceCompletionProvider is
- * not bound to the #GtkSourceCompletion object.
- *
- * An error code used with %GTK_SOURCE_COMPLETION_ERROR in a #GError returned
- * from a completion-related function.
- */
-typedef enum _GtkSourceCompletionError
-{
-       GTK_SOURCE_COMPLETION_ERROR_ALREADY_BOUND = 0,
-       GTK_SOURCE_COMPLETION_ERROR_NOT_BOUND
-} GtkSourceCompletionError;
-
-GTK_SOURCE_AVAILABLE_IN_ALL
-GQuark                      gtk_source_completion_error_quark         (void);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gboolean                    gtk_source_completion_add_provider        (GtkSourceCompletion          
*completion,
-                                                                       GtkSourceCompletionProvider  
*provider,
-                                                                       GError                      **error);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gboolean                    gtk_source_completion_remove_provider     (GtkSourceCompletion          
*completion,
-                                                                       GtkSourceCompletionProvider  
*provider,
-                                                                       GError                      **error);
-GTK_SOURCE_AVAILABLE_IN_ALL
-GList                      *gtk_source_completion_get_providers       (GtkSourceCompletion          
*completion);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gboolean                    gtk_source_completion_start               (GtkSourceCompletion          
*completion,
-                                                                       GList                        
*providers,
-                                                                       GtkSourceCompletionContext   
*context);
-GTK_SOURCE_AVAILABLE_IN_ALL
-void                        gtk_source_completion_hide                (GtkSourceCompletion          
*completion);
-GTK_SOURCE_AVAILABLE_IN_ALL
-GtkSourceView              *gtk_source_completion_get_view            (GtkSourceCompletion          
*completion);
-GTK_SOURCE_AVAILABLE_IN_ALL
-GtkSourceCompletionContext *gtk_source_completion_create_context      (GtkSourceCompletion          
*completion,
-                                                                       GtkTextIter                  
*position);
-GTK_SOURCE_AVAILABLE_IN_ALL
-void                        gtk_source_completion_block_interactive   (GtkSourceCompletion          
*completion);
-GTK_SOURCE_AVAILABLE_IN_ALL
-void                        gtk_source_completion_unblock_interactive (GtkSourceCompletion          
*completion);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceView   *gtk_source_completion_get_view            (GtkSourceCompletion         *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceBuffer *gtk_source_completion_get_buffer          (GtkSourceCompletion         *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void             gtk_source_completion_show                (GtkSourceCompletion         *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void             gtk_source_completion_hide                (GtkSourceCompletion         *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void             gtk_source_completion_add_provider        (GtkSourceCompletion         *self,
+                                                            GtkSourceCompletionProvider *provider);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void             gtk_source_completion_remove_provider     (GtkSourceCompletion         *self,
+                                                            GtkSourceCompletionProvider *provider);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void             gtk_source_completion_block_interactive   (GtkSourceCompletion         *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void             gtk_source_completion_unblock_interactive (GtkSourceCompletion         *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+guint            gtk_source_completion_get_page_size       (GtkSourceCompletion         *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void             gtk_source_completion_set_page_size       (GtkSourceCompletion         *self,
+                                                            guint                        page_size);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean         gtk_source_completion_fuzzy_match         (const char                  *haystack,
+                                                            const char                  *casefold_needle,
+                                                            guint                       *priority);
+GTK_SOURCE_AVAILABLE_IN_5_0
+PangoAttrList   *gtk_source_completion_fuzzy_highlight     (const char                  *haystack,
+                                                            const char                  *casefold_query);
 
 G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletioncell-private.h b/gtksourceview/gtksourcecompletioncell-private.h
new file mode 100644
index 00000000..ea01f232
--- /dev/null
+++ b/gtksourceview/gtksourcecompletioncell-private.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include "gtksourcecompletioncell.h"
+
+G_BEGIN_DECLS
+
+void     _gtk_source_completion_cell_set_attrs (GtkSourceCompletionCell *self,
+                                                PangoAttrList           *attrs);
+gboolean _gtk_source_completion_cell_is_empty  (GtkSourceCompletionCell *self);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletioncell.c b/gtksourceview/gtksourcecompletioncell.c
new file mode 100644
index 00000000..a2a7356e
--- /dev/null
+++ b/gtksourceview/gtksourcecompletioncell.c
@@ -0,0 +1,419 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gtksource-enumtypes.h"
+#include "gtksourcecompletioncell.h"
+
+struct _GtkSourceCompletionCell
+{
+       GtkWidget                  widget;
+       GtkSourceCompletionColumn  column;
+       GtkWidget                 *child;
+       PangoAttrList             *attrs;
+};
+
+enum {
+       PROP_0,
+       PROP_COLUMN,
+       PROP_MARKUP,
+       PROP_PAINTABLE,
+       PROP_TEXT,
+       PROP_WIDGET,
+       N_PROPS
+};
+
+G_DEFINE_TYPE (GtkSourceCompletionCell, gtk_source_completion_cell, GTK_TYPE_WIDGET)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gtk_source_completion_cell_set_column (GtkSourceCompletionCell   *self,
+                                       GtkSourceCompletionColumn  column)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_CELL (self));
+
+       self->column = column;
+
+       switch (column)
+       {
+       case GTK_SOURCE_COMPLETION_COLUMN_ICON:
+               gtk_widget_add_css_class (GTK_WIDGET (self), "icon");
+               break;
+
+       case GTK_SOURCE_COMPLETION_COLUMN_BEFORE:
+               gtk_widget_add_css_class (GTK_WIDGET (self), "before");
+               break;
+
+       case GTK_SOURCE_COMPLETION_COLUMN_TYPED_TEXT:
+               gtk_widget_add_css_class (GTK_WIDGET (self), "typed-text");
+               break;
+
+       case GTK_SOURCE_COMPLETION_COLUMN_AFTER:
+               gtk_widget_add_css_class (GTK_WIDGET (self), "after");
+               break;
+
+       default:
+               break;
+       }
+}
+
+static void
+gtk_source_completion_cell_dispose (GObject *object)
+{
+       GtkSourceCompletionCell *self = (GtkSourceCompletionCell *)object;
+
+       g_clear_pointer (&self->child, gtk_widget_unparent);
+       g_clear_pointer (&self->attrs, pango_attr_list_unref);
+
+       G_OBJECT_CLASS (gtk_source_completion_cell_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_completion_cell_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+       GtkSourceCompletionCell *self = GTK_SOURCE_COMPLETION_CELL (object);
+
+       switch (prop_id)
+       {
+       case PROP_COLUMN:
+               g_value_set_enum (value, self->column);
+               break;
+
+       case PROP_TEXT:
+               if (GTK_IS_LABEL (self->child))
+                       g_value_set_string (value, gtk_label_get_label (GTK_LABEL (self->child)));
+               break;
+
+       case PROP_MARKUP:
+               if (GTK_IS_LABEL (self->child) &&
+                   gtk_label_get_use_markup (GTK_LABEL (self->child)))
+                       g_value_set_string (value, gtk_label_get_label (GTK_LABEL (self->child)));
+               break;
+
+       case PROP_PAINTABLE:
+               if (GTK_IS_IMAGE (self->child))
+                       g_value_set_object (value, gtk_image_get_paintable (GTK_IMAGE (self->child)));
+               break;
+
+       case PROP_WIDGET:
+               g_value_set_object (value, self->child);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+gtk_source_completion_cell_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+       GtkSourceCompletionCell *self = GTK_SOURCE_COMPLETION_CELL (object);
+
+       switch (prop_id)
+       {
+       case PROP_COLUMN:
+               gtk_source_completion_cell_set_column (self, g_value_get_enum (value));
+               break;
+
+       case PROP_MARKUP:
+               gtk_source_completion_cell_set_markup (self, g_value_get_string (value));
+               break;
+
+       case PROP_TEXT:
+               gtk_source_completion_cell_set_text (self, g_value_get_string (value));
+               break;
+
+       case PROP_PAINTABLE:
+               gtk_source_completion_cell_set_paintable (self, g_value_get_object (value));
+               break;
+
+       case PROP_WIDGET:
+               gtk_source_completion_cell_set_widget (self, g_value_get_object (value));
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+gtk_source_completion_cell_class_init (GtkSourceCompletionCellClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->dispose = gtk_source_completion_cell_dispose;
+       object_class->get_property = gtk_source_completion_cell_get_property;
+       object_class->set_property = gtk_source_completion_cell_set_property;
+
+       properties [PROP_COLUMN] =
+               g_param_spec_enum ("column",
+                                  "Column",
+                                  "Column",
+                                  GTK_SOURCE_TYPE_COMPLETION_COLUMN,
+                                  GTK_SOURCE_COMPLETION_COLUMN_TYPED_TEXT,
+                                  (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_MARKUP] =
+               g_param_spec_string ("markup",
+                                    "Markup",
+                                    "Markup",
+                                    NULL,
+                                    (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_TEXT] =
+               g_param_spec_string ("text",
+                                    "Text",
+                                    "Text",
+                                    NULL,
+                                    (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_PAINTABLE] =
+               g_param_spec_object ("paintable",
+                                    "Paintable",
+                                    "Paintable",
+                                    GDK_TYPE_PAINTABLE,
+                                    (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_WIDGET] =
+               g_param_spec_object ("widget",
+                                    "Widget",
+                                    "Widget",
+                                    GTK_TYPE_WIDGET,
+                                    (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+       
+       g_object_class_install_properties (object_class, N_PROPS, properties);
+
+       gtk_widget_class_set_css_name (widget_class, "cell");
+       gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+}
+
+static void
+gtk_source_completion_cell_init (GtkSourceCompletionCell *self)
+{
+       gtk_widget_add_css_class (GTK_WIDGET (self), "cell");
+}
+
+void
+gtk_source_completion_cell_set_markup (GtkSourceCompletionCell *self,
+                                       const char              *markup)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self));
+
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self));
+
+       if (!GTK_IS_LABEL (self->child))
+       {
+               GtkWidget *child = gtk_label_new (NULL);
+               gtk_source_completion_cell_set_widget (self, child);
+       }
+
+       gtk_label_set_text (GTK_LABEL (self->child), markup);
+       gtk_label_set_use_markup (GTK_LABEL (self->child), TRUE);
+}
+
+void
+gtk_source_completion_cell_set_text (GtkSourceCompletionCell *self,
+                                     const char              *text)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self));
+
+       if (!GTK_IS_LABEL (self->child))
+       {
+               GtkWidget *child = gtk_label_new (NULL);
+               gtk_source_completion_cell_set_widget (self, child);
+       }
+
+       gtk_label_set_use_markup (GTK_LABEL (self->child), FALSE);
+       gtk_label_set_text (GTK_LABEL (self->child), text);
+}
+
+void
+gtk_source_completion_cell_set_text_with_attributes (GtkSourceCompletionCell *self,
+                                                     const char              *text,
+                                                     PangoAttrList           *attrs)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self));
+
+       gtk_source_completion_cell_set_text (self, text);
+
+       if (attrs != NULL)
+       {
+               if (self->attrs != NULL)
+               {
+                       PangoAttrList *copy = pango_attr_list_copy (self->attrs);
+                       pango_attr_list_splice (copy, attrs, 0, 0);
+                       gtk_label_set_attributes (GTK_LABEL (self->child), copy);
+                       g_clear_pointer (&copy, pango_attr_list_unref);
+               }
+               else
+               {
+                       gtk_label_set_attributes (GTK_LABEL (self->child), attrs);
+               }
+       }
+       else
+       {
+               gtk_label_set_attributes (GTK_LABEL (self->child), self->attrs);
+       }
+}
+
+void
+gtk_source_completion_cell_set_paintable (GtkSourceCompletionCell *self,
+                                          GdkPaintable            *paintable)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self));
+       g_return_if_fail (!paintable || GDK_IS_PAINTABLE (paintable));
+
+       gtk_source_completion_cell_set_widget (self, gtk_image_new_from_paintable (paintable));
+}
+
+/**
+ * gtk_source_completion_cell_get_widget:
+ * @self: a #GtkSourceCompletionCell
+ *
+ * Gets the child #GtkWidget, if any.
+ *
+ * Returns: (transfer none) (nullable): a #GtkWidget or %NULL
+ *
+ * Since: 5.0
+ */
+GtkWidget *
+gtk_source_completion_cell_get_widget (GtkSourceCompletionCell *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self), NULL);
+
+       return self->child;
+}
+
+void
+gtk_source_completion_cell_set_widget (GtkSourceCompletionCell *self,
+                                       GtkWidget               *widget)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self));
+       g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
+       g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
+
+       if (widget == self->child)
+               return;
+
+       g_clear_pointer (&self->child, gtk_widget_unparent);
+
+       if (widget != NULL)
+       {
+               self->child = widget;
+               gtk_widget_set_parent (widget, GTK_WIDGET (self));
+
+               if (GTK_IS_LABEL (widget))
+               {
+                       gtk_label_set_attributes (GTK_LABEL (widget), self->attrs);
+
+                       if (self->column == GTK_SOURCE_COMPLETION_COLUMN_BEFORE)
+                       {
+                               gtk_label_set_xalign (GTK_LABEL (widget), 1.0);
+                       }
+                       else if (self->column == GTK_SOURCE_COMPLETION_COLUMN_AFTER ||
+                                self->column == GTK_SOURCE_COMPLETION_COLUMN_TYPED_TEXT ||
+                                self->column == GTK_SOURCE_COMPLETION_COLUMN_COMMENT ||
+                                self->column == GTK_SOURCE_COMPLETION_COLUMN_DETAILS)
+                       {
+                               gtk_label_set_xalign (GTK_LABEL (widget), 0.0);
+                       }
+
+                       if (self->column == GTK_SOURCE_COMPLETION_COLUMN_COMMENT)
+                       {
+                               gtk_label_set_xalign (GTK_LABEL (widget), 0.0);
+                               gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
+                               gtk_label_set_lines (GTK_LABEL (widget), 3);
+                               gtk_label_set_wrap (GTK_LABEL (widget), TRUE);
+                               gtk_label_set_max_width_chars (GTK_LABEL (widget), 50);
+                               gtk_widget_set_valign (widget, GTK_ALIGN_BASELINE);
+                       }
+               }
+               else if (GTK_IS_IMAGE (widget))
+               {
+                       if (self->column == GTK_SOURCE_COMPLETION_COLUMN_AFTER)
+                       {
+                               gtk_widget_set_halign (widget, GTK_ALIGN_END);
+                       }
+               }
+       }
+}
+
+GtkSourceCompletionColumn
+gtk_source_completion_cell_get_column (GtkSourceCompletionCell *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self), 0);
+
+       return self->column;
+}
+
+void
+_gtk_source_completion_cell_set_attrs (GtkSourceCompletionCell *self,
+                                       PangoAttrList           *attrs)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self));
+
+       if (attrs != self->attrs)
+       {
+               g_clear_pointer (&self->attrs, pango_attr_list_unref);
+
+               if (attrs)
+               {
+                       self->attrs = pango_attr_list_ref (attrs);
+               }
+
+               if (GTK_IS_LABEL (self->child))
+               {
+                       gtk_label_set_attributes (GTK_LABEL (self->child), attrs);
+               }
+       }
+}
+
+void
+gtk_source_completion_cell_set_icon_name (GtkSourceCompletionCell *self,
+                                          const char              *icon_name)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self));
+
+       if (!GTK_IS_IMAGE (self->child))
+       {
+               GtkWidget *image = gtk_image_new ();
+               gtk_source_completion_cell_set_widget (self, image);
+       }
+
+       gtk_image_set_from_icon_name (GTK_IMAGE (self->child), icon_name);
+}
+
+gboolean
+_gtk_source_completion_cell_is_empty (GtkSourceCompletionCell *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (self), FALSE);
+
+       return self->child == NULL;
+}
diff --git a/gtksourceview/gtksourcecompletioncell.h b/gtksourceview/gtksourcecompletioncell.h
new file mode 100644
index 00000000..6e5ff291
--- /dev/null
+++ b/gtksourceview/gtksourcecompletioncell.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "gtksourcetypes.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_COMPLETION_CELL (gtk_source_completion_cell_get_type())
+
+typedef enum _GtkSourceCompletionColumn
+{
+       GTK_SOURCE_COMPLETION_COLUMN_ICON       = 0,
+       GTK_SOURCE_COMPLETION_COLUMN_BEFORE     = 1,
+       GTK_SOURCE_COMPLETION_COLUMN_TYPED_TEXT = 2,
+       GTK_SOURCE_COMPLETION_COLUMN_AFTER      = 3,
+       GTK_SOURCE_COMPLETION_COLUMN_COMMENT    = 4,
+       GTK_SOURCE_COMPLETION_COLUMN_DETAILS    = 5,
+} GtkSourceCompletionColumn;
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+G_DECLARE_FINAL_TYPE (GtkSourceCompletionCell, gtk_source_completion_cell, GTK_SOURCE, COMPLETION_CELL, 
GtkWidget)
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceCompletionColumn gtk_source_completion_cell_get_column               (GtkSourceCompletionCell 
*self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkWidget                *gtk_source_completion_cell_get_widget               (GtkSourceCompletionCell 
*self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void                      gtk_source_completion_cell_set_widget               (GtkSourceCompletionCell *self,
+                                                                               GtkWidget               
*child);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void                      gtk_source_completion_cell_set_markup               (GtkSourceCompletionCell *self,
+                                                                               const char              
*markup);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void                      gtk_source_completion_cell_set_icon_name            (GtkSourceCompletionCell *self,
+                                                                               const char              
*icon_name);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void                      gtk_source_completion_cell_set_paintable            (GtkSourceCompletionCell *self,
+                                                                               GdkPaintable            
*paintable);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void                      gtk_source_completion_cell_set_text                 (GtkSourceCompletionCell *self,
+                                                                               const char              
*text);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void                      gtk_source_completion_cell_set_text_with_attributes (GtkSourceCompletionCell *self,
+                                                                               const char              *text,
+                                                                               PangoAttrList           
*attrs);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletioncontext-private.h 
b/gtksourceview/gtksourcecompletioncontext-private.h
index cacd3e66..c025315c 100644
--- a/gtksourceview/gtksourcecompletioncontext-private.h
+++ b/gtksourceview/gtksourcecompletioncontext-private.h
@@ -1,7 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,18 +15,42 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #pragma once
 
+#include <gtk/gtk.h>
+
 #include "gtksourcecompletioncontext.h"
 
 G_BEGIN_DECLS
 
-G_GNUC_INTERNAL
-GtkSourceCompletionContext *_gtk_source_completion_context_new    (GtkSourceCompletion        *completion,
-                                                                   GtkTextIter                *position);
-G_GNUC_INTERNAL
-void                        _gtk_source_completion_context_cancel (GtkSourceCompletionContext *context);
+GtkSourceCompletionContext  *_gtk_source_completion_context_new              (GtkSourceCompletion            
*completion);
+gboolean                     _gtk_source_completion_context_get_item_full    (GtkSourceCompletionContext     
*self,
+                                                                              guint                          
 position,
+                                                                              GtkSourceCompletionProvider   
**provider,
+                                                                              GtkSourceCompletionProposal   
**proposal);
+void                         _gtk_source_completion_context_add_provider     (GtkSourceCompletionContext     
*self,
+                                                                              GtkSourceCompletionProvider    
*provider);
+void                         _gtk_source_completion_context_remove_provider  (GtkSourceCompletionContext     
*self,
+                                                                              GtkSourceCompletionProvider    
*provider);
+gboolean                     _gtk_source_completion_context_can_refilter     (GtkSourceCompletionContext     
*self,
+                                                                              const GtkTextIter              
*begin,
+                                                                              const GtkTextIter              
*end);
+void                         _gtk_source_completion_context_refilter         (GtkSourceCompletionContext     
*self);
+gboolean                     _gtk_source_completion_context_iter_invalidates (GtkSourceCompletionContext     
*self,
+                                                                              const GtkTextIter              
*iter);
+void                         _gtk_source_completion_context_complete_async   (GtkSourceCompletionContext     
*self,
+                                                                              GtkSourceCompletionActivation  
 activation,
+                                                                              const GtkTextIter              
*begin,
+                                                                              const GtkTextIter              
*end,
+                                                                              GCancellable                   
*cancellable,
+                                                                              GAsyncReadyCallback            
 callback,
+                                                                              gpointer                       
 user_data);
+gboolean                     _gtk_source_completion_context_complete_finish  (GtkSourceCompletionContext     
*self,
+                                                                              GAsyncResult                   
*result,
+                                                                              GError                        
**error);
 
 G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletioncontext.c b/gtksourceview/gtksourcecompletioncontext.c
index 1d715345..78c21182 100644
--- a/gtksourceview/gtksourcecompletioncontext.c
+++ b/gtksourceview/gtksourcecompletioncontext.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,163 +15,208 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
-/**
- * SECTION:completioncontext
- * @title: GtkSourceCompletionContext
- * @short_description: The context of a completion
- *
- * Initially, the completion window is hidden. For a completion to occur, it has
- * to be activated. The different possible activations are listed in
- * #GtkSourceCompletionActivation. When an activation occurs, a
- * #GtkSourceCompletionContext object is created, and the eligible providers are
- * asked to add proposals with gtk_source_completion_context_add_proposals().
- *
- * If no proposals are added, the completion window remains hidden, and the
- * context is destroyed.
- *
- * On the other hand, if proposals are added, the completion window becomes
- * visible, and the user can choose a proposal. If the user is not happy with
- * the shown proposals, he or she can insert or delete characters, to modify the
- * completion context and therefore hoping to see the proposal he or she wants.
- * This means that when an insertion or deletion occurs in the #GtkTextBuffer
- * when the completion window is visible, the eligible providers are again asked
- * to add proposals. The #GtkSourceCompletionContext:activation remains the
- * same in this case.
- *
- * When the completion window is hidden, the interactive completion is triggered
- * only on insertion in the buffer, not on deletion. Once the completion window
- * is visible, then on each insertion or deletion, there is a new population and
- * the providers are asked to add proposals. If there are no more proposals, the
- * completion window disappears. So if you want to keep the completion window
- * visible, but there are no proposals, you can insert a dummy proposal named
- * "No proposals". For example, the user types progressively the name of
- * a function, and some proposals appear. The user types a bad character and
- * there are no proposals anymore. What the user wants is to delete the last
- * character, and see the previous proposals. If the completion window
- * disappears, the previous proposals will not reappear on the character
- * deletion.
- *
- * A #GtkTextIter is associated with the context, this is where the completion
- * takes place. With this #GtkTextIter, you can get the associated
- * #GtkTextBuffer with gtk_text_iter_get_buffer().
- */
+
 
 #include "config.h"
 
-#include "gtksourcecompletion-private.h"
+#include <string.h>
+
+#include "gtksourcecompletion.h"
 #include "gtksourcecompletioncontext-private.h"
-#include "gtksource-enumtypes.h"
+#include "gtksourcecompletionproposal.h"
 #include "gtksourcecompletionprovider.h"
-#include "gtksourcecompletion.h"
 
 struct _GtkSourceCompletionContext
 {
-       GInitiallyUnowned parent_instance;
+       GObject parent_instance;
 
        GtkSourceCompletion *completion;
 
-       GtkTextMark *mark;
+       GArray *providers;
+
+       GtkTextMark *begin_mark;
+       GtkTextMark *end_mark;
+
        GtkSourceCompletionActivation activation;
+
+       guint busy : 1;
+       guint has_populated : 1;
+       guint empty : 1;
 };
 
-enum
+typedef struct
+{
+       GtkSourceCompletionProvider *provider;
+       GListModel                  *results;
+       GError                      *error;
+       gulong                       items_changed_handler;
+} ProviderInfo;
+
+typedef struct
 {
+       guint n_active;
+} CompleteTaskData;
+
+static void list_model_iface_init (GListModelInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionContext, gtk_source_completion_context, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+enum {
        PROP_0,
+       PROP_BUSY,
        PROP_COMPLETION,
-       PROP_ITER,
-       PROP_ACTIVATION,
+       PROP_EMPTY,
        N_PROPS
 };
 
-enum
+static GParamSpec *properties [N_PROPS];
+
+static void
+clear_provider_info (gpointer data)
 {
-       CANCELLED,
-       N_SIGNALS
-};
+       ProviderInfo *info = data;
 
-static guint context_signals[N_SIGNALS];
-static GParamSpec *properties[N_PROPS];
+       if (info->items_changed_handler != 0)
+       {
+               g_signal_handler_disconnect (info->results, info->items_changed_handler);
+               info->items_changed_handler = 0;
+       }
 
-G_DEFINE_TYPE (GtkSourceCompletionContext, gtk_source_completion_context, G_TYPE_INITIALLY_UNOWNED)
+       g_clear_object (&info->provider);
+       g_clear_object (&info->results);
+       g_clear_error (&info->error);
+}
+
+static gint
+compare_provider_info (gconstpointer a,
+                       gconstpointer b,
+                       gpointer      user_data)
+{
+       GtkSourceCompletionContext *self = user_data;
+       const ProviderInfo *info_a = a;
+       const ProviderInfo *info_b = b;
+       int priority_a = gtk_source_completion_provider_get_priority (info_a->provider, self);
+       int priority_b = gtk_source_completion_provider_get_priority (info_b->provider, self);
+
+       if (priority_a < priority_b)
+               return -1;
+       else if (priority_a > priority_b)
+               return 1;
+       else
+               return 0;
+}
 
 static void
-gtk_source_completion_context_dispose (GObject *object)
+complete_task_data_free (gpointer data)
 {
-       GtkSourceCompletionContext *context = GTK_SOURCE_COMPLETION_CONTEXT (object);
+  CompleteTaskData *task_data = data;
 
-       if (context->mark != NULL)
+  g_slice_free (CompleteTaskData, task_data);
+}
+
+static void
+gtk_source_completion_context_update_empty (GtkSourceCompletionContext *self)
+{
+       gboolean empty = TRUE;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+
+       for (guint i = 0; i < self->providers->len; i++)
        {
-               GtkTextBuffer *buffer = gtk_text_mark_get_buffer (context->mark);
+               const ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
 
-               if (buffer != NULL)
+               if (info->results != NULL && g_list_model_get_n_items (info->results) > 0)
                {
-                       gtk_text_buffer_delete_mark (buffer, context->mark);
+                       empty = FALSE;
+                       break;
                }
-
-               g_clear_object (&context->mark);
        }
 
-       g_clear_object (&context->completion);
-
-       G_OBJECT_CLASS (gtk_source_completion_context_parent_class)->dispose (object);
+       if (self->empty != empty)
+       {
+               self->empty = empty;
+               g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EMPTY]);
+       }
 }
 
 static void
-set_iter (GtkSourceCompletionContext *context,
-         GtkTextIter                *iter)
+gtk_source_completion_context_mark_failed (GtkSourceCompletionContext  *self,
+                                           GtkSourceCompletionProvider *provider,
+                                           const GError                *error)
 {
-       GtkTextBuffer *buffer;
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+       g_assert (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_assert (error != NULL);
 
-       buffer = gtk_text_iter_get_buffer (iter);
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+           g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+               return;
 
-       if (context->mark != NULL)
+       for (guint i = 0; i < self->providers->len; i++)
        {
-               GtkTextBuffer *old_buffer;
-
-               old_buffer = gtk_text_mark_get_buffer (context->mark);
+               ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
 
-               if (old_buffer != buffer)
+               if (info->provider == provider)
                {
-                       g_object_unref (context->mark);
-                       context->mark = NULL;
+                       if (error != info->error)
+                       {
+                               g_clear_error (&info->error);
+                               info->error = g_error_copy (error);
+                       }
+
+                       break;
                }
        }
+}
 
-       if (context->mark == NULL)
+static void
+gtk_source_completion_context_dispose (GObject *object)
+{
+       GtkSourceCompletionContext *self = (GtkSourceCompletionContext *)object;
+
+       g_clear_pointer (&self->providers, g_array_unref);
+       g_clear_object (&self->completion);
+
+       if (self->begin_mark != NULL)
        {
-               context->mark = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
-               g_object_ref (context->mark);
+               gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (self->begin_mark), self->begin_mark);
+               g_clear_object (&self->begin_mark);
        }
-       else
+
+       if (self->end_mark != NULL)
        {
-               gtk_text_buffer_move_mark (buffer, context->mark, iter);
+               gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (self->end_mark), self->end_mark);
+               g_clear_object (&self->end_mark);
        }
 
-       g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_ITER]);
+       G_OBJECT_CLASS (gtk_source_completion_context_parent_class)->dispose (object);
 }
 
 static void
-gtk_source_completion_context_set_property (GObject      *object,
-                                            guint         prop_id,
-                                            const GValue *value,
-                                            GParamSpec   *pspec)
+gtk_source_completion_context_get_property (GObject    *object,
+                                            guint       prop_id,
+                                            GValue     *value,
+                                            GParamSpec *pspec)
 {
-       GtkSourceCompletionContext *context = GTK_SOURCE_COMPLETION_CONTEXT (object);
+       GtkSourceCompletionContext *self = GTK_SOURCE_COMPLETION_CONTEXT (object);
 
        switch (prop_id)
        {
-       case PROP_COMPLETION:
-               context->completion = g_value_dup_object (value);
+       case PROP_BUSY:
+               g_value_set_boolean (value, gtk_source_completion_context_get_busy (self));
                break;
 
-       case PROP_ITER:
-               set_iter (context, g_value_get_boxed (value));
+       case PROP_COMPLETION:
+               g_value_set_object (value, gtk_source_completion_context_get_completion (self));
                break;
 
-       case PROP_ACTIVATION:
-               context->activation = g_value_get_flags (value);
+       case PROP_EMPTY:
+               g_value_set_boolean (value, gtk_source_completion_context_get_empty (self));
                break;
 
        default:
@@ -180,32 +225,17 @@ gtk_source_completion_context_set_property (GObject      *object,
 }
 
 static void
-gtk_source_completion_context_get_property (GObject    *object,
-                                            guint       prop_id,
-                                            GValue     *value,
-                                            GParamSpec *pspec)
+gtk_source_completion_context_set_property (GObject      *object,
+                                            guint         prop_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec)
 {
-       GtkSourceCompletionContext *context = GTK_SOURCE_COMPLETION_CONTEXT (object);
+       GtkSourceCompletionContext *self = GTK_SOURCE_COMPLETION_CONTEXT (object);
 
        switch (prop_id)
        {
        case PROP_COMPLETION:
-               g_value_set_object (value, context->completion);
-               break;
-
-       case PROP_ITER:
-               {
-                       GtkTextIter iter;
-
-                       if (gtk_source_completion_context_get_iter (context, &iter))
-                       {
-                               g_value_set_boxed (value, &iter);
-                       }
-               }
-               break;
-
-       case PROP_ACTIVATION:
-               g_value_set_flags (value, context->activation);
+               self->completion = g_value_dup_object (value);
                break;
 
        default:
@@ -218,191 +248,760 @@ gtk_source_completion_context_class_init (GtkSourceCompletionContextClass *klass
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-       object_class->set_property = gtk_source_completion_context_set_property;
-       object_class->get_property = gtk_source_completion_context_get_property;
        object_class->dispose = gtk_source_completion_context_dispose;
+       object_class->get_property = gtk_source_completion_context_get_property;
+       object_class->set_property = gtk_source_completion_context_set_property;
 
        /**
-        * GtkSourceCompletionContext::cancelled:
-        *
-        * Emitted when the current population of proposals has been cancelled.
-        * Providers adding proposals asynchronously should connect to this signal
-        * to know when to cancel running proposal queries.
-        **/
-       context_signals[CANCELLED] =
-               g_signal_new_class_handler ("cancelled",
-                                           G_TYPE_FROM_CLASS (klass),
-                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                           NULL, NULL, NULL, NULL, G_TYPE_NONE, 0);
+       * GtkSourceCompletionContext:busy:
+       *
+       * The "busy" property is %TRUE while the completion context is
+       * populating completion proposals.
+       *
+       * Since: 5.0
+       */
+       properties [PROP_BUSY] =
+               g_param_spec_boolean ("busy",
+                                     "Busy",
+                                     "Is the completion context busy populating",
+                                     FALSE,
+                                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
        /**
-        * GtkSourceCompletionContext:completion:
-        *
-        * The #GtkSourceCompletion associated with the context.
-        **/
+       * GtkSourceCompletionContext:empty:
+       *
+       * The "empty" property is %TRUE when there are no results.
+       *
+       * It will be notified when the first result is added or the last
+       * result is removed.
+       *
+       * Since: 5.0
+       */
+       properties [PROP_EMPTY] =
+               g_param_spec_boolean ("empty",
+                                     "Empty",
+                                     "If the context has no results",
+                                     TRUE,
+                                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+       /**
+       * GtkSourceCompletionContext:completion:
+       *
+       * The "completion" is the #GtkSourceCompletion that was used to create the context.
+       *
+       * Since: 5.0
+       */
        properties [PROP_COMPLETION] =
                g_param_spec_object ("completion",
                                     "Completion",
-                                    "The completion object to which the context belongs",
+                                    "Completion",
                                     GTK_SOURCE_TYPE_COMPLETION,
-                                    (G_PARAM_READWRITE |
-                                     G_PARAM_CONSTRUCT_ONLY |
-                                     G_PARAM_STATIC_STRINGS));
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
-       /**
-        * GtkSourceCompletionContext:iter:
-        *
-        * The #GtkTextIter at which the completion is invoked.
-        **/
-       properties [PROP_ITER] =
-               g_param_spec_boxed ("iter",
-                                   "Iterator",
-                                   "The GtkTextIter at which the completion was invoked",
-                                   GTK_TYPE_TEXT_ITER,
-                                   (G_PARAM_READWRITE |
-                                    G_PARAM_EXPLICIT_NOTIFY |
-                                    G_PARAM_STATIC_STRINGS));
+       g_object_class_install_properties (object_class, N_PROPS, properties);
+}
 
-       /**
-        * GtkSourceCompletionContext:activation:
-        *
-        * The completion activation
-        **/
-       properties [PROP_ACTIVATION] =
-               g_param_spec_flags ("activation",
-                                   "Activation",
-                                   "The type of activation",
-                                   GTK_SOURCE_TYPE_COMPLETION_ACTIVATION,
-                                   GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED,
-                                   (G_PARAM_READWRITE |
-                                    G_PARAM_CONSTRUCT |
-                                    G_PARAM_STATIC_STRINGS));
+static void
+gtk_source_completion_context_init (GtkSourceCompletionContext *self)
+{
+       self->empty = TRUE;
 
-       g_object_class_install_properties (object_class, N_PROPS, properties);
+       self->providers = g_array_new (FALSE, FALSE, sizeof (ProviderInfo));
+       g_array_set_clear_func (self->providers, clear_provider_info);
+}
+
+static GType
+gtk_source_completion_context_get_item_type (GListModel *model)
+{
+       return GTK_SOURCE_TYPE_COMPLETION_PROPOSAL;
+}
+
+static guint
+gtk_source_completion_context_get_n_items (GListModel *model)
+{
+       GtkSourceCompletionContext *self = (GtkSourceCompletionContext *)model;
+       guint count = 0;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+
+       for (guint i = 0; i < self->providers->len; i++)
+       {
+               const ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
+
+               if (info->results != NULL)
+               {
+                       count += g_list_model_get_n_items (info->results);
+               }
+       }
+
+       return count;
+}
+
+gboolean
+_gtk_source_completion_context_get_item_full (GtkSourceCompletionContext   *self,
+                                              guint                         position,
+                                              GtkSourceCompletionProvider **provider,
+                                              GtkSourceCompletionProposal **proposal)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), FALSE);
+       g_return_val_if_fail (position < G_MAXUINT, FALSE);
+
+       if (provider != NULL)
+               *provider = NULL;
+
+       if (proposal != NULL)
+               *proposal = NULL;
+
+       for (guint i = 0; i < self->providers->len; i++)
+       {
+               const ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
+               guint n_items;
+
+               if (info->results == NULL)
+                       continue;
+
+               n_items = g_list_model_get_n_items (info->results);
+
+               if (position >= n_items)
+               {
+                       position -= n_items;
+                       continue;
+               }
+
+               if (provider != NULL)
+               {
+                       *provider = g_object_ref (info->provider);
+               }
+
+               if (proposal != NULL)
+               {
+                       *proposal = g_list_model_get_item (info->results, position);
+               }
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gpointer
+gtk_source_completion_context_get_item (GListModel *model,
+                                        guint       position)
+{
+       GtkSourceCompletionContext *self = (GtkSourceCompletionContext *)model;
+       g_autoptr(GtkSourceCompletionProposal) proposal = NULL;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+
+       if (_gtk_source_completion_context_get_item_full (self, position, NULL, &proposal))
+       {
+               return g_steal_pointer (&proposal);
+       }
+
+       return NULL;
 }
 
 static void
-gtk_source_completion_context_init (GtkSourceCompletionContext *context)
+list_model_iface_init (GListModelInterface *iface)
+{
+       iface->get_item_type = gtk_source_completion_context_get_item_type;
+       iface->get_item = gtk_source_completion_context_get_item;
+       iface->get_n_items = gtk_source_completion_context_get_n_items;
+}
+
+/**
+ * gtk_source_completion_context_get_bounds:
+ * @self: an #GtkSourceCompletionContext
+ * @begin: (out) (optional): a #GtkTextIter
+ * @end: (out) (optional): a #GtkTextIter
+ *
+ * Gets the bounds for the completion, which is the beginning of the
+ * current word (taking break characters into account) to the current
+ * insertion cursor.
+ *
+ * If @begin is non-%NULL, it will be set to the start position of the
+ * current word being completed.
+ *
+ * If @end is non-%NULL, it will be set to the insertion cursor for the
+ * current word being completed.
+ *
+ * Returns: %TRUE if the marks are still valid and @begin or @end was set.
+ *
+ * Since: 5.0
+ */
+gboolean
+gtk_source_completion_context_get_bounds (GtkSourceCompletionContext *self,
+                                          GtkTextIter                *begin,
+                                          GtkTextIter                *end)
 {
-       context = gtk_source_completion_context_get_instance_private (context);
+       GtkSourceBuffer *buffer;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), FALSE);
+       g_return_val_if_fail (self->completion != NULL, FALSE);
+       g_return_val_if_fail (begin != NULL || end != NULL, FALSE);
+
+       buffer = gtk_source_completion_get_buffer (self->completion);
+
+       g_return_val_if_fail (buffer != NULL, FALSE);
+
+       if (begin != NULL)
+               memset (begin, 0, sizeof *begin);
+
+       if (end != NULL)
+               memset (end, 0, sizeof *end);
+
+       if (self->begin_mark == NULL)
+       {
+               /* Try to give some sort of valid iter */
+               gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), begin, end);
+               return FALSE;
+       }
+
+       g_assert (GTK_IS_TEXT_MARK (self->begin_mark));
+       g_assert (GTK_IS_TEXT_MARK (self->end_mark));
+       g_assert (GTK_IS_TEXT_BUFFER (buffer));
+
+       if (begin != NULL)
+       {
+               gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), begin, self->begin_mark);
+       }
+
+       if (end != NULL)
+       {
+               gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), end, self->end_mark);
+       }
+
+       return TRUE;
 }
 
 /**
- * gtk_source_completion_context_add_proposals:
- * @context: a #GtkSourceCompletionContext.
- * @provider: a #GtkSourceCompletionProvider.
- * @proposals: (nullable) (element-type GtkSource.CompletionProposal): The list of proposals to add.
- * @finished: Whether the provider is finished adding proposals.
- *
- * Providers can use this function to add proposals to the completion. They
- * can do so asynchronously by means of the @finished argument. Providers must
- * ensure that they always call this function with @finished set to %TRUE
- * once each population (even if no proposals need to be added).
- * Population occurs when the gtk_source_completion_provider_populate()
- * function is called.
- **/
+ * gtk_source_completion_context_get_completion:
+ * @self: an #GtkSourceCompletionContext
+ *
+ * Gets the #GtkSourceCompletion that created the context.
+ *
+ * Returns: (transfer none) (nullable): an #GtkSourceCompletion or %NULL
+ *
+ * Since: 5.0
+ */
+GtkSourceCompletion *
+gtk_source_completion_context_get_completion (GtkSourceCompletionContext *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), NULL);
+
+       return self->completion;
+}
+
+GtkSourceCompletionContext *
+_gtk_source_completion_context_new (GtkSourceCompletion *completion)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), NULL);
+
+       return g_object_new (GTK_SOURCE_TYPE_COMPLETION_CONTEXT,
+                            "completion", completion,
+                            NULL);
+}
+
 void
-gtk_source_completion_context_add_proposals (GtkSourceCompletionContext  *context,
-                                             GtkSourceCompletionProvider *provider,
-                                             GList                       *proposals,
-                                             gboolean                     finished)
+_gtk_source_completion_context_add_provider (GtkSourceCompletionContext  *self,
+                                             GtkSourceCompletionProvider *provider)
 {
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+       ProviderInfo info = {0};
+
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
        g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_return_if_fail (self->has_populated == FALSE);
 
-       _gtk_source_completion_add_proposals (context->completion,
-                                             context,
-                                             provider,
-                                             proposals,
-                                             finished);
+       info.provider = g_object_ref (provider);
+       info.results = NULL;
+
+       g_array_append_val (self->providers, info);
+       g_array_sort_with_data (self->providers, compare_provider_info, self);
+}
+
+void
+_gtk_source_completion_context_remove_provider (GtkSourceCompletionContext  *self,
+                                                GtkSourceCompletionProvider *provider)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_return_if_fail (self->has_populated == FALSE);
+
+       for (guint i = 0; i < self->providers->len; i++)
+       {
+               const ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
+
+               if (info->provider == provider)
+               {
+                       g_array_remove_index (self->providers, i);
+                       return;
+               }
+       }
+
+       g_warning ("No such provider <%s %p> in context",
+                  G_OBJECT_TYPE_NAME (provider), provider);
+}
+
+static void
+gtk_source_completion_context_items_changed_cb (GtkSourceCompletionContext *self,
+                                                guint                       position,
+                                                guint                       removed,
+                                                guint                       added,
+                                                GListModel                 *results)
+{
+       guint real_position = 0;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+       g_assert (G_IS_LIST_MODEL (results));
+
+       if (removed == 0 && added == 0)
+               return;
+
+       for (guint i = 0; i < self->providers->len; i++)
+       {
+               ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
+
+               if (info->results == results)
+               {
+                       g_list_model_items_changed (G_LIST_MODEL (self),
+                                                   real_position + position,
+                                                   removed,
+                                                   added);
+                       break;
+               }
+
+               if (info->results != NULL)
+                       real_position += g_list_model_get_n_items (info->results);
+       }
+
+       gtk_source_completion_context_update_empty (self);
 }
 
 /**
- * gtk_source_completion_context_get_iter:
- * @context: a #GtkSourceCompletionContext.
- * @iter: (out): a #GtkTextIter.
+ * gtk_source_completion_context_set_proposals_for_provider:
+ * @self: an #GtkSourceCompletionContext
+ * @provider: an #GtkSourceCompletionProvider
+ * @results: (nullable): a #GListModel or %NULL
  *
- * Get the iter at which the completion was invoked. Providers can use this
- * to determine how and if to match proposals.
+ * This function allows providers to update their results for a context
+ * outside of a call to gtk_source_completion_provider_populate_async(). This
+ * can be used to immediately return results for a provider while it does
+ * additional asynchronous work. Doing so will allow the completions to
+ * update while the operation is in progress.
  *
- * Returns: %TRUE if @iter is correctly set, %FALSE otherwise.
- **/
-gboolean
-gtk_source_completion_context_get_iter (GtkSourceCompletionContext *context,
-                                        GtkTextIter                *iter)
+ * Since: 5.0
+ */
+void
+gtk_source_completion_context_set_proposals_for_provider (GtkSourceCompletionContext  *self,
+                                                          GtkSourceCompletionProvider *provider,
+                                                          GListModel                  *results)
 {
-       GtkTextBuffer *mark_buffer;
-       GtkSourceView *view;
-       GtkTextBuffer *completion_buffer;
+       guint position = 0;
 
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), FALSE);
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+       g_assert (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_assert (!results || G_IS_LIST_MODEL (results));
 
-       if (context->mark == NULL)
+       for (guint i = 0; i < self->providers->len; i++)
        {
-               /* This should never happen: context should be always be created
-                  providing a position iter */
-               g_warning ("Completion context without mark");
-               return FALSE;
+               ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
+
+               if (info->provider == provider)
+               {
+                       guint n_removed = 0;
+                       guint n_added = 0;
+
+                       if (info->results == results)
+                               return;
+
+                       if (info->results != NULL)
+                               n_removed = g_list_model_get_n_items (info->results);
+
+                       if (results != NULL)
+                               n_added = g_list_model_get_n_items (results);
+
+                       if (info->items_changed_handler != 0)
+                       {
+                               g_signal_handler_disconnect (info->results, info->items_changed_handler);
+                               info->items_changed_handler = 0;
+                       }
+
+                       g_set_object (&info->results, results);
+
+                       if (info->results != NULL)
+                               info->items_changed_handler =
+                                       g_signal_connect_object (info->results,
+                                                                "items-changed",
+                                                                G_CALLBACK 
(gtk_source_completion_context_items_changed_cb),
+                                                                self,
+                                                                G_CONNECT_SWAPPED);
+
+                       g_list_model_items_changed (G_LIST_MODEL (self), position, n_removed, n_added);
+
+                       break;
+               }
+
+               if (info->results != NULL)
+                       position += g_list_model_get_n_items (info->results);
        }
 
-       mark_buffer = gtk_text_mark_get_buffer (context->mark);
+       gtk_source_completion_context_update_empty (self);
+}
+
+static void
+gtk_source_completion_context_populate_cb (GObject      *object,
+                                           GAsyncResult *result,
+                                           gpointer      user_data)
+{
+       GtkSourceCompletionProvider *provider = (GtkSourceCompletionProvider *)object;
+       GtkSourceCompletionContext *self;
+       CompleteTaskData *task_data;
+       g_autoptr(GListModel) results = NULL;
+       g_autoptr(GTask) task = user_data;
+       g_autoptr(GError) error = NULL;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_assert (G_IS_ASYNC_RESULT (result));
+       g_assert (G_IS_TASK (task));
+
+       self = g_task_get_source_object (task);
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+
+       task_data = g_task_get_task_data (task);
+       g_assert (task_data != NULL);
+
+       if (!(results = gtk_source_completion_provider_populate_finish (provider, result, &error)))
+               gtk_source_completion_context_mark_failed (self, provider, error);
+       else
+               gtk_source_completion_context_set_proposals_for_provider (self, provider, results);
+
+       task_data->n_active--;
+
+       gtk_source_completion_context_update_empty (self);
+
+       if (task_data->n_active == 0)
+               g_task_return_boolean (task, TRUE);
+}
+
+static void
+gtk_source_completion_context_notify_complete_cb (GtkSourceCompletionContext *self,
+                                                  GParamSpec                 *pspec,
+                                                  GTask                      *task)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+       g_assert (G_IS_TASK (task));
+
+       self->busy = FALSE;
+       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+}
 
-       if (mark_buffer == NULL)
+/**
+ * _gtk_source_completion_context_complete_async:
+ * @self: a #GtkSourceCompletionContext
+ * @activation: how we are being activated
+ * @iter: a #GtkTextIter
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: (nullable): a callback or %NULL
+ * @user_data: closure data for @callback
+ *
+ * Asynchronously requests that the completion context load proposals
+ * from the registered providers.
+ *
+ * Since: 5.0
+ */
+void
+_gtk_source_completion_context_complete_async (GtkSourceCompletionContext    *self,
+                                               GtkSourceCompletionActivation  activation,
+                                               const GtkTextIter             *begin,
+                                               const GtkTextIter             *end,
+                                               GCancellable                  *cancellable,
+                                               GAsyncReadyCallback            callback,
+                                               gpointer                       user_data)
+{
+       g_autoptr(GTask) task = NULL;
+       CompleteTaskData *task_data;
+       GtkSourceBuffer *buffer;
+       guint n_items;
+
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+       g_return_if_fail (self->has_populated == FALSE);
+       g_return_if_fail (self->begin_mark == NULL);
+       g_return_if_fail (self->end_mark == NULL);
+       g_return_if_fail (begin != NULL);
+       g_return_if_fail (end != NULL);
+       g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+       self->activation = activation;
+       self->has_populated = TRUE;
+       self->busy = TRUE;
+
+       buffer = gtk_source_completion_context_get_buffer (self);
+
+       self->begin_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, begin, TRUE);
+       g_object_ref (self->begin_mark);
+
+       self->end_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, end, FALSE);
+       g_object_ref (self->end_mark);
+
+       task = g_task_new (self, cancellable, callback, user_data);
+       g_task_set_source_tag (task, _gtk_source_completion_context_complete_async);
+       g_task_set_priority (task, G_PRIORITY_LOW);
+
+       task_data = g_slice_new0 (CompleteTaskData);
+       task_data->n_active = self->providers->len;
+       g_task_set_task_data (task, task_data, complete_task_data_free);
+
+       /* Always notify of busy completion, whether we fail or not */
+       g_signal_connect_object (task,
+                                "notify::completed",
+                                G_CALLBACK (gtk_source_completion_context_notify_complete_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+
+       for (guint i = 0; i < self->providers->len; i++)
        {
-               return FALSE;
+               const ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
+
+               gtk_source_completion_provider_populate_async (info->provider,
+                                                              self,
+                                                              cancellable,
+                                                              gtk_source_completion_context_populate_cb,
+                                                              g_object_ref (task));
        }
 
-       view = gtk_source_completion_get_view (context->completion);
-       if (view == NULL)
+       /* Providers may adjust their position based on our new marks */
+       n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
+       g_array_sort_with_data (self->providers, compare_provider_info, self);
+       g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
+
+       if (task_data->n_active == 0)
        {
-               return FALSE;
+               g_task_return_boolean (task, TRUE);
        }
 
-       completion_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+}
 
-       if (completion_buffer != mark_buffer)
+/**
+ * _gtk_source_completion_context_complete_finish:
+ * @self: an #GtkSourceCompletionContext
+ * @result: a #GAsyncResult provided to callback
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes an asynchronous request to populate proposals.
+ *
+ * Returns: %TRUE if successful; otherwise %FALSE and @error is set
+ *
+ * Since: 5.0
+ */
+gboolean
+_gtk_source_completion_context_complete_finish (GtkSourceCompletionContext  *self,
+                                                GAsyncResult                *result,
+                                                GError                     **error)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), FALSE);
+       g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+       return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+/**
+ * gtk_source_completion_context_get_busy:
+ *
+ * Gets the "busy" property. This is set to %TRUE while the completion
+ * context is actively fetching proposals from registered
+ * #GtkSourceCompletionProvider's.
+ *
+ * Returns: %TRUE if the context is busy
+ *
+ * Since: 5.0
+ */
+gboolean
+gtk_source_completion_context_get_busy (GtkSourceCompletionContext *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), FALSE);
+
+       return self->busy;
+}
+
+/**
+ * gtk_source_completion_context_get_buffer:
+ * @self: an #GtkSourceCompletionContext
+ *
+ * Gets the underlying buffer used by the context.
+ *
+ * This is a convenience function to get the buffer via the #GtkSourceCompletion
+ * property.
+ *
+ * Returns: (transfer none) (nullable): a #GtkTextBuffer or %NULL
+ *
+ * Since: 5.0
+ */
+GtkSourceBuffer *
+gtk_source_completion_context_get_buffer (GtkSourceCompletionContext *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), NULL);
+
+       if (self->completion != NULL)
+               return gtk_source_completion_get_buffer (self->completion);
+
+       return NULL;
+}
+
+/**
+ * gtk_source_completion_context_get_view:
+ * @self: a #GtkSourceCompletionContext
+ *
+ * Gets the text view for the context.
+ *
+ * Returns: (nullable) (transfer none): a #GtkSourceView or %NULL
+ *
+ * Since: 5.0
+ */
+GtkSourceView *
+gtk_source_completion_context_get_view (GtkSourceCompletionContext *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), NULL);
+
+       if (self->completion != NULL)
+               return gtk_source_completion_get_view (self->completion);
+
+       return NULL;
+}
+
+void
+_gtk_source_completion_context_refilter (GtkSourceCompletionContext *self)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+
+       for (guint i = 0; i < self->providers->len; i++)
        {
-               return FALSE;
+               const ProviderInfo *info = &g_array_index (self->providers, ProviderInfo, i);
+
+               if (info->error != NULL)
+                       continue;
+
+               if (info->results == NULL)
+                       continue;
+
+               gtk_source_completion_provider_refilter (info->provider, self, info->results);
        }
+}
 
-       gtk_text_buffer_get_iter_at_mark (mark_buffer, iter, context->mark);
-       return TRUE;
+gboolean
+_gtk_source_completion_context_iter_invalidates (GtkSourceCompletionContext *self,
+                                                 const GtkTextIter          *iter)
+{
+       GtkTextIter begin, end;
+       GtkTextBuffer *buffer;
+
+       g_assert (!self || GTK_SOURCE_IS_COMPLETION_CONTEXT (self));
+       g_assert (iter != NULL);
+
+       if (self == NULL)
+               return FALSE;
+
+       buffer = gtk_text_iter_get_buffer (iter);
+
+       gtk_text_buffer_get_iter_at_mark (buffer, &begin, self->begin_mark);
+       gtk_text_buffer_get_iter_at_mark (buffer, &end, self->end_mark);
+
+       return gtk_text_iter_compare (&begin, iter) <= 0 &&
+              gtk_text_iter_compare (&end, iter) >= 0;
 }
 
 /**
- * gtk_source_completion_context_get_activation:
- * @context: a #GtkSourceCompletionContext.
+ * gtk_source_completion_context_get_empty:
+ * @self: a #GtkSourceCompletionContext
+ *
+ * Checks if any proposals have been provided to the context.
  *
- * Get the context activation.
+ * Out of convenience, this function will return %TRUE if @self is %NULL.
  *
- * Returns: The context activation.
+ * Returns: %TRUE if there are no proposals in the context
+ *
+ * Since: 5.0
  */
-GtkSourceCompletionActivation
-gtk_source_completion_context_get_activation (GtkSourceCompletionContext *context)
+gboolean
+gtk_source_completion_context_get_empty (GtkSourceCompletionContext *self)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context),
-                             GTK_SOURCE_COMPLETION_ACTIVATION_NONE);
+       g_return_val_if_fail (!self || GTK_SOURCE_IS_COMPLETION_CONTEXT (self), FALSE);
 
-       return context->activation;
+       return self ? self->empty : TRUE;
 }
 
-void
-_gtk_source_completion_context_cancel (GtkSourceCompletionContext *context)
+/**
+ * gtk_source_completion_context_get_word:
+ * @self: a #GtkSourceCompletionContext
+ *
+ * Gets the word that is being completed up to the position of the insert mark.
+ *
+ * Returns: (transfer full): a string containing the current word
+ *
+ * Since: 5.0
+ */
+gchar *
+gtk_source_completion_context_get_word (GtkSourceCompletionContext *self)
 {
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+       GtkTextIter begin, end;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), NULL);
+
+       gtk_source_completion_context_get_bounds (self, &begin, &end);
 
-       g_signal_emit (context, context_signals[CANCELLED], 0);
+       return gtk_text_iter_get_slice (&begin, &end);
 }
 
-GtkSourceCompletionContext *
-_gtk_source_completion_context_new (GtkSourceCompletion *completion,
-                                   GtkTextIter         *position)
+gboolean
+_gtk_source_completion_context_can_refilter (GtkSourceCompletionContext *self,
+                                             const GtkTextIter          *begin,
+                                             const GtkTextIter          *end)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION (completion), NULL);
-       g_return_val_if_fail (position != NULL, NULL);
+       GtkTextIter old_begin;
+       GtkTextIter old_end;
 
-       return g_object_new (GTK_SOURCE_TYPE_COMPLETION_CONTEXT,
-                            "completion", completion,
-                            "iter", position,
-                             NULL);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), FALSE);
+       g_return_val_if_fail (begin != NULL, FALSE);
+       g_return_val_if_fail (end != NULL, FALSE);
+
+       gtk_source_completion_context_get_bounds (self, &old_begin, &old_end);
+
+       if (gtk_text_iter_equal (&old_begin, begin))
+       {
+               /*
+                * TODO: We can probably get smarter about this by asking all of
+                * the providers if they can refilter the new word (and only reload
+                * the data for those that cannot.
+                *
+                * Also, we might want to deal with that by copying the context
+                * into a new context and query using that.
+                */
+               if (gtk_text_iter_compare (&old_end, end) <= 0)
+               {
+                       GtkTextBuffer *buffer = gtk_text_iter_get_buffer (begin);
+
+                       gtk_text_buffer_move_mark (buffer, self->begin_mark, begin);
+                       gtk_text_buffer_move_mark (buffer, self->end_mark, end);
+
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+/**
+ * gtk_source_completion_context_get_activation:
+ * @self: a #GtkSourceCompletionContext
+ *
+ * Gets the mode for which the context was activated.
+ *
+ * Since: 5.0
+ */
+GtkSourceCompletionActivation
+gtk_source_completion_context_get_activation (GtkSourceCompletionContext *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (self), 0);
+
+       return self->activation;
 }
diff --git a/gtksourceview/gtksourcecompletioncontext.h b/gtksourceview/gtksourcecompletioncontext.h
index f37856a5..17025749 100644
--- a/gtksourceview/gtksourcecompletioncontext.h
+++ b/gtksourceview/gtksourcecompletioncontext.h
@@ -1,7 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,14 +15,12 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #pragma once
 
-#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
-#error "Only <gtksourceview/gtksource.h> can be included directly."
-#endif
-
 #include <gtk/gtk.h>
 
 #include "gtksourcetypes.h"
@@ -31,35 +29,42 @@ G_BEGIN_DECLS
 
 #define GTK_SOURCE_TYPE_COMPLETION_CONTEXT (gtk_source_completion_context_get_type())
 
-GTK_SOURCE_AVAILABLE_IN_ALL
-G_DECLARE_FINAL_TYPE (GtkSourceCompletionContext, gtk_source_completion_context, GTK_SOURCE, 
COMPLETION_CONTEXT, GInitiallyUnowned)
-
-/**
- * GtkSourceCompletionActivation:
- * @GTK_SOURCE_COMPLETION_ACTIVATION_NONE: None.
- * @GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE: Interactive activation. By
- * default, it occurs on each insertion in the #GtkTextBuffer. This can be
- * blocked temporarily with gtk_source_completion_block_interactive().
- * @GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED: User requested activation.
- * By default, it occurs when the user presses
- * <keycombo><keycap>Control</keycap><keycap>space</keycap></keycombo>.
- */
 typedef enum _GtkSourceCompletionActivation
 {
-       GTK_SOURCE_COMPLETION_ACTIVATION_NONE           = 0,
-       GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE    = 1 << 0,
-       GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED = 1 << 1
+       GTK_SOURCE_COMPLETION_ACTIVATION_NONE = 0,
+       GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE = 1,
+       GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED = 2,
 } GtkSourceCompletionActivation;
 
-GTK_SOURCE_AVAILABLE_IN_ALL
-void                          gtk_source_completion_context_add_proposals  (GtkSourceCompletionContext  
*context,
-                                                                            GtkSourceCompletionProvider 
*provider,
-                                                                            GList                       
*proposals,
-                                                                            gboolean                     
finished);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gboolean                      gtk_source_completion_context_get_iter       (GtkSourceCompletionContext  
*context,
-                                                                            GtkTextIter                 
*iter);
-GTK_SOURCE_AVAILABLE_IN_ALL
-GtkSourceCompletionActivation gtk_source_completion_context_get_activation (GtkSourceCompletionContext  
*context);
+GTK_SOURCE_AVAILABLE_IN_5_0
+G_DECLARE_FINAL_TYPE (GtkSourceCompletionContext, gtk_source_completion_context, GTK_SOURCE, 
COMPLETION_CONTEXT, GObject)
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceCompletion           *gtk_source_completion_context_get_completion             
(GtkSourceCompletionContext  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceCompletionActivation  gtk_source_completion_context_get_activation             
(GtkSourceCompletionContext  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean                       gtk_source_completion_context_get_bounds                 
(GtkSourceCompletionContext  *self,
+                                                                                         GtkTextIter         
        *begin,
+                                                                                         GtkTextIter         
        *end);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void                           gtk_source_completion_context_get_start_iter             
(GtkSourceCompletionContext  *self,
+                                                                                         GtkTextIter         
        *iter);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean                       gtk_source_completion_context_get_empty                  
(GtkSourceCompletionContext  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+char                          *gtk_source_completion_context_get_word                   
(GtkSourceCompletionContext  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean                       gtk_source_completion_context_get_busy                   
(GtkSourceCompletionContext  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceView                 *gtk_source_completion_context_get_view                   
(GtkSourceCompletionContext  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceBuffer               *gtk_source_completion_context_get_buffer                 
(GtkSourceCompletionContext  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceLanguage             *gtk_source_completion_context_get_language               
(GtkSourceCompletionContext  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void                           gtk_source_completion_context_set_proposals_for_provider 
(GtkSourceCompletionContext  *self,
+                                                                                         
GtkSourceCompletionProvider *provider,
+                                                                                         GListModel          
        *results);
 
 G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletioninfo-private.h b/gtksourceview/gtksourcecompletioninfo-private.h
index 9017b430..2f82edbe 100644
--- a/gtksourceview/gtksourcecompletioninfo-private.h
+++ b/gtksourceview/gtksourcecompletioninfo-private.h
@@ -1,8 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2007 - 2009 Jesús Barbero Rodríguez <chuchiperriman gmail com>
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,16 +15,23 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #pragma once
 
-#include "gtksourcecompletioninfo.h"
+#include <gtk/gtk.h>
+
+#include "gtksourcetypes-private.h"
 
 G_BEGIN_DECLS
 
-G_GNUC_INTERNAL
-void _gtk_source_completion_info_set_xoffset (GtkSourceCompletionInfo *info,
-                                              gint                     xoffset);
+#define GTK_SOURCE_TYPE_COMPLETION_INFO (_gtk_source_completion_info_get_type())
+
+G_DECLARE_FINAL_TYPE (GtkSourceCompletionInfo, _gtk_source_completion_info, GTK_SOURCE, COMPLETION_INFO, 
GtkSourceAssistant)
+
+GtkSourceCompletionInfo *_gtk_source_completion_info_new      (void);
+GtkSourceCompletionCell *_gtk_source_completion_info_get_cell (GtkSourceCompletionInfo *self);
 
 G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletioninfo.c b/gtksourceview/gtksourcecompletioninfo.c
index fc05baa4..405ac3fa 100644
--- a/gtksourceview/gtksourcecompletioninfo.c
+++ b/gtksourceview/gtksourcecompletioninfo.c
@@ -1,9 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2007-2009 Jesús Barbero Rodríguez <chuchiperriman gmail com>
- * Copyright 2009 Jesse van den Kieboom <jessevdk gnome org>
- * Copyright 2012 Sébastien Wilmet <swilmet gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,286 +15,74 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * SECTION:completioninfo
- * @title: GtkSourceCompletionInfo
- * @short_description: Calltips object
- *
- * This object can be used to show a calltip or help for the
- * current completion proposal.
- *
- * The info window has always the same size as the natural size of its child
- * widget, added with gtk_widget_set_child().  If you want a fixed size instead, a
- * possibility is to use a scrolled window, as the following example
- * demonstrates.
  *
- * <example>
- *   <title>Fixed size with a scrolled window.</title>
- *   <programlisting>
- * GtkSourceCompletionInfo *info;
- * GtkWidget *your_widget;
- * GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
- *
- * gtk_widget_set_size_request (scrolled_window, 300, 200);
- * gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), your_widget);
- * gtk_window_set_child (GTK_WINDOW (info), scrolled_window);
- *   </programlisting>
- * </example>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #include "config.h"
 
-#include <glib/gi18n-lib.h>
-
+#include "gtksourceassistant-private.h"
+#include "gtksourcecompletioncell-private.h"
 #include "gtksourcecompletioninfo-private.h"
 
 struct _GtkSourceCompletionInfo
 {
-       GtkWindow parent_instance;
-
-       guint idle_resize;
-
-       GtkWidget *attached_to;
-       GtkEventController *focus;
-       gulong focus_out_event_handler;
-
-       gint xoffset;
-
-       guint transient_set : 1;
+       GtkSourceAssistant       parent_instance;
+       GtkSourceCompletionCell *cell;
 };
 
-G_DEFINE_TYPE (GtkSourceCompletionInfo, gtk_source_completion_info, GTK_TYPE_WINDOW);
-
-/* Resize the window */
-
-/* Init, dispose, finalize, ... */
+G_DEFINE_TYPE (GtkSourceCompletionInfo, _gtk_source_completion_info, GTK_SOURCE_TYPE_ASSISTANT)
 
 static void
-set_attached_to (GtkSourceCompletionInfo *info,
-                 GtkWidget               *attached_to)
+_gtk_source_completion_info_get_offset (GtkSourceAssistant *assistant,
+                                        int                *x_offset,
+                                        int                *y_offset)
 {
-       if (info->attached_to == attached_to)
-       {
-               return;
-       }
-
-       if (info->attached_to != NULL)
-       {
-               g_object_remove_weak_pointer (G_OBJECT (info->attached_to),
-                                             (gpointer *) &info->attached_to);
-
-               if (info->focus_out_event_handler != 0)
-               {
-                       g_signal_handler_disconnect (info->focus,
-                                                    info->focus_out_event_handler);
-
-                       info->focus_out_event_handler = 0;
-                       info->focus = NULL;
-               }
-       }
-
-       info->attached_to = attached_to;
-       info->focus = NULL;
+       GtkStyleContext *style_context;
+       GtkBorder margin;
 
-       if (attached_to == NULL)
-       {
-               return;
-       }
+       g_assert (GTK_SOURCE_IS_COMPLETION_INFO (assistant));
 
-       g_object_add_weak_pointer (G_OBJECT (attached_to),
-                                  (gpointer *) &info->attached_to);
+       style_context = gtk_widget_get_style_context (GTK_WIDGET (assistant));
+       gtk_style_context_get_margin (style_context, &margin);
 
-       info->focus = gtk_event_controller_focus_new ();
-       gtk_widget_add_controller (GTK_WIDGET (attached_to), info->focus);
-
-       info->focus_out_event_handler =
-               g_signal_connect_swapped (info->focus,
-                                         "leave",
-                                         G_CALLBACK (gtk_widget_hide),
-                                         info);
-
-       info->transient_set = FALSE;
+       *x_offset = -margin.left + 1;
+       *y_offset = -margin.top;
 }
 
 static void
-gtk_source_completion_info_init (GtkSourceCompletionInfo *info)
+_gtk_source_completion_info_class_init (GtkSourceCompletionInfoClass *klass)
 {
-       /* Tooltip style */
-       gtk_window_set_title (GTK_WINDOW (info), _("Completion Info"));
-       gtk_widget_set_name (GTK_WIDGET (info), "gtk-tooltip");
+       GtkSourceAssistantClass *assistant_class = GTK_SOURCE_ASSISTANT_CLASS (klass);
 
-       g_object_set (info,
-                     "margin-top", 1,
-                     "margin-bottom", 1,
-                     "margin-start", 1,
-                     "margin-end", 1,
-                     NULL);
+       assistant_class->get_offset = _gtk_source_completion_info_get_offset;
 }
 
 static void
-gtk_source_completion_info_dispose (GObject *object)
+_gtk_source_completion_info_init (GtkSourceCompletionInfo *self)
 {
-       GtkSourceCompletionInfo *info = GTK_SOURCE_COMPLETION_INFO (object);
-
-       if (info->idle_resize != 0)
-       {
-               g_source_remove (info->idle_resize);
-               info->idle_resize = 0;
-       }
-
-       set_attached_to (info, NULL);
-
-       G_OBJECT_CLASS (gtk_source_completion_info_parent_class)->dispose (object);
+       gtk_widget_add_css_class (GTK_WIDGET (self), "completion-info");
+       gtk_popover_set_position (GTK_POPOVER (self), GTK_POS_RIGHT);
+       gtk_popover_set_autohide (GTK_POPOVER (self), FALSE);
+
+       self->cell = g_object_new (GTK_SOURCE_TYPE_COMPLETION_CELL,
+                                  "column", GTK_SOURCE_COMPLETION_COLUMN_DETAILS,
+                                  "halign", GTK_ALIGN_START,
+                                  "valign", GTK_ALIGN_START,
+                                  NULL);
+       _gtk_source_assistant_set_child (GTK_SOURCE_ASSISTANT (self), GTK_WIDGET (self->cell));
 }
 
-static void
-gtk_source_completion_info_show (GtkWidget *widget)
-{
-       GtkSourceCompletionInfo *info = GTK_SOURCE_COMPLETION_INFO (widget);
-
-       if (info->attached_to != NULL && !info->transient_set)
-       {
-               GtkRoot *toplevel;
-
-               toplevel = gtk_widget_get_root (GTK_WIDGET (info->attached_to));
-
-               if (toplevel != NULL)
-               {
-                       gtk_window_set_transient_for (GTK_WINDOW (info),
-                                                     GTK_WINDOW (toplevel));
-                       info->transient_set = TRUE;
-               }
-       }
-
-       GTK_WIDGET_CLASS (gtk_source_completion_info_parent_class)->show (widget);
-}
-
-static void
-gtk_source_completion_info_class_init (GtkSourceCompletionInfoClass *klass)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (klass);
-       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
-       object_class->dispose = gtk_source_completion_info_dispose;
-
-       widget_class->show = gtk_source_completion_info_show;
-}
-
-void
-_gtk_source_completion_info_set_xoffset (GtkSourceCompletionInfo *window,
-                                        gint                     xoffset)
-{
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_INFO (window));
-
-       window->xoffset = xoffset;
-}
-
-static void
-move_to_iter (GtkSourceCompletionInfo *window,
-              GtkTextView             *view,
-              GtkTextIter             *iter)
-{
-       GdkRectangle location;
-       GdkPopupLayout *layout;
-       GdkSurface *surface;
-       GtkRoot *root;
-       gdouble x;
-       gdouble y;
-
-       if (!GTK_IS_NATIVE (window))
-               return;
-
-       surface = gtk_native_get_surface (GTK_NATIVE (window));
-       if (surface == NULL)
-               return;
-
-       root = gtk_widget_get_root (GTK_WIDGET (view));
-       if (root == NULL)
-               return;
-
-       gtk_text_view_get_iter_location (view, iter, &location);
-       gtk_text_view_buffer_to_window_coords (view,
-                                              GTK_TEXT_WINDOW_WIDGET,
-                                              location.x,
-                                              location.y,
-                                              &location.x,
-                                              &location.y);
-
-       gtk_widget_translate_coordinates (GTK_WIDGET (view),
-                                         GTK_WIDGET (root),
-                                         location.x,
-                                         location.y,
-                                         &x,
-                                         &y);
-
-       location.x = x;
-       location.y = y;
-
-       layout = gdk_popup_layout_new (&location, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST);
-       gdk_popup_layout_set_anchor_hints (layout, GDK_ANCHOR_SLIDE | GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_RESIZE);
-       gdk_popup_layout_set_offset (layout, window->xoffset, 0);
-       gdk_popup_present (GDK_POPUP (surface), 0, 0, layout);
-       gdk_popup_layout_unref (layout);
-}
-
-static void
-move_to_cursor (GtkSourceCompletionInfo *window,
-               GtkTextView             *view)
-{
-       GtkTextBuffer *buffer;
-       GtkTextIter insert;
-
-       buffer = gtk_text_view_get_buffer (view);
-       gtk_text_buffer_get_iter_at_mark (buffer, &insert, gtk_text_buffer_get_insert (buffer));
-
-       move_to_iter (window, view, &insert);
-}
-
-/* Public functions */
-
-/**
- * gtk_source_completion_info_new:
- *
- * Returns: a new GtkSourceCompletionInfo.
- */
 GtkSourceCompletionInfo *
-gtk_source_completion_info_new (void)
+_gtk_source_completion_info_new (void)
 {
-       return g_object_new (GTK_SOURCE_TYPE_COMPLETION_INFO,
-                            "margin-top", 3,
-                            "margin-bottom", 3,
-                            "margin-start", 3,
-                            "margin-end", 3,
-                            NULL);
+       return g_object_new (GTK_SOURCE_TYPE_COMPLETION_INFO, NULL);
 }
 
-/**
- * gtk_source_completion_info_move_to_iter:
- * @info: a #GtkSourceCompletionInfo.
- * @view: a #GtkTextView on which the info window should be positioned.
- * @iter: (nullable): a #GtkTextIter.
- *
- * Moves the #GtkSourceCompletionInfo to @iter. If @iter is %NULL @info is
- * moved to the cursor position. Moving will respect the #GdkGravity setting
- * of the info window and will ensure the line at @iter is not occluded by
- * the window.
- */
-void
-gtk_source_completion_info_move_to_iter (GtkSourceCompletionInfo *info,
-                                         GtkTextView             *view,
-                                         GtkTextIter             *iter)
+GtkSourceCompletionCell *
+_gtk_source_completion_info_get_cell (GtkSourceCompletionInfo *self)
 {
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_INFO (info));
-       g_return_if_fail (GTK_IS_TEXT_VIEW (view));
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_INFO (self), NULL);
 
-       if (iter == NULL)
-       {
-               move_to_cursor (info, view);
-       }
-       else
-       {
-               move_to_iter (info, view, iter);
-       }
+       return self->cell;
 }
diff --git a/gtksourceview/gtksourcecompletionlist-private.h b/gtksourceview/gtksourcecompletionlist-private.h
new file mode 100644
index 00000000..1b7f06fa
--- /dev/null
+++ b/gtksourceview/gtksourcecompletionlist-private.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "gtksourceassistant-private.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_COMPLETION_LIST (_gtk_source_completion_list_get_type())
+
+G_DECLARE_FINAL_TYPE (GtkSourceCompletionList, _gtk_source_completion_list, GTK_SOURCE, COMPLETION_LIST, 
GtkSourceAssistant)
+
+GtkSourceCompletionList    *_gtk_source_completion_list_new              (void);
+void                        _gtk_source_completion_list_reposition       (GtkSourceCompletionList    *self);
+GtkSourceCompletionContext *_gtk_source_completion_list_get_context      (GtkSourceCompletionList    *self);
+void                        _gtk_source_completion_list_set_context      (GtkSourceCompletionList    *self,
+                                                                          GtkSourceCompletionContext 
*context);
+gboolean                    _gtk_source_completion_list_get_show_details (GtkSourceCompletionList    *self);
+void                        _gtk_source_completion_list_set_show_details (GtkSourceCompletionList    *self,
+                                                                          gboolean                    
show_details);
+guint                       _gtk_source_completion_list_get_n_rows       (GtkSourceCompletionList    *self);
+void                        _gtk_source_completion_list_set_n_rows       (GtkSourceCompletionList    *self,
+                                                                          guint                       
n_rows);
+void                        _gtk_source_completion_list_set_font_desc    (GtkSourceCompletionList    *self,
+                                                                          const PangoFontDescription 
*font_desc);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletionlist.c b/gtksourceview/gtksourcecompletionlist.c
new file mode 100644
index 00000000..64f69727
--- /dev/null
+++ b/gtksourceview/gtksourcecompletionlist.c
@@ -0,0 +1,563 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gtksourcecompletioncell-private.h"
+#include "gtksourcecompletioncontext-private.h"
+#include "gtksourcecompletionlist-private.h"
+#include "gtksourcecompletionlistbox-private.h"
+#include "gtksourcecompletionlistboxrow-private.h"
+#include "gtksourcecompletioninfo-private.h"
+#include "gtksourcecompletionprovider.h"
+#include "gtksourceutils-private.h"
+#include "gtksourceview.h"
+
+struct _GtkSourceCompletionList
+{
+       GtkSourceAssistant          parent_instance;
+
+       GtkSourceCompletionContext *context;
+       GtkSourceCompletionInfo    *info;
+
+       /* Template Widgets */
+       GtkSourceCompletionListBox *listbox;
+       GtkScrolledWindow          *scroller;
+       GtkToggleButton            *show_details;
+       GtkBox                     *details;
+       GtkSourceCompletionCell    *comments;
+       GtkLabel                   *alternate_label;
+};
+
+enum {
+       PROP_0,
+       PROP_CONTEXT,
+       PROP_SHOW_DETAILS,
+       N_PROPS
+};
+
+G_DEFINE_TYPE (GtkSourceCompletionList, _gtk_source_completion_list, GTK_SOURCE_TYPE_ASSISTANT)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+_gtk_source_completion_list_show (GtkWidget *widget)
+{
+       GtkSourceCompletionList *self = (GtkSourceCompletionList *)widget;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (self));
+
+       _gtk_source_completion_list_reposition (self);
+
+       GTK_WIDGET_CLASS (_gtk_source_completion_list_parent_class)->show (widget);
+
+       if (_gtk_source_completion_list_get_show_details (self))
+       {
+               gtk_widget_show (GTK_WIDGET (self->info));
+       }
+}
+
+static void
+_gtk_source_completion_list_show_details_notify_active_cb (GtkSourceCompletionList *self,
+                                                           GParamSpec              *pspec,
+                                                           GtkToggleButton         *toggle)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (self));
+       g_assert (pspec != NULL);
+       g_assert (GTK_IS_TOGGLE_BUTTON (toggle));
+
+       if (gtk_widget_get_visible (GTK_WIDGET (self)) &&
+           _gtk_source_completion_list_get_show_details (self))
+       {
+               gtk_widget_show (GTK_WIDGET (self->info));
+       }
+       else
+       {
+               gtk_widget_hide (GTK_WIDGET (self->info));
+       }
+
+       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHOW_DETAILS]);
+}
+
+static void
+_gtk_source_completion_list_update_comment (GtkSourceCompletionList *self)
+{
+       GtkSourceCompletionCell *info = NULL;
+       GtkSourceCompletionProposal *proposal = NULL;
+       GtkSourceCompletionProvider *provider = NULL;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (self));
+
+       if (self->info != NULL)
+       {
+               info = _gtk_source_completion_info_get_cell (self->info);
+       }
+
+       if (_gtk_source_completion_list_box_get_selected (self->listbox, &provider, &proposal))
+       {
+               gtk_source_completion_provider_display (provider, self->context, proposal, self->comments);
+
+               if (info != NULL)
+               {
+                       gtk_source_completion_provider_display (provider, self->context, proposal, info);
+               }
+       }
+       else
+       {
+               gtk_source_completion_cell_set_widget (self->comments, NULL);
+
+               if (info != NULL)
+               {
+                       gtk_source_completion_cell_set_widget (info, NULL);
+               }
+       }
+
+       if (_gtk_source_completion_cell_is_empty (self->comments) &&
+           (info == NULL || _gtk_source_completion_cell_is_empty (info)))
+       {
+               gtk_widget_hide (GTK_WIDGET (self->details));
+       }
+       else
+       {
+               gtk_widget_show (GTK_WIDGET (self->details));
+       }
+
+       if (info != NULL)
+       {
+               if (_gtk_source_completion_cell_is_empty (info))
+               {
+                       gtk_widget_hide (GTK_WIDGET (self->info));
+               }
+               else if (_gtk_source_completion_list_get_show_details (self))
+               {
+                       if (gtk_widget_get_visible (GTK_WIDGET (self)))
+                       {
+                               gtk_widget_show (GTK_WIDGET (self->info));
+                       }
+               }
+       }
+
+       g_clear_object (&proposal);
+       g_clear_object (&provider);
+}
+
+static void
+_gtk_source_completion_list_notify_alternates_cb (GtkSourceCompletionList    *self,
+                                                  GParamSpec                 *pspec,
+                                                  GtkSourceCompletionListBox *listbox)
+{
+       int n_alternates;
+       int alternate;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (self));
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (listbox));
+
+       n_alternates = _gtk_source_completion_list_box_get_n_alternates (listbox);
+       alternate = _gtk_source_completion_list_box_get_alternate (listbox);
+
+       if (n_alternates == 0)
+       {
+               gtk_label_set_label (self->alternate_label, NULL);
+       }
+       else
+       {
+               char *str;
+
+               n_alternates++;
+               alternate = _gtk_source_completion_list_box_get_alternate (self->listbox);
+
+               if (alternate == -1)
+               {
+                       str = g_strdup_printf (_("%d of %u"), 1, n_alternates);
+               }
+               else
+               {
+                       str = g_strdup_printf (_("%d of %u"), alternate + 1, n_alternates);
+               }
+
+               gtk_label_set_label (self->alternate_label, str);
+
+               g_free (str);
+       }
+}
+
+static void
+_gtk_source_completion_list_notify_proposal (GtkSourceCompletionList    *self,
+                                             GParamSpec                 *pspec,
+                                             GtkSourceCompletionListBox *listbox)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (self));
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (listbox));
+
+       _gtk_source_completion_list_update_comment (self);
+       _gtk_source_completion_list_notify_alternates_cb (self, NULL, listbox);
+}
+
+static void
+_gtk_source_completion_list_get_offset (GtkSourceAssistant *assistant,
+                                        int                *x_offset,
+                                        int                *y_offset)
+{
+       GtkSourceCompletionList *self = (GtkSourceCompletionList *)assistant;
+       GtkSourceCompletionListBoxRow *row;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (self));
+       g_assert (x_offset != NULL);
+       g_assert (y_offset != NULL);
+
+       GTK_SOURCE_ASSISTANT_CLASS (_gtk_source_completion_list_parent_class)->get_offset (assistant, 
x_offset, y_offset);
+
+       if ((row = _gtk_source_completion_list_box_get_first (self->listbox)))
+       {
+               *x_offset = _gtk_source_completion_list_box_row_get_x_offset (row, GTK_WIDGET (self));
+       }
+}
+
+static gboolean
+key_press_propagate_cb (GtkSourceCompletionList *self,
+                        guint                    keyval,
+                        guint                    keycode,
+                        GdkModifierType          modifiers,
+                        GtkEventControllerKey   *key)
+{
+       GtkWidget *parent;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (self));
+       g_assert (GTK_IS_EVENT_CONTROLLER_KEY (key));
+
+       if (gtk_event_controller_key_forward (key, GTK_WIDGET (self->listbox)))
+       {
+               return TRUE;
+       }
+
+       parent = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_SOURCE_TYPE_VIEW);
+
+       if (GTK_SOURCE_IS_VIEW (parent))
+       {
+               return gtk_event_controller_key_forward (key, parent);
+       }
+
+       return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+key_release_propagate_cb (GtkSourceCompletionList *self,
+                          guint                    keyval,
+                          guint                    keycode,
+                          GdkModifierType          modifiers,
+                          GtkEventControllerKey   *key)
+{
+       GtkWidget *parent;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST (self));
+       g_assert (GTK_IS_EVENT_CONTROLLER_KEY (key));
+
+       if (gtk_event_controller_key_forward (key, GTK_WIDGET (self->listbox)))
+       {
+               return TRUE;
+       }
+
+       parent = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_SOURCE_TYPE_VIEW);
+
+       if (GTK_SOURCE_IS_VIEW (parent))
+       {
+               return gtk_event_controller_key_forward (key, parent);
+       }
+
+       return GDK_EVENT_PROPAGATE;
+}
+
+static void
+_gtk_source_completion_list_get_target_location (GtkSourceAssistant *assistant,
+                                                 GdkRectangle       *rect)
+{
+       g_assert (GTK_SOURCE_IS_ASSISTANT (assistant));
+       g_assert (rect != NULL);
+
+       GTK_SOURCE_ASSISTANT_CLASS (_gtk_source_completion_list_parent_class)->get_target_location 
(assistant, rect);
+
+       /* We want to align to the beginning of the character, so set the width
+        * to 0 to ensure things align correctly.
+        */
+       rect->width = 0;
+}
+
+static GtkSizeRequestMode
+_gtk_source_completion_list_get_request_mode (GtkWidget *widget)
+{
+       return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+}
+
+static void
+_gtk_source_completion_list_dispose (GObject *object)
+{
+       GtkSourceCompletionList *self = (GtkSourceCompletionList *)object;
+
+       g_clear_object (&self->context);
+
+       G_OBJECT_CLASS (_gtk_source_completion_list_parent_class)->dispose (object);
+}
+
+static void
+_gtk_source_completion_list_get_property (GObject    *object,
+                                          guint       prop_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+       GtkSourceCompletionList *self = GTK_SOURCE_COMPLETION_LIST (object);
+
+       switch (prop_id)
+       {
+       case PROP_CONTEXT:
+               g_value_set_object (value, self->context);
+               break;
+
+       case PROP_SHOW_DETAILS:
+               g_value_set_boolean (value, _gtk_source_completion_list_get_show_details (self));
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+_gtk_source_completion_list_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+       GtkSourceCompletionList *self = GTK_SOURCE_COMPLETION_LIST (object);
+
+       switch (prop_id)
+       {
+       case PROP_CONTEXT:
+               _gtk_source_completion_list_set_context (self, g_value_get_object (value));
+               break;
+
+       case PROP_SHOW_DETAILS:
+               _gtk_source_completion_list_set_show_details (self, g_value_get_boolean (value));
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+_gtk_source_completion_list_class_init (GtkSourceCompletionListClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+       GtkSourceAssistantClass *assistant_class = GTK_SOURCE_ASSISTANT_CLASS (klass);
+
+       object_class->dispose = _gtk_source_completion_list_dispose;
+       object_class->get_property = _gtk_source_completion_list_get_property;
+       object_class->set_property = _gtk_source_completion_list_set_property;
+
+       widget_class->get_request_mode = _gtk_source_completion_list_get_request_mode;
+       widget_class->show = _gtk_source_completion_list_show;
+
+       assistant_class->get_offset = _gtk_source_completion_list_get_offset;
+       assistant_class->get_target_location = _gtk_source_completion_list_get_target_location;
+
+       properties [PROP_CONTEXT] =
+               g_param_spec_object ("context",
+                                    "Context",
+                                    "The context containing results",
+                                    GTK_SOURCE_TYPE_COMPLETION_CONTEXT,
+                                    (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_SHOW_DETAILS] =
+               g_param_spec_boolean ("show-details",
+                                     "Show Details",
+                                     "Show the details assistant",
+                                     FALSE,
+                                     (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_properties (object_class, N_PROPS, properties);
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/gtksourceview/gtksourcecompletionlist.ui");
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionList, alternate_label);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionList, comments);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionList, details);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionList, listbox);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionList, scroller);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionList, show_details);
+       gtk_widget_class_bind_template_callback (widget_class, _gtk_source_completion_list_reposition);
+       gtk_widget_class_bind_template_callback (widget_class, _gtk_source_completion_list_notify_proposal);
+
+       g_type_ensure (GTK_SOURCE_TYPE_COMPLETION_LIST_BOX);
+}
+
+static void
+_gtk_source_completion_list_init (GtkSourceCompletionList *self)
+{
+       GtkEventController *key;
+
+       gtk_widget_init_template (GTK_WIDGET (self));
+       gtk_widget_add_css_class (GTK_WIDGET (self), "completion");
+       gtk_popover_set_position (GTK_POPOVER (self), GTK_POS_BOTTOM);
+
+       key = gtk_event_controller_key_new ();
+       gtk_event_controller_set_propagation_phase (key, GTK_PHASE_BUBBLE);
+       g_signal_connect_object (key,
+                                "key-pressed",
+                                G_CALLBACK (key_press_propagate_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+       g_signal_connect_object (key,
+                                "key-released",
+                                G_CALLBACK (key_release_propagate_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+       gtk_widget_add_controller (GTK_WIDGET (self), g_steal_pointer (&key));
+
+       self->info = GTK_SOURCE_COMPLETION_INFO (_gtk_source_completion_info_new ());
+       _gtk_source_assistant_attach (GTK_SOURCE_ASSISTANT (self->info),
+                                     GTK_SOURCE_ASSISTANT (self));
+
+       g_signal_connect_object (self->show_details,
+                                "notify::active",
+                                G_CALLBACK (_gtk_source_completion_list_show_details_notify_active_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+
+       g_signal_connect_object (self->listbox,
+                                "notify::alternate",
+                                G_CALLBACK (_gtk_source_completion_list_notify_alternates_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+
+       g_signal_connect_object (self->listbox,
+                                "notify::n-alternates",
+                                G_CALLBACK (_gtk_source_completion_list_notify_alternates_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+}
+
+GtkSourceCompletionList *
+_gtk_source_completion_list_new (void)
+{
+       return g_object_new (GTK_SOURCE_TYPE_COMPLETION_LIST, NULL);
+}
+
+GtkSourceCompletionContext *
+_gtk_source_completion_list_get_context (GtkSourceCompletionList *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST (self), NULL);
+
+       return self->context;
+}
+
+void
+_gtk_source_completion_list_set_context (GtkSourceCompletionList    *self,
+                                         GtkSourceCompletionContext *context)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST (self));
+       g_return_if_fail (!context || GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+
+       if (g_set_object (&self->context, context))
+       {
+               _gtk_source_completion_list_box_set_context (self->listbox, context);
+               g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONTEXT]);
+       }
+}
+
+gboolean
+_gtk_source_completion_list_get_show_details (GtkSourceCompletionList *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST (self), FALSE);
+
+       return gtk_toggle_button_get_active (self->show_details);
+}
+
+void
+_gtk_source_completion_list_set_show_details (GtkSourceCompletionList *self,
+                                              gboolean                 show_details)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST (self));
+
+       gtk_toggle_button_set_active (self->show_details, show_details);
+}
+
+guint
+_gtk_source_completion_list_get_n_rows (GtkSourceCompletionList *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST (self), 0);
+
+       return _gtk_source_completion_list_box_get_n_rows (self->listbox);
+}
+
+void
+_gtk_source_completion_list_set_n_rows (GtkSourceCompletionList *self,
+                                        guint                    n_rows)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST (self));
+
+       _gtk_source_completion_list_box_set_n_rows (self->listbox, n_rows);
+}
+
+void
+_gtk_source_completion_list_set_font_desc (GtkSourceCompletionList    *self,
+                                           const PangoFontDescription *font_desc)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST (self));
+
+       _gtk_source_completion_list_box_set_font_desc (self->listbox, font_desc);
+}
+
+void
+_gtk_source_completion_list_reposition (GtkSourceCompletionList *self)
+{
+       int old_x_offset, old_y_offset;
+       int x_offset = 0, y_offset = 0;
+       int min_width, nat_width;
+
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST (self));
+
+       /* Hack to force the sizing request, otherwise the popover does
+        * not shrink as results are reduced.
+        */
+       gtk_widget_set_size_request (GTK_WIDGET (self), -1, -1);
+       gtk_widget_measure (GTK_WIDGET (self),
+                           GTK_ORIENTATION_HORIZONTAL,
+                           -1,
+                           &min_width, &nat_width, NULL, NULL);
+       gtk_widget_set_size_request (GTK_WIDGET (self), min_width, -1);
+
+       /* Reposition the popup if we have a new offset */
+       _gtk_source_completion_list_get_offset (GTK_SOURCE_ASSISTANT (self), &x_offset, &y_offset);
+       gtk_popover_get_offset (GTK_POPOVER (self), &old_x_offset, &old_y_offset);
+       if (old_x_offset != x_offset || old_y_offset != y_offset)
+       {
+               gtk_popover_set_offset (GTK_POPOVER (self), x_offset, y_offset);
+       }
+
+       gtk_native_check_resize (GTK_NATIVE (self));
+
+       if (gtk_widget_get_visible (GTK_WIDGET (self->info)))
+       {
+               gtk_native_check_resize (GTK_NATIVE (self->info));
+       }
+}
diff --git a/gtksourceview/gtksourcecompletionlist.ui b/gtksourceview/gtksourcecompletionlist.ui
new file mode 100644
index 00000000..70d76ccb
--- /dev/null
+++ b/gtksourceview/gtksourcecompletionlist.ui
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtkSourceCompletionList" parent="GtkSourceAssistant">
+    <property name="can-focus">false</property>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="can-focus">false</property>
+        <child>
+          <object class="GtkScrolledWindow" id="scroller">
+            <property name="hscrollbar-policy">never</property>
+            <property name="vscrollbar-policy">automatic</property>
+            <property name="propagate-natural-width">true</property>
+            <property name="propagate-natural-height">true</property>
+            <property name="min-content-height">1</property>
+            <child>
+              <object class="GtkSourceCompletionListBox" id="listbox">
+                <property name="can-focus">false</property>
+                <signal name="notify::proposal" handler="_gtk_source_completion_list_notify_proposal" 
swapped="true"/>
+                <signal name="reposition" handler="_gtk_source_completion_list_reposition" swapped="true"/>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox" id="details">
+            <property name="can-focus">false</property>
+            <property name="halign">fill</property>
+            <property name="hexpand">true</property>
+            <property name="orientation">horizontal</property>
+            <property name="spacing">12</property>
+            <property name="vexpand">true</property>
+            <style>
+              <class name="details"/>
+            </style>
+            <child>
+              <object class="GtkSourceCompletionCell" id="comments">
+                <property name="column">comment</property>
+                <property name="hexpand">true</property>
+                <property name="valign">baseline</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="orientation">horizontal</property>
+                <property name="baseline-position">top</property>
+                <property name="valign">baseline</property>
+                <child>
+                  <object class="GtkLabel" id="alternate_label">
+                    <property name="margin-end">6</property>
+                    <property name="margin-start">6</property>
+                    <property name="valign">baseline</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkToggleButton" id="show_details">
+                    <property name="can-focus">false</property>
+                    <property name="label" translatable="yes">_Details</property>
+                    <property name="use-underline">true</property>
+                    <property name="halign">end</property>
+                    <property name="valign">start</property>
+                    <style>
+                      <class name="flat"/>
+                    </style>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/gtksourceview/gtksourcecompletionlistbox-private.h 
b/gtksourceview/gtksourcecompletionlistbox-private.h
new file mode 100644
index 00000000..16dcacbf
--- /dev/null
+++ b/gtksourceview/gtksourcecompletionlistbox-private.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "gtksourcetypes-private.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_COMPLETION_LIST_BOX (gtk_source_completion_list_box_get_type())
+
+G_DECLARE_FINAL_TYPE (GtkSourceCompletionListBox, gtk_source_completion_list_box, GTK_SOURCE, 
COMPLETION_LIST_BOX, GtkWidget)
+
+GtkWidget                     *_gtk_source_completion_list_box_new              (void);
+GtkSourceCompletionContext    *_gtk_source_completion_list_box_get_context      (GtkSourceCompletionListBox  
 *self);
+void                           _gtk_source_completion_list_box_set_context      (GtkSourceCompletionListBox  
 *self,
+                                                                                 GtkSourceCompletionContext  
 *context);
+guint                          _gtk_source_completion_list_box_get_n_rows       (GtkSourceCompletionListBox  
 *self);
+void                           _gtk_source_completion_list_box_set_n_rows       (GtkSourceCompletionListBox  
 *self,
+                                                                                 guint                       
  n_rows);
+GtkSourceCompletionProposal   *_gtk_source_completion_list_box_get_proposal     (GtkSourceCompletionListBox  
 *self);
+gboolean                       _gtk_source_completion_list_box_get_selected     (GtkSourceCompletionListBox  
 *self,
+                                                                                 GtkSourceCompletionProvider 
**provider,
+                                                                                 GtkSourceCompletionProposal 
**proposal);
+void                           _gtk_source_completion_list_box_move_cursor      (GtkSourceCompletionListBox  
 *self,
+                                                                                 GtkMovementStep             
  step,
+                                                                                 gint                        
  direction);
+void                           _gtk_source_completion_list_box_set_font_desc    (GtkSourceCompletionListBox  
 *self,
+                                                                                 const PangoFontDescription  
 *font_desc);
+GtkSourceCompletionListBoxRow *_gtk_source_completion_list_box_get_first        (GtkSourceCompletionListBox  
 *self);
+gboolean                       _gtk_source_completion_list_box_key_activates    (GtkSourceCompletionListBox  
 *self,
+                                                                                 const GdkKeyEvent           
 *key);
+int                            _gtk_source_completion_list_box_get_alternate    (GtkSourceCompletionListBox  
 *self);
+guint                          _gtk_source_completion_list_box_get_n_alternates (GtkSourceCompletionListBox  
 *self);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletionlistbox.c b/gtksourceview/gtksourcecompletionlistbox.c
new file mode 100644
index 00000000..07301719
--- /dev/null
+++ b/gtksourceview/gtksourcecompletionlistbox.c
@@ -0,0 +1,1265 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gtksourcecompletion-private.h"
+#include "gtksourcecompletioncontext-private.h"
+#include "gtksourcecompletionlistbox-private.h"
+#include "gtksourcecompletionlistboxrow-private.h"
+#include "gtksourcecompletionproposal.h"
+#include "gtksourcecompletionprovider.h"
+
+struct _GtkSourceCompletionListBox
+{
+       GtkWidget parent_instance;
+
+       /* The box containing the rows. */
+       GtkBox *box;
+
+       /* Font styling for rows */
+       PangoAttrList *font_attrs;
+
+       /* The completion context that is being displayed. */
+       GtkSourceCompletionContext *context;
+
+       /* The handler for GtkSourceCompletionContext::items-chaged which should
+        * be disconnected when no longer needed.
+        */
+       gulong items_changed_handler;
+
+       /* The number of rows we expect to have visible to the user. */
+       guint n_rows;
+
+       /* The currently selected index within the result set. Signed to
+        * ensure our math in various places allows going negative to catch
+        * lower edge.
+        */
+       int selected;
+
+       /* The alternate proposal for the current selection. This references
+        * something from GtkSourceCompletionProvider.list_alternates().
+        */
+       GPtrArray *alternates;
+       int alternate;
+
+       /* This is set whenever we make a change that requires updating the
+        * row content. We delay the update until the next frame callback so
+        * that we only update once right before we draw the frame. This helps
+        * reduce duplicate work when reacting to ::items-changed in the model.
+        */
+       guint queued_update;
+
+       /* These size groups are used to keep each portion of the proposal row
+        * aligned with each other. Since we only have a fixed number of visible
+        * rows, the overhead here is negligible by introducing the size cycle.
+        */
+       GtkSizeGroup *before_size_group;
+       GtkSizeGroup *typed_text_size_group;
+       GtkSizeGroup *after_size_group;
+
+       /* The adjustments for scrolling the GtkScrollable. */
+       GtkAdjustment *hadjustment;
+       GtkAdjustment *vadjustment;
+
+       /* Gesture to handle button press/touch events. */
+       GtkGesture *click_gesture;
+};
+
+typedef struct
+{
+       GtkSourceCompletionListBox *self;
+       GtkSourceCompletionContext *context;
+       guint n_items;
+       guint position;
+       int selected;
+} UpdateState;
+
+enum {
+       PROP_0,
+       PROP_ALTERNATE,
+       PROP_CONTEXT,
+       PROP_PROPOSAL,
+       PROP_N_ROWS,
+       PROP_HADJUSTMENT,
+       PROP_HSCROLL_POLICY,
+       PROP_N_ALTERNATES,
+       PROP_VADJUSTMENT,
+       PROP_VSCROLL_POLICY,
+       N_PROPS
+};
+
+enum {
+       REPOSITION,
+       N_SIGNALS
+};
+
+static void     gtk_source_completion_list_box_queue_update (GtkSourceCompletionListBox *self);
+static gboolean gtk_source_completion_list_box_update_cb    (GtkWidget                  *widget,
+                                                             GdkFrameClock              *frame_clock,
+                                                             gpointer                    user_data);
+static void     gtk_source_completion_list_box_do_update    (GtkSourceCompletionListBox *self,
+                                                             gboolean                    update_selection);
+
+G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionListBox, gtk_source_completion_list_box, GTK_TYPE_WIDGET,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static void
+gtk_source_completion_list_box_set_selected (GtkSourceCompletionListBox *self,
+                                             int                         selected)
+{
+       GtkSourceCompletionProposal *proposal = NULL;
+       GtkSourceCompletionProvider *provider = NULL;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       if (selected == -1 && self->context != NULL)
+       {
+               GtkSourceCompletion *completion;
+               gboolean select_on_show;
+
+               completion = gtk_source_completion_context_get_completion (self->context);
+               select_on_show = _gtk_source_completion_get_select_on_show (completion);
+
+               if (select_on_show)
+               {
+                       selected = 0;
+               }
+       }
+
+       self->selected = selected;
+       self->alternate = -1;
+       g_clear_pointer (&self->alternates, g_ptr_array_unref);
+
+       if (_gtk_source_completion_list_box_get_selected (self, &provider, &proposal))
+       {
+               self->alternates = gtk_source_completion_provider_list_alternates (provider, self->context, 
proposal);
+
+               if (self->alternates != NULL)
+               {
+                       g_ptr_array_set_free_func (self->alternates, g_object_unref);
+               }
+       }
+
+       gtk_source_completion_list_box_queue_update (self);
+
+       g_clear_object (&proposal);
+       g_clear_object (&provider);
+}
+
+static gboolean
+move_next_alternate (GtkWidget *widget,
+                     GVariant  *param,
+                     gpointer   user_data)
+{
+       GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)widget;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       if (self->alternates == NULL || self->alternates->len == 0)
+       {
+               return FALSE;
+       }
+
+       if ((guint)(self->alternate + 1) < self->alternates->len)
+       {
+               self->alternate++;
+       }
+       else
+       {
+               self->alternate = -1;
+       }
+
+       gtk_source_completion_list_box_do_update (self, FALSE);
+
+       return TRUE;
+}
+
+static void
+move_next_alternate_action (GtkWidget  *widget,
+                            const char *action_name,
+                            GVariant   *param)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (widget));
+
+       move_next_alternate (widget, param, NULL);
+}
+
+static gboolean
+move_previous_alternate (GtkWidget *widget,
+                         GVariant  *param,
+                         gpointer   user_data)
+{
+       GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)widget;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       if (self->alternates == NULL || self->alternates->len == 0)
+       {
+               return FALSE;
+       }
+
+       if (self->alternate < 0)
+       {
+               self->alternate = self->alternates->len - 1;
+       }
+       else
+       {
+               self->alternate--;
+       }
+
+       gtk_source_completion_list_box_do_update (self, FALSE);
+
+       return TRUE;
+}
+
+static void
+move_previous_alternate_action (GtkWidget  *widget,
+                                const char *action_name,
+                                GVariant   *param)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (widget));
+
+       move_previous_alternate (widget, param, NULL);
+}
+
+static guint
+gtk_source_completion_list_box_get_offset (GtkSourceCompletionListBox *self)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       return gtk_adjustment_get_value (self->vadjustment);
+}
+
+static void
+gtk_source_completion_list_box_set_offset (GtkSourceCompletionListBox *self,
+                                           guint                       offset)
+{
+       double value = offset;
+       double page_size;
+       double upper;
+       double lower;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       lower = gtk_adjustment_get_lower (self->vadjustment);
+       upper = gtk_adjustment_get_upper (self->vadjustment);
+       page_size = gtk_adjustment_get_page_size (self->vadjustment);
+
+       if (value > (upper - page_size))
+       {
+               value = upper - page_size;
+       }
+
+       if (value < lower)
+       {
+               value = lower;
+       }
+
+       gtk_adjustment_set_value (self->vadjustment, value);
+}
+
+static void
+gtk_source_completion_list_box_value_changed (GtkSourceCompletionListBox *self,
+                                              GtkAdjustment              *vadj)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+       g_assert (GTK_IS_ADJUSTMENT (vadj));
+
+       gtk_source_completion_list_box_queue_update (self);
+}
+
+static void
+gtk_source_completion_list_box_set_hadjustment (GtkSourceCompletionListBox *self,
+                                                GtkAdjustment              *hadjustment)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+       g_assert (!hadjustment || GTK_IS_ADJUSTMENT (hadjustment));
+
+       if (g_set_object (&self->hadjustment, hadjustment))
+       {
+               gtk_source_completion_list_box_queue_update (self);
+       }
+}
+
+static void
+gtk_source_completion_list_box_set_vadjustment (GtkSourceCompletionListBox *self,
+                                                GtkAdjustment        *vadjustment)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+       g_assert (!vadjustment || GTK_IS_ADJUSTMENT (vadjustment));
+
+       if (self->vadjustment == vadjustment)
+               return;
+
+       if (self->vadjustment)
+       {
+               g_signal_handlers_disconnect_by_func (self->vadjustment,
+                                                     G_CALLBACK 
(gtk_source_completion_list_box_value_changed),
+                                                     self);
+               g_clear_object (&self->vadjustment);
+       }
+
+       if (vadjustment)
+       {
+               self->vadjustment = g_object_ref (vadjustment);
+
+               gtk_adjustment_set_lower (self->vadjustment, 0);
+               gtk_adjustment_set_upper (self->vadjustment, 0);
+               gtk_adjustment_set_value (self->vadjustment, 0);
+               gtk_adjustment_set_step_increment (self->vadjustment, 1);
+               gtk_adjustment_set_page_size (self->vadjustment, self->n_rows);
+               gtk_adjustment_set_page_increment (self->vadjustment, self->n_rows);
+
+               g_signal_connect_object (self->vadjustment,
+                                        "value-changed",
+                                        G_CALLBACK (gtk_source_completion_list_box_value_changed),
+                                        self,
+                                        G_CONNECT_SWAPPED);
+       }
+
+       gtk_source_completion_list_box_queue_update (self);
+}
+
+static guint
+get_row_at_y (GtkSourceCompletionListBox *self,
+              double                      y)
+{
+       GtkAllocation alloc;
+       guint offset;
+       guint n_items;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+       g_assert (G_IS_LIST_MODEL (self->context));
+
+       gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+       offset = gtk_source_completion_list_box_get_offset (self);
+
+       n_items = g_list_model_get_n_items (G_LIST_MODEL (self->context));
+       n_items = MAX (1, MIN (self->n_rows, n_items));
+
+       return offset + (y / (alloc.height / n_items));
+}
+
+static void
+click_gesture_pressed (GtkGestureClick            *gesture,
+                       guint                       n_press,
+                       double                      x,
+                       double                      y,
+                       GtkSourceCompletionListBox *self)
+{
+       int selected;
+
+       g_assert (GTK_IS_GESTURE_CLICK (gesture));
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       if (self->context == NULL)
+       {
+               return;
+       }
+
+       selected = get_row_at_y (self, y);
+
+       if (selected != self->selected)
+       {
+               gtk_source_completion_list_box_set_selected (self, selected);
+       }
+       else
+       {
+               GtkSourceCompletionProvider *provider = NULL;
+               GtkSourceCompletionProposal *proposal = NULL;
+
+               if (self->selected >= 0 &&
+                   self->selected < (int)g_list_model_get_n_items (G_LIST_MODEL (self->context)) &&
+                   _gtk_source_completion_context_get_item_full (self->context, self->selected, &provider, 
&proposal))
+               {
+                       _gtk_source_completion_activate (gtk_source_completion_context_get_completion 
(self->context),
+                                                        self->context, provider, proposal);
+                       g_clear_object (&provider);
+                       g_clear_object (&proposal);
+               }
+       }
+}
+
+static gboolean
+move_binding_cb (GtkWidget *widget,
+                 GVariant  *param,
+                 gpointer   user_data)
+{
+       GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)widget;
+       int direction = 0;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       g_variant_get (param, "(i)", &direction);
+
+       if (ABS (direction) == 1)
+       {
+               _gtk_source_completion_list_box_move_cursor (self, GTK_MOVEMENT_DISPLAY_LINES, direction);
+       }
+       else
+       {
+               _gtk_source_completion_list_box_move_cursor (self, GTK_MOVEMENT_PAGES, direction > 0 ? 1 : 
-1);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+activate_nth_cb (GtkWidget *widget,
+                 GVariant  *param,
+                 gpointer   user_data)
+{
+       GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)widget;
+       GtkSourceCompletionProvider *provider = NULL;
+       GtkSourceCompletionProposal *proposal = NULL;
+       int nth = 0;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       if (self->context == NULL)
+       {
+               return FALSE;
+       }
+
+       g_variant_get (param, "(i)", &nth);
+
+       if (nth == 0 && self->selected >= 0)
+       {
+               nth = self->selected;
+       }
+       else
+       {
+               nth--;
+       }
+
+       if (nth < 0 || (guint)nth >= g_list_model_get_n_items (G_LIST_MODEL (self->context)))
+       {
+               return FALSE;
+       }
+
+       if (!_gtk_source_completion_context_get_item_full (self->context, nth, &provider, &proposal))
+       {
+               return FALSE;
+       }
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_assert (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal));
+
+       _gtk_source_completion_activate (gtk_source_completion_context_get_completion (self->context),
+                                        self->context,
+                                        provider,
+                                        proposal);
+
+       g_clear_object (&proposal);
+       g_clear_object (&provider);
+
+       return TRUE;
+}
+
+static gboolean
+_gtk_source_completion_list_box_key_pressed_cb (GtkSourceCompletionListBox *self,
+                                                guint                       keyval,
+                                                guint                       keycode,
+                                                GdkModifierType             state,
+                                                GtkEventControllerKey      *key)
+{
+       GtkSourceCompletionProvider *provider = NULL;
+       GtkSourceCompletionProposal *proposal = NULL;
+       gboolean ret = FALSE;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+       g_assert (GTK_IS_EVENT_CONTROLLER_KEY (key));
+
+       if (self->context == NULL)
+       {
+               return FALSE;
+       }
+
+       if (_gtk_source_completion_list_box_get_selected (self, &provider, &proposal))
+       {
+               if (gtk_source_completion_provider_key_activates (provider, self->context, proposal, keyval, 
state))
+               {
+                       _gtk_source_completion_activate (gtk_source_completion_context_get_completion 
(self->context),
+                                                        self->context,
+                                                        provider,
+                                                        proposal);
+                       ret = TRUE;
+               }
+       }
+
+       g_clear_object (&proposal);
+       g_clear_object (&provider);
+
+       return ret;
+}
+
+static void
+gtk_source_completion_list_box_constructed (GObject *object)
+{
+       GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)object;
+
+       G_OBJECT_CLASS (gtk_source_completion_list_box_parent_class)->constructed (object);
+
+       if (self->hadjustment == NULL)
+       {
+               self->hadjustment = gtk_adjustment_new (0, 0, 0, 0, 0, 0);
+       }
+
+       if (self->vadjustment == NULL)
+       {
+               self->vadjustment = gtk_adjustment_new (0, 0, 0, 0, 0, 0);
+       }
+
+       gtk_adjustment_set_lower (self->hadjustment, 0);
+       gtk_adjustment_set_upper (self->hadjustment, 0);
+       gtk_adjustment_set_value (self->hadjustment, 0);
+
+       gtk_source_completion_list_box_queue_update (self);
+}
+
+static void
+gtk_source_completion_list_box_dispose (GObject *object)
+{
+       GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)object;
+
+       if (self->box != NULL)
+       {
+               gtk_widget_unparent (GTK_WIDGET (self->box));
+               self->box = NULL;
+       }
+
+       g_clear_object (&self->before_size_group);
+       g_clear_object (&self->typed_text_size_group);
+       g_clear_object (&self->after_size_group);
+       g_clear_object (&self->hadjustment);
+       g_clear_object (&self->vadjustment);
+       g_clear_pointer (&self->font_attrs, pango_attr_list_unref);
+
+       G_OBJECT_CLASS (gtk_source_completion_list_box_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_completion_list_box_get_property (GObject    *object,
+                                             guint       prop_id,
+                                             GValue     *value,
+                                             GParamSpec *pspec)
+{
+       GtkSourceCompletionListBox *self = GTK_SOURCE_COMPLETION_LIST_BOX (object);
+
+       switch (prop_id)
+       {
+       case PROP_N_ALTERNATES:
+               g_value_set_int (value, self->alternates ? self->alternates->len : 0);
+               break;
+
+       case PROP_ALTERNATE:
+               g_value_set_int (value, self->alternate);
+               break;
+
+       case PROP_CONTEXT:
+               g_value_set_object (value, _gtk_source_completion_list_box_get_context (self));
+               break;
+
+       case PROP_PROPOSAL:
+               g_value_take_object (value, _gtk_source_completion_list_box_get_proposal (self));
+               break;
+
+       case PROP_N_ROWS:
+               g_value_set_uint (value, _gtk_source_completion_list_box_get_n_rows (self));
+               break;
+
+       case PROP_HADJUSTMENT:
+               g_value_set_object (value, self->hadjustment);
+               break;
+
+       case PROP_VADJUSTMENT:
+               g_value_set_object (value, self->vadjustment);
+               break;
+
+       case PROP_HSCROLL_POLICY:
+               g_value_set_enum (value, GTK_SCROLL_NATURAL);
+               break;
+
+       case PROP_VSCROLL_POLICY:
+               g_value_set_enum (value, GTK_SCROLL_NATURAL);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+gtk_source_completion_list_box_set_property (GObject      *object,
+                                             guint         prop_id,
+                                             const GValue *value,
+                                             GParamSpec   *pspec)
+{
+       GtkSourceCompletionListBox *self = GTK_SOURCE_COMPLETION_LIST_BOX (object);
+
+       switch (prop_id)
+       {
+       case PROP_CONTEXT:
+               _gtk_source_completion_list_box_set_context (self, g_value_get_object (value));
+               break;
+
+       case PROP_N_ROWS:
+               _gtk_source_completion_list_box_set_n_rows (self, g_value_get_uint (value));
+               break;
+
+       case PROP_HADJUSTMENT:
+               gtk_source_completion_list_box_set_hadjustment (self, g_value_get_object (value));
+               break;
+
+       case PROP_VADJUSTMENT:
+               gtk_source_completion_list_box_set_vadjustment (self, g_value_get_object (value));
+               break;
+
+       case PROP_HSCROLL_POLICY:
+               /* Do nothing */
+               break;
+
+       case PROP_VSCROLL_POLICY:
+               /* Do nothing */
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+gtk_source_completion_list_box_class_init (GtkSourceCompletionListBoxClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->constructed = gtk_source_completion_list_box_constructed;
+       object_class->dispose = gtk_source_completion_list_box_dispose;
+       object_class->get_property = gtk_source_completion_list_box_get_property;
+       object_class->set_property = gtk_source_completion_list_box_set_property;
+
+       properties [PROP_ALTERNATE] =
+               g_param_spec_int ("alternate",
+                                 "Alternate",
+                                 "The alternate to choose",
+                                 -1, G_MAXINT, -1,
+                                 (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_N_ALTERNATES] =
+               g_param_spec_int ("n-alternates",
+                                 "N Alternates",
+                                 "The number of alternates",
+                                 0, G_MAXINT, 0,
+                                 (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_CONTEXT] =
+               g_param_spec_object ("context",
+                                    "Context",
+                                    "The context being displayed",
+                                    GTK_SOURCE_TYPE_COMPLETION_CONTEXT,
+                                    (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_HADJUSTMENT] =
+               g_param_spec_object ("hadjustment", NULL, NULL,
+                                    GTK_TYPE_ADJUSTMENT,
+                                    (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_HSCROLL_POLICY] =
+               g_param_spec_enum ("hscroll-policy", NULL, NULL,
+                                  GTK_TYPE_SCROLLABLE_POLICY,
+                                  GTK_SCROLL_NATURAL,
+                                  (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_VADJUSTMENT] =
+               g_param_spec_object ("vadjustment", NULL, NULL,
+                                    GTK_TYPE_ADJUSTMENT,
+                                    (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_VSCROLL_POLICY] =
+               g_param_spec_enum ("vscroll-policy", NULL, NULL,
+                                  GTK_TYPE_SCROLLABLE_POLICY,
+                                  GTK_SCROLL_NATURAL,
+                                  (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_PROPOSAL] =
+               g_param_spec_object ("proposal",
+                                    "Proposal",
+                                    "The proposal that is currently selected",
+                                    GTK_SOURCE_TYPE_COMPLETION_PROPOSAL,
+                                    (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_N_ROWS] =
+               g_param_spec_uint ("n-rows",
+                                  "N Rows",
+                                  "The number of visible rows",
+                                  1, 32, 5,
+                                  (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | 
G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_properties (object_class, N_PROPS, properties);
+
+       signals [REPOSITION] =
+               g_signal_new_class_handler ("reposition",
+                                           G_TYPE_FROM_CLASS (klass),
+                                           G_SIGNAL_RUN_LAST,
+                                           NULL, NULL, NULL,
+                                           g_cclosure_marshal_VOID__VOID,
+                                           G_TYPE_NONE, 0);
+       g_signal_set_va_marshaller (signals [REPOSITION],
+                                   G_TYPE_FROM_CLASS (klass),
+                                   g_cclosure_marshal_VOID__VOIDv);
+
+       gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+       gtk_widget_class_set_css_name (widget_class, "list");
+
+       gtk_widget_class_install_action (widget_class, "proposal.move-next-alternate", NULL, 
move_next_alternate_action);
+       gtk_widget_class_install_action (widget_class, "proposal.move-previous-alternate", NULL, 
move_previous_alternate_action);
+
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_Down, 0, move_binding_cb, "(i)", 1);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_Up, 0, move_binding_cb, "(i)", -1);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_Page_Up, 0, move_binding_cb, "(i)", -2);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_Page_Down, 0, move_binding_cb, "(i)", 2);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_1, GDK_ALT_MASK, activate_nth_cb, "(i)", 1);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_2, GDK_ALT_MASK, activate_nth_cb, "(i)", 2);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_3, GDK_ALT_MASK, activate_nth_cb, "(i)", 3);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_4, GDK_ALT_MASK, activate_nth_cb, "(i)", 4);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_5, GDK_ALT_MASK, activate_nth_cb, "(i)", 5);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_6, GDK_ALT_MASK, activate_nth_cb, "(i)", 6);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_7, GDK_ALT_MASK, activate_nth_cb, "(i)", 7);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_8, GDK_ALT_MASK, activate_nth_cb, "(i)", 8);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_9, GDK_ALT_MASK, activate_nth_cb, "(i)", 9);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_Return, 0, activate_nth_cb, "(i)", 0);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_KP_Enter, 0, activate_nth_cb, "(i)", 0);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_Tab, 0, activate_nth_cb, "(i)", 0);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_Right, 0, move_next_alternate, NULL);
+       gtk_widget_class_add_binding (widget_class, GDK_KEY_Left, 0, move_previous_alternate, NULL);
+       gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "assistant.hide", NULL);
+
+       g_type_ensure (GTK_SOURCE_TYPE_COMPLETION_LIST_BOX_ROW);
+}
+
+static void
+gtk_source_completion_list_box_init (GtkSourceCompletionListBox *self)
+{
+       GtkEventController *key;
+
+       key = gtk_event_controller_key_new ();
+       g_signal_connect_swapped (key,
+                                 "key-pressed",
+                                 G_CALLBACK (_gtk_source_completion_list_box_key_pressed_cb),
+                                 self);
+       gtk_widget_add_controller (GTK_WIDGET (self), key);
+
+       self->box = g_object_new (GTK_TYPE_BOX,
+                                 "orientation", GTK_ORIENTATION_VERTICAL,
+                                 "visible", TRUE,
+                                 NULL);
+       gtk_widget_set_parent (GTK_WIDGET (self->box), GTK_WIDGET (self));
+
+       self->selected = -1;
+       self->alternate = -1;
+       self->before_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+       self->typed_text_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+       self->after_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+       self->click_gesture = gtk_gesture_click_new ();
+       gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->click_gesture), 
GTK_PHASE_BUBBLE);
+       gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (self->click_gesture), FALSE);
+       gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->click_gesture), GDK_BUTTON_PRIMARY);
+       g_signal_connect_object (self->click_gesture, "pressed",
+                                G_CALLBACK (click_gesture_pressed), self, 0);
+       gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (self->click_gesture));
+}
+
+static void
+gtk_source_completion_list_box_do_update (GtkSourceCompletionListBox *self,
+                                          gboolean                    update_selection)
+{
+       UpdateState state = {0};
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       state.self = self;
+       state.context = self->context;
+       state.position = gtk_source_completion_list_box_get_offset (self);
+       state.selected = self->selected;
+
+       if (self->context != NULL)
+       {
+               state.n_items = g_list_model_get_n_items (G_LIST_MODEL (self->context));
+       }
+
+       state.position = MIN (state.position, MAX (state.n_items, self->n_rows) - self->n_rows);
+       state.selected = MIN (self->selected, state.n_items ? (int)state.n_items - 1 : 0);
+
+       if (gtk_adjustment_get_upper (self->vadjustment) != state.n_items)
+       {
+               gtk_adjustment_set_upper (self->vadjustment, state.n_items);
+       }
+
+       for (GtkWidget *iter = gtk_widget_get_first_child (GTK_WIDGET (self->box));
+            iter != NULL;
+            iter = gtk_widget_get_next_sibling (iter))
+       {
+               GtkSourceCompletionProposal *proposal = NULL;
+               GtkSourceCompletionProvider *provider = NULL;
+               gboolean has_alternates = FALSE;
+
+               g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX_ROW (iter));
+
+               if (state.selected >= 0 && state.position == (guint)state.selected)
+               {
+                       gtk_widget_set_state_flags (iter, GTK_STATE_FLAG_SELECTED, FALSE);
+               }
+               else
+               {
+                       gtk_widget_unset_state_flags (iter, GTK_STATE_FLAG_SELECTED);
+               }
+
+               if (state.context != NULL && state.position < state.n_items)
+               {
+                       _gtk_source_completion_context_get_item_full (state.context,
+                                                                     state.position,
+                                                                     &provider,
+                                                                     &proposal);
+
+                       if (state.selected == (int)state.position)
+                       {
+                               if (self->alternate >= 0 && self->alternate < (int)self->alternates->len)
+                               {
+                                       g_clear_object (&proposal);
+                                       proposal = g_object_ref (g_ptr_array_index (self->alternates, 
self->alternate));
+                               }
+
+                               has_alternates = self->alternates != NULL && self->alternates->len > 0;
+                       }
+
+                       _gtk_source_completion_list_box_row_display (GTK_SOURCE_COMPLETION_LIST_BOX_ROW 
(iter),
+                                                                    state.context,
+                                                                    provider,
+                                                                    proposal,
+                                                                    has_alternates);
+
+                       if (gtk_widget_get_visible (iter))
+                       {
+                               gtk_widget_queue_resize (iter);
+                       }
+                       else
+                       {
+                               gtk_widget_show (iter);
+                       }
+               }
+               else
+               {
+                       _gtk_source_completion_list_box_row_display (GTK_SOURCE_COMPLETION_LIST_BOX_ROW 
(iter),
+                                                                    NULL, NULL, NULL, FALSE);
+                       gtk_widget_hide (GTK_WIDGET (iter));
+               }
+
+               state.position++;
+
+               g_clear_object (&proposal);
+               g_clear_object (&provider);
+       }
+
+       if (update_selection)
+       {
+               gtk_source_completion_list_box_set_selected (self, state.selected);
+       }
+
+       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROPOSAL]);
+       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_N_ALTERNATES]);
+       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ALTERNATE]);
+
+       g_signal_emit (self, signals [REPOSITION], 0);
+}
+
+static gboolean
+gtk_source_completion_list_box_update_cb (GtkWidget     *widget,
+                                          GdkFrameClock *frame_clock,
+                                          gpointer       user_data)
+{
+       GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)widget;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       gtk_source_completion_list_box_do_update (self, TRUE);
+       self->queued_update = 0;
+       return G_SOURCE_REMOVE;
+}
+
+static void
+gtk_source_completion_list_box_queue_update (GtkSourceCompletionListBox *self)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       /* See gtk_source_completion_list_box_set_selected() for while this needs
+        * to be set here to avoid re-entrancy.
+        */
+       if (self->queued_update == 0)
+       {
+               self->queued_update = gtk_widget_add_tick_callback (GTK_WIDGET (self),
+                                                                   gtk_source_completion_list_box_update_cb,
+                                                                   NULL, NULL);
+       }
+}
+
+GtkWidget *
+_gtk_source_completion_list_box_new (void)
+{
+       return g_object_new (GTK_SOURCE_TYPE_COMPLETION_LIST_BOX, NULL);
+}
+
+guint
+_gtk_source_completion_list_box_get_n_rows (GtkSourceCompletionListBox *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self), 0);
+
+       return self->n_rows;
+}
+
+void
+_gtk_source_completion_list_box_set_n_rows (GtkSourceCompletionListBox *self,
+                                            guint                       n_rows)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+       g_return_if_fail (n_rows > 0);
+       g_return_if_fail (n_rows <= 32);
+
+       if (n_rows != self->n_rows)
+       {
+               GtkWidget *tmp;
+
+               while ((tmp = gtk_widget_get_first_child (GTK_WIDGET (self->box))))
+               {
+                       gtk_box_remove (self->box, tmp);
+               }
+
+               self->n_rows = n_rows;
+
+               if (self->vadjustment != NULL)
+               {
+                       gtk_adjustment_set_page_size (self->vadjustment, n_rows);
+               }
+
+               for (guint i = 0; i < n_rows; i++)
+               {
+                       GtkWidget *row;
+
+                       row = _gtk_source_completion_list_box_row_new ();
+                       gtk_widget_set_can_focus (GTK_WIDGET (row), FALSE);
+                       _gtk_source_completion_list_box_row_attach (GTK_SOURCE_COMPLETION_LIST_BOX_ROW (row),
+                                                                   self->before_size_group,
+                                                                   self->typed_text_size_group,
+                                                                   self->after_size_group);
+                       _gtk_source_completion_list_box_row_set_attrs (GTK_SOURCE_COMPLETION_LIST_BOX_ROW 
(row),
+                                                                      self->font_attrs);
+                       gtk_box_append (self->box, row);
+               }
+
+               gtk_source_completion_list_box_queue_update (self);
+
+               g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_N_ROWS]);
+       }
+}
+
+/**
+ * gtk_source_completion_list_box_get_proposal:
+ * @self: a #GtkSourceCompletionListBox
+ *
+ * Gets the currently selected proposal, or %NULL if no proposal is selected
+ *
+ * Returns: (nullable) (transfer full): a #GtkSourceCompletionProposal or %NULL
+ *
+ * Since: 5.0
+ */
+GtkSourceCompletionProposal *
+_gtk_source_completion_list_box_get_proposal (GtkSourceCompletionListBox *self)
+{
+       GtkSourceCompletionProposal *ret = NULL;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self), NULL);
+
+       if (self->context != NULL &&
+           self->selected < (int)g_list_model_get_n_items (G_LIST_MODEL (self->context)))
+               ret = g_list_model_get_item (G_LIST_MODEL (self->context), self->selected);
+
+       g_return_val_if_fail (!ret || GTK_SOURCE_IS_COMPLETION_PROPOSAL (ret), NULL);
+
+       return ret;
+}
+
+/**
+ * _gtk_source_completion_list_box_get_selected:
+ * @self: an #GtkSourceCompletionListBox
+ * @provider: (out) (transfer full) (optional): a location for the provider
+ * @proposal: (out) (transfer full) (optional): a location for the proposal
+ *
+ * Gets the selected item if there is any.
+ *
+ * If there is a selection, %TRUE is returned and @provider and @proposal
+ * are set to the selected provider/proposal.
+ *
+ * Returns: %TRUE if there is a selection
+ *
+ * Since: 5.0
+ */
+gboolean
+_gtk_source_completion_list_box_get_selected (GtkSourceCompletionListBox   *self,
+                                              GtkSourceCompletionProvider **provider,
+                                              GtkSourceCompletionProposal **proposal)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self), FALSE);
+
+       if (self->context != NULL)
+       {
+               guint n_items = g_list_model_get_n_items (G_LIST_MODEL (self->context));
+
+               if (n_items > 0)
+               {
+                       if (self->selected >= 0)
+                       {
+                               gint selected = MIN (self->selected, (int)n_items - 1);
+                               _gtk_source_completion_context_get_item_full (self->context, selected, 
provider, proposal);
+                               return TRUE;
+                       }
+               }
+       }
+
+       return FALSE;
+}
+
+/**
+ * gtk_source_completion_list_box_get_context:
+ * @self: a #GtkSourceCompletionListBox
+ *
+ * Gets the context that is being displayed in the list box.
+ *
+ * Returns: (transfer none) (nullable): an #GtkSourceCompletionContext or %NULL
+ *
+ * Since: 5.0
+ */
+GtkSourceCompletionContext *
+_gtk_source_completion_list_box_get_context (GtkSourceCompletionListBox *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self), NULL);
+
+       return self->context;
+}
+
+static void
+gtk_source_completion_list_box_items_changed_cb (GtkSourceCompletionListBox *self,
+                                                 guint                       position,
+                                                 guint                       removed,
+                                                 guint                       added,
+                                                 GListModel                 *model)
+{
+       guint offset;
+
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+       g_assert (G_IS_LIST_MODEL (model));
+
+       offset = gtk_source_completion_list_box_get_offset (self);
+
+       /* Skip widget resize if results are after visible area */
+       if (position >= offset + self->n_rows)
+               return;
+
+       gtk_source_completion_list_box_queue_update (self);
+}
+
+/**
+ * gtk_source_completion_list_box_set_context:
+ * @self: a #GtkSourceCompletionListBox
+ * @context: the #GtkSourceCompletionContext
+ *
+ * Sets the context to be displayed.
+ *
+ * Since: 5.0
+ */
+void
+_gtk_source_completion_list_box_set_context (GtkSourceCompletionListBox *self,
+                                             GtkSourceCompletionContext *context)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+       g_return_if_fail (!context || GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+
+       if (self->context == context)
+               return;
+
+       if (self->context != NULL && self->items_changed_handler != 0)
+       {
+               g_signal_handler_disconnect (self->context, self->items_changed_handler);
+               self->items_changed_handler = 0;
+       }
+
+       g_set_object (&self->context, context);
+
+       if (self->context != NULL)
+       {
+               self->items_changed_handler =
+                       g_signal_connect_object (self->context,
+                                                "items-changed",
+                                                G_CALLBACK (gtk_source_completion_list_box_items_changed_cb),
+                                                self,
+                                                G_CONNECT_SWAPPED);
+       }
+
+       gtk_source_completion_list_box_set_selected (self, -1);
+       gtk_adjustment_set_value (self->vadjustment, 0);
+
+       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONTEXT]);
+}
+
+GtkSourceCompletionListBoxRow *
+_gtk_source_completion_list_box_get_first (GtkSourceCompletionListBox *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self), NULL);
+
+       return GTK_SOURCE_COMPLETION_LIST_BOX_ROW (gtk_widget_get_first_child (GTK_WIDGET (self->box)));
+}
+
+void
+_gtk_source_completion_list_box_move_cursor (GtkSourceCompletionListBox *self,
+                                             GtkMovementStep             step,
+                                             int                         direction)
+{
+       int n_items;
+       int offset;
+
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       if (self->context == NULL || direction == 0)
+               return;
+
+       if (!(n_items = g_list_model_get_n_items (G_LIST_MODEL (self->context))))
+               return;
+
+       /* n_items is signed so that we can do negative comparison */
+       if (n_items < 0)
+               return;
+
+       if (step == GTK_MOVEMENT_BUFFER_ENDS)
+       {
+               if (direction > 0)
+               {
+                       gtk_source_completion_list_box_set_offset (self, n_items);
+                       gtk_source_completion_list_box_set_selected (self, n_items - 1);
+               }
+               else
+               {
+                       gtk_source_completion_list_box_set_offset (self, 0);
+                       gtk_source_completion_list_box_set_selected (self, -1);
+               }
+
+               gtk_source_completion_list_box_queue_update (self);
+
+               return;
+       }
+
+       if ((direction < 0 && self->selected == 0) ||
+           (direction > 0 && self->selected == n_items - 1))
+       {
+               return;
+       }
+
+       if (step == GTK_MOVEMENT_PAGES)
+       {
+               direction *= self->n_rows;
+       }
+
+       if ((self->selected + direction) > n_items)
+       {
+               gtk_source_completion_list_box_set_selected (self, n_items - 1);
+       }
+       else if ((self->selected + direction) < 0)
+       {
+               gtk_source_completion_list_box_set_selected (self, 0);
+       }
+       else
+       {
+               gtk_source_completion_list_box_set_selected (self, self->selected + direction);
+       }
+
+       offset = gtk_source_completion_list_box_get_offset (self);
+
+       if (self->selected < offset)
+       {
+               gtk_source_completion_list_box_set_offset (self, self->selected);
+       }
+       else if (self->selected >= (int)(offset + self->n_rows))
+       {
+               gtk_source_completion_list_box_set_offset (self, self->selected - self->n_rows + 1);
+       }
+
+       gtk_source_completion_list_box_queue_update (self);
+}
+
+void
+_gtk_source_completion_list_box_set_font_desc (GtkSourceCompletionListBox *self,
+                                               const PangoFontDescription *font_desc)
+{
+       GtkWidget *iter;
+
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+
+       g_clear_pointer (&self->font_attrs, pango_attr_list_unref);
+
+       if (font_desc)
+       {
+               self->font_attrs = pango_attr_list_new ();
+
+               if (font_desc != NULL)
+               {
+                       pango_attr_list_insert (self->font_attrs, pango_attr_font_desc_new (font_desc));
+                       pango_attr_list_insert (self->font_attrs, pango_attr_font_features_new ("tnum"));
+               }
+       }
+
+       for ((iter = gtk_widget_get_first_child (GTK_WIDGET (self->box)));
+            iter != NULL;
+            iter = gtk_widget_get_next_sibling (iter))
+       {
+               _gtk_source_completion_list_box_row_set_attrs (GTK_SOURCE_COMPLETION_LIST_BOX_ROW (iter),
+                                                              self->font_attrs);
+       }
+}
+
+int
+_gtk_source_completion_list_box_get_alternate (GtkSourceCompletionListBox *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self), 0);
+
+       return self->alternate + 1;
+}
+
+guint
+_gtk_source_completion_list_box_get_n_alternates (GtkSourceCompletionListBox *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self), 0);
+
+       return self->alternates ? self->alternates->len : 0;
+}
diff --git a/gtksourceview/gtksourcecompletionlistboxrow-private.h 
b/gtksourceview/gtksourcecompletionlistboxrow-private.h
new file mode 100644
index 00000000..7e2edda5
--- /dev/null
+++ b/gtksourceview/gtksourcecompletionlistboxrow-private.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "gtksourcetypes.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_COMPLETION_LIST_BOX_ROW (gtk_source_completion_list_box_row_get_type())
+
+G_DECLARE_FINAL_TYPE (GtkSourceCompletionListBoxRow, gtk_source_completion_list_box_row, GTK_SOURCE, 
COMPLETION_LIST_BOX_ROW, GtkListBoxRow)
+
+GtkWidget                   *_gtk_source_completion_list_box_row_new          (void);
+void                         _gtk_source_completion_list_box_row_display      (GtkSourceCompletionListBoxRow 
*self,
+                                                                               GtkSourceCompletionContext    
*context,
+                                                                               GtkSourceCompletionProvider   
*provider,
+                                                                               GtkSourceCompletionProposal   
*proposal,
+                                                                               gboolean                      
 has_alternates);
+gint                         _gtk_source_completion_list_box_row_get_x_offset (GtkSourceCompletionListBoxRow 
*self,
+                                                                               GtkWidget                     
*toplevel);
+void                         _gtk_source_completion_list_box_row_set_attrs    (GtkSourceCompletionListBoxRow 
*self,
+                                                                               PangoAttrList                 
*attrs);
+void                         _gtk_source_completion_list_box_row_attach       (GtkSourceCompletionListBoxRow 
*self,
+                                                                               GtkSizeGroup                  
*before,
+                                                                               GtkSizeGroup                  
*typed_text,
+                                                                               GtkSizeGroup                  
*after);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletionlistboxrow.c b/gtksourceview/gtksourcecompletionlistboxrow.c
new file mode 100644
index 00000000..3d3a81c2
--- /dev/null
+++ b/gtksourceview/gtksourcecompletionlistboxrow.c
@@ -0,0 +1,212 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gtksourcecompletioncell-private.h"
+#include "gtksourcecompletioncontext-private.h"
+#include "gtksourcecompletionlistboxrow-private.h"
+#include "gtksourcecompletionproposal.h"
+#include "gtksourcecompletionprovider.h"
+
+struct _GtkSourceCompletionListBoxRow
+{
+       GtkListBoxRow                parent_instance;
+
+       GtkSourceCompletionProposal *proposal;
+       PangoAttrList               *attrs;
+
+       GtkBox                      *box;
+       GtkBox                      *more;
+       GtkSourceCompletionCell     *icon;
+       GtkSourceCompletionCell     *before;
+       GtkSourceCompletionCell     *typed_text;
+       GtkSourceCompletionCell     *after;
+};
+
+enum {
+       PROP_0,
+       PROP_PROPOSAL,
+       N_PROPS
+};
+
+G_DEFINE_TYPE (GtkSourceCompletionListBoxRow, gtk_source_completion_list_box_row, GTK_TYPE_LIST_BOX_ROW)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gtk_source_completion_list_box_row_finalize (GObject *object)
+{
+       GtkSourceCompletionListBoxRow *self = (GtkSourceCompletionListBoxRow *)object;
+
+       g_clear_pointer (&self->attrs, pango_attr_list_unref);
+
+       G_OBJECT_CLASS (gtk_source_completion_list_box_row_parent_class)->finalize (object);
+}
+
+static void
+gtk_source_completion_list_box_row_class_init (GtkSourceCompletionListBoxRowClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->finalize = gtk_source_completion_list_box_row_finalize;
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/gtksourceview/gtksourcecompletionlistboxrow.ui");
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionListBoxRow, box);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionListBoxRow, icon);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionListBoxRow, before);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionListBoxRow, typed_text);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionListBoxRow, after);
+       gtk_widget_class_bind_template_child (widget_class, GtkSourceCompletionListBoxRow, more);
+
+       g_type_ensure (GTK_SOURCE_TYPE_COMPLETION_CELL);
+}
+
+static void
+gtk_source_completion_list_box_row_init (GtkSourceCompletionListBoxRow *self)
+{
+       gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget *
+_gtk_source_completion_list_box_row_new (void)
+{
+       return g_object_new (GTK_SOURCE_TYPE_COMPLETION_LIST_BOX_ROW, NULL);
+}
+
+GtkSourceCompletionProposal *
+_gtk_source_completion_list_box_row_get_proposal (GtkSourceCompletionListBoxRow *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX_ROW (self), NULL);
+
+       return self->proposal;
+}
+
+void
+_gtk_source_completion_list_box_row_display (GtkSourceCompletionListBoxRow *self,
+                                             GtkSourceCompletionContext    *context,
+                                             GtkSourceCompletionProvider   *provider,
+                                             GtkSourceCompletionProposal   *proposal,
+                                             gboolean                       has_alternates)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX_ROW (self));
+       g_return_if_fail (!context || GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+       g_return_if_fail (!provider || GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_return_if_fail (!proposal || GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal));
+
+       if (proposal == NULL)
+       {
+               gtk_source_completion_cell_set_widget (self->icon, NULL);
+               gtk_source_completion_cell_set_widget (self->before, NULL);
+               gtk_source_completion_cell_set_widget (self->typed_text, NULL);
+               gtk_source_completion_cell_set_widget (self->after, NULL);
+       }
+       else
+       {
+               gtk_source_completion_provider_display (provider, context, proposal, self->icon);
+               gtk_source_completion_provider_display (provider, context, proposal, self->before);
+               gtk_source_completion_provider_display (provider, context, proposal, self->typed_text);
+               gtk_source_completion_provider_display (provider, context, proposal, self->after);
+       }
+
+       gtk_widget_set_visible (GTK_WIDGET (self->more), has_alternates);
+}
+
+void
+_gtk_source_completion_list_box_row_attach (GtkSourceCompletionListBoxRow *self,
+                                            GtkSizeGroup                  *before,
+                                            GtkSizeGroup                  *typed_text,
+                                            GtkSizeGroup                  *after)
+{
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX_ROW (self));
+       g_return_if_fail (GTK_IS_SIZE_GROUP (before));
+       g_return_if_fail (GTK_IS_SIZE_GROUP (typed_text));
+       g_return_if_fail (GTK_IS_SIZE_GROUP (after));
+
+       gtk_size_group_add_widget (before, GTK_WIDGET (self->before));
+       gtk_size_group_add_widget (typed_text, GTK_WIDGET (self->typed_text));
+       gtk_size_group_add_widget (after, GTK_WIDGET (self->after));
+}
+
+static void
+get_margin_and_border (GtkWidget *widget,
+                       GtkBorder *margin,
+                       GtkBorder *border)
+{
+       GtkStyleContext *style_context = gtk_widget_get_style_context (widget);
+       gtk_style_context_get_margin (style_context, margin);
+       gtk_style_context_get_border (style_context, border);
+}
+
+gint
+_gtk_source_completion_list_box_row_get_x_offset (GtkSourceCompletionListBoxRow *self,
+                                                  GtkWidget                     *toplevel)
+{
+       GtkStyleContext *style_context;
+       GtkRequisition min;
+       GtkRequisition nat;
+       GtkBorder margin;
+       GtkBorder border;
+       double x = 0;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX_ROW (self), 0);
+       g_return_val_if_fail (GTK_IS_WIDGET (toplevel), 0);
+
+       for (GtkWidget *iter = GTK_WIDGET (self->box);
+            iter != NULL;
+            iter = gtk_widget_get_parent (iter))
+       {
+               get_margin_and_border (iter, &margin, &border);
+               x += margin.left + border.left;
+
+               if (iter == toplevel)
+               {
+                       break;
+               }
+       }
+
+       get_margin_and_border (GTK_WIDGET (self->icon), &margin, &border);
+       gtk_widget_get_preferred_size (GTK_WIDGET (self->icon), &min, &nat);
+       x += margin.left + border.left + nat.width + margin.right + border.right;
+
+       get_margin_and_border (GTK_WIDGET (self->before), &margin, &border);
+       gtk_widget_get_preferred_size (GTK_WIDGET (self->before), &min, &nat);
+       x += margin.left + border.left + nat.width + border.right + margin.right;
+
+       get_margin_and_border (GTK_WIDGET (self->typed_text), &margin, &border);
+       gtk_widget_get_preferred_size (GTK_WIDGET (self->typed_text), &min, &nat);
+       x += margin.left + border.left;
+
+       return -x;
+}
+
+void
+_gtk_source_completion_list_box_row_set_attrs (GtkSourceCompletionListBoxRow *self,
+                                               PangoAttrList                 *attrs)
+{
+       g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX_ROW (self));
+
+       _gtk_source_completion_cell_set_attrs (self->icon, attrs);
+       _gtk_source_completion_cell_set_attrs (self->before, attrs);
+       _gtk_source_completion_cell_set_attrs (self->typed_text, attrs);
+       _gtk_source_completion_cell_set_attrs (self->after, attrs);
+}
diff --git a/gtksourceview/gtksourcecompletionlistboxrow.ui b/gtksourceview/gtksourcecompletionlistboxrow.ui
new file mode 100644
index 00000000..671fce2f
--- /dev/null
+++ b/gtksourceview/gtksourcecompletionlistboxrow.ui
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtkSourceCompletionListBoxRow" parent="GtkListBoxRow">
+    <property name="can-focus">false</property>
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="can-focus">false</property>
+        <property name="hexpand">true</property>
+        <property name="orientation">horizontal</property>
+        <child>
+          <object class="GtkSourceCompletionCell" id="icon">
+            <property name="can-focus">false</property>
+            <property name="column">icon</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSourceCompletionCell" id="before">
+            <property name="can-focus">false</property>
+            <property name="column">before</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSourceCompletionCell" id="typed_text">
+            <property name="can-focus">false</property>
+            <property name="column">typed-text</property>
+            <property name="hexpand">true</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSourceCompletionCell" id="after">
+            <property name="can-focus">false</property>
+            <property name="column">after</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox" id="more">
+            <style>
+              <class name="more"/>
+            </style>
+            <child>
+              <object class="GtkButton" id="more1">
+                <property name="action-name">proposal.move-previous-alternate</property>
+                <property name="can-focus">false</property>
+                <property name="icon-name">pan-start-symbolic</property>
+                <style>
+                  <class name="flat"/>
+                </style>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="more2">
+                <property name="action-name">proposal.move-next-alternate</property>
+                <property name="can-focus">false</property>
+                <property name="icon-name">pan-end-symbolic</property>
+                <style>
+                  <class name="flat"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/gtksourceview/gtksourcecompletionproposal.c b/gtksourceview/gtksourcecompletionproposal.c
index 1fb5cc36..b66e2f83 100644
--- a/gtksourceview/gtksourcecompletionproposal.c
+++ b/gtksourceview/gtksourcecompletionproposal.c
@@ -1,8 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2007 - 2009 Jesús Barbero Rodríguez <chuchiperriman gmail com>
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,319 +15,19 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include "gtksourcecompletionproposal.h"
-
-/**
- * SECTION:completionproposal
- * @title: GtkSourceCompletionProposal
- * @short_description: Completion proposal interface
- *
- * The proposal interface represents a completion item in the completion window.
- * It provides information on how to display the completion item and what action
- * should be taken when the completion item is activated.
  *
- * The proposal is displayed in the completion window with a label and
- * optionally an icon.
- * The label may be specified using plain text or markup by implementing
- * the corresponding get function. Only one of those get functions
- * should return a value different from %NULL.
- * The icon may be specified as a #GdkTexture, as an icon name or as a #GIcon by
- * implementing the corresponding get function. At most one of those get functions
- * should return a value different from %NULL, if they all return %NULL no icon
- * will be used.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
-enum
-{
-       CHANGED,
-       N_SIGNALS
-};
-
-static guint signals[N_SIGNALS];
-
-G_DEFINE_INTERFACE (GtkSourceCompletionProposal, gtk_source_completion_proposal, G_TYPE_OBJECT)
-
-static gchar *
-gtk_source_completion_proposal_get_label_default (GtkSourceCompletionProposal *proposal)
-{
-       return NULL;
-}
-
-static gchar *
-gtk_source_completion_proposal_get_markup_default (GtkSourceCompletionProposal *proposal)
-{
-       return NULL;
-}
-
-static gchar *
-gtk_source_completion_proposal_get_text_default (GtkSourceCompletionProposal *proposal)
-{
-       return NULL;
-}
-
-static GdkTexture *
-gtk_source_completion_proposal_get_icon_default (GtkSourceCompletionProposal *proposal)
-{
-       return NULL;
-}
 
-static const gchar *
-gtk_source_completion_proposal_get_icon_name_default (GtkSourceCompletionProposal *proposal)
-{
-       return NULL;
-}
-
-static GIcon *
-gtk_source_completion_proposal_get_gicon_default (GtkSourceCompletionProposal *proposal)
-{
-       return NULL;
-}
 
-static gchar *
-gtk_source_completion_proposal_get_info_default (GtkSourceCompletionProposal *proposal)
-{
-       return NULL;
-}
+#include "config.h"
 
-static guint
-gtk_source_completion_proposal_hash_default (GtkSourceCompletionProposal *proposal)
-{
-       return g_direct_hash (proposal);
-}
+#include "gtksourcecompletionproposal.h"
 
-static gboolean
-gtk_source_completion_proposal_equal_default (GtkSourceCompletionProposal *proposal,
-                                              GtkSourceCompletionProposal *other)
-{
-       return g_direct_equal (proposal, other);
-}
+G_DEFINE_INTERFACE (GtkSourceCompletionProposal, gtk_source_completion_proposal, G_TYPE_OBJECT)
 
 static void
 gtk_source_completion_proposal_default_init (GtkSourceCompletionProposalInterface *iface)
 {
-       static gboolean initialized = FALSE;
-
-       iface->get_label = gtk_source_completion_proposal_get_label_default;
-       iface->get_markup = gtk_source_completion_proposal_get_markup_default;
-       iface->get_text = gtk_source_completion_proposal_get_text_default;
-       iface->get_icon = gtk_source_completion_proposal_get_icon_default;
-       iface->get_icon_name = gtk_source_completion_proposal_get_icon_name_default;
-       iface->get_gicon = gtk_source_completion_proposal_get_gicon_default;
-       iface->get_info = gtk_source_completion_proposal_get_info_default;
-       iface->hash = gtk_source_completion_proposal_hash_default;
-       iface->equal = gtk_source_completion_proposal_equal_default;
-
-       if (!initialized)
-       {
-               /**
-                * GtkSourceCompletionProposal::changed:
-                * @proposal: The #GtkSourceCompletionProposal
-                *
-                * Emitted when the proposal has changed. The completion popup
-                * will react to this by updating the shown information.
-                *
-                */
-               signals[CHANGED] =
-                       g_signal_new ("changed",
-                             G_TYPE_FROM_INTERFACE (iface),
-                             G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                             G_STRUCT_OFFSET (GtkSourceCompletionProposalInterface, changed),
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__VOID,
-                             G_TYPE_NONE, 0);
-               g_signal_set_va_marshaller (signals[CHANGED],
-                                           G_TYPE_FROM_INTERFACE (iface),
-                                           g_cclosure_marshal_VOID__VOIDv);
-
-               initialized = TRUE;
-       }
-}
-
-/**
- * gtk_source_completion_proposal_get_label:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Gets the label of @proposal. The label is shown in the list of proposals as
- * plain text. If you need any markup (such as bold or italic text), you have
- * to implement gtk_source_completion_proposal_get_markup(). The returned string
- * must be freed with g_free().
- *
- * Returns: a new string containing the label of @proposal.
- */
-gchar *
-gtk_source_completion_proposal_get_label (GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->get_label (proposal);
-}
-
-/**
- * gtk_source_completion_proposal_get_markup:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Gets the label of @proposal with markup. The label is shown in the list of
- * proposals and may contain markup. This will be used instead of
- * gtk_source_completion_proposal_get_label() if implemented. The returned string
- * must be freed with g_free().
- *
- * Returns: a new string containing the label of @proposal with markup.
- */
-gchar *
-gtk_source_completion_proposal_get_markup (GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->get_markup (proposal);
-}
-
-/**
- * gtk_source_completion_proposal_get_text:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Gets the text of @proposal. The text that is inserted into
- * the text buffer when the proposal is activated by the default activation.
- * You are free to implement a custom activation handler in the provider and
- * not implement this function. For more information, see
- * gtk_source_completion_provider_activate_proposal(). The returned string must
- * be freed with g_free().
- *
- * Returns: a new string containing the text of @proposal.
- */
-gchar *
-gtk_source_completion_proposal_get_text (GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->get_text (proposal);
-}
-
-/**
- * gtk_source_completion_proposal_get_icon:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Gets the #GdkTexture for the icon of @proposal.
- *
- * Returns: (nullable) (transfer none): A #GdkTexture with the icon of @proposal.
- */
-GdkTexture *
-gtk_source_completion_proposal_get_icon (GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->get_icon (proposal);
-}
-
-/**
- * gtk_source_completion_proposal_get_icon_name:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Gets the icon name of @proposal.
- *
- * Returns: (nullable) (transfer none): The icon name of @proposal.
- *
- * Since: 3.18
- */
-const gchar *
-gtk_source_completion_proposal_get_icon_name (GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->get_icon_name (proposal);
-}
-
-/**
- * gtk_source_completion_proposal_get_gicon:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Gets the #GIcon for the icon of @proposal.
- *
- * Returns: (nullable) (transfer none): A #GIcon with the icon of @proposal.
- *
- * Since: 3.18
- */
-GIcon *
-gtk_source_completion_proposal_get_gicon (GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->get_gicon (proposal);
-}
-
-/**
- * gtk_source_completion_proposal_get_info:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Gets extra information associated to the proposal. This information will be
- * used to present the user with extra, detailed information about the
- * selected proposal. The returned string must be freed with g_free().
- *
- * Returns: (nullable) (transfer full): a newly-allocated string containing
- * extra information of @proposal or %NULL if no extra information is associated
- * to @proposal.
- */
-gchar *
-gtk_source_completion_proposal_get_info (GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->get_info (proposal);
-}
-
-/**
- * gtk_source_completion_proposal_hash:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Get the hash value of @proposal. This is used to (together with
- * gtk_source_completion_proposal_equal()) to match proposals in the completion
- * model. By default, it uses a direct hash (g_direct_hash()).
- *
- * Returns: The hash value of @proposal.
- */
-guint
-gtk_source_completion_proposal_hash (GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), 0);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->hash (proposal);
-}
-
-/**
- * gtk_source_completion_proposal_equal:
- * @proposal: a #GtkSourceCompletionProposal.
- * @other: a #GtkSourceCompletionProposal.
- *
- * Get whether two proposal objects are the same.  This is used to (together
- * with gtk_source_completion_proposal_hash()) to match proposals in the
- * completion model. By default, it uses direct equality (g_direct_equal()).
- *
- * Returns: %TRUE if @proposal and @object are the same proposal
- */
-gboolean
-gtk_source_completion_proposal_equal (GtkSourceCompletionProposal *proposal,
-                                      GtkSourceCompletionProposal *other)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), FALSE);
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (other), FALSE);
-
-       return GTK_SOURCE_COMPLETION_PROPOSAL_GET_IFACE (proposal)->equal (proposal, other);
-}
-
-/**
- * gtk_source_completion_proposal_changed:
- * @proposal: a #GtkSourceCompletionProposal.
- *
- * Emits the "changed" signal on @proposal. This should be called by
- * implementations whenever the name, icon or info of the proposal has
- * changed.
- */
-void
-gtk_source_completion_proposal_changed (GtkSourceCompletionProposal *proposal)
-{
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal));
-       g_signal_emit (proposal, signals[CHANGED], 0);
 }
diff --git a/gtksourceview/gtksourcecompletionproposal.h b/gtksourceview/gtksourcecompletionproposal.h
index 969662ea..723f8012 100644
--- a/gtksourceview/gtksourcecompletionproposal.h
+++ b/gtksourceview/gtksourcecompletionproposal.h
@@ -1,8 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2007 - 2009 Jesús Barbero Rodríguez <chuchiperriman gmail com>
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,90 +15,26 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #pragma once
 
-#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
-#error "Only <gtksourceview/gtksource.h> can be included directly."
-#endif
-
-#include <gtk/gtk.h>
+#include <glib-object.h>
 
 #include "gtksourcetypes.h"
 
 G_BEGIN_DECLS
 
-#define GTK_SOURCE_TYPE_COMPLETION_PROPOSAL (gtk_source_completion_proposal_get_type ())
+#define GTK_SOURCE_TYPE_COMPLETION_PROPOSAL (gtk_source_completion_proposal_get_type())
 
-GTK_SOURCE_AVAILABLE_IN_ALL
+GTK_SOURCE_AVAILABLE_IN_5_0
 G_DECLARE_INTERFACE (GtkSourceCompletionProposal, gtk_source_completion_proposal, GTK_SOURCE, 
COMPLETION_PROPOSAL, GObject)
 
-/**
- * GtkSourceCompletionProposalInterface:
- * @parent: The parent interface.
- * @get_label: The virtual function pointer for gtk_source_completion_proposal_get_label().
- * By default, %NULL is returned.
- * @get_markup: The virtual function pointer for gtk_source_completion_proposal_get_markup().
- * By default, %NULL is returned.
- * @get_text: The virtual function pointer for gtk_source_completion_proposal_get_text().
- * By default, %NULL is returned.
- * @get_icon: The virtual function pointer for gtk_source_completion_proposal_get_icon().
- * By default, %NULL is returned.
- * @get_icon_name: The virtual function pointer for gtk_source_completion_proposal_get_icon_name().
- * By default, %NULL is returned.
- * @get_gicon: The virtual function pointer for gtk_source_completion_proposal_get_gicon().
- * By default, %NULL is returned.
- * @get_info: The virtual function pointer for gtk_source_completion_proposal_get_info().
- * By default, %NULL is returned.
- * @hash: The virtual function pointer for gtk_source_completion_proposal_hash().
- * By default, it uses a direct hash (g_direct_hash()).
- * @equal: The virtual function pointer for gtk_source_completion_proposal_equal().
- * By default, it uses direct equality (g_direct_equal()).
- * @changed: The function pointer for the #GtkSourceCompletionProposal::changed signal.
- *
- * The virtual function table for #GtkSourceCompletionProposal.
- */
 struct _GtkSourceCompletionProposalInterface
 {
-       GTypeInterface parent;
-
-       /* Interface functions */
-       gchar           *(*get_label)     (GtkSourceCompletionProposal *proposal);
-       gchar           *(*get_markup)    (GtkSourceCompletionProposal *proposal);
-       gchar           *(*get_text)      (GtkSourceCompletionProposal *proposal);
-       GdkTexture      *(*get_icon)      (GtkSourceCompletionProposal *proposal);
-       const gchar     *(*get_icon_name) (GtkSourceCompletionProposal *proposal);
-       GIcon           *(*get_gicon)     (GtkSourceCompletionProposal *proposal);
-       gchar           *(*get_info)      (GtkSourceCompletionProposal *proposal);
-       guint            (*hash)          (GtkSourceCompletionProposal *proposal);
-       gboolean         (*equal)         (GtkSourceCompletionProposal *proposal,
-                                          GtkSourceCompletionProposal *other);
-
-       /* Signals */
-       void             (*changed)       (GtkSourceCompletionProposal *proposal);
+       GTypeInterface parent_iface;
 };
 
-GTK_SOURCE_AVAILABLE_IN_ALL
-gchar       *gtk_source_completion_proposal_get_label     (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gchar       *gtk_source_completion_proposal_get_markup    (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gchar       *gtk_source_completion_proposal_get_text      (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_ALL
-GdkTexture  *gtk_source_completion_proposal_get_icon      (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_3_18
-const gchar *gtk_source_completion_proposal_get_icon_name (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_3_18
-GIcon       *gtk_source_completion_proposal_get_gicon     (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gchar       *gtk_source_completion_proposal_get_info      (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_ALL
-void         gtk_source_completion_proposal_changed       (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_ALL
-guint        gtk_source_completion_proposal_hash          (GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gboolean     gtk_source_completion_proposal_equal         (GtkSourceCompletionProposal *proposal,
-                                                           GtkSourceCompletionProposal *other);
-
 G_END_DECLS
diff --git a/gtksourceview/gtksourcecompletionprovider.c b/gtksourceview/gtksourcecompletionprovider.c
index 1eafcc9c..5278bc02 100644
--- a/gtksourceview/gtksourcecompletionprovider.c
+++ b/gtksourceview/gtksourcecompletionprovider.c
@@ -1,8 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2007 - 2009 Jesús Barbero Rodríguez <chuchiperriman gmail com>
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,431 +15,251 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
+
+
 #include "config.h"
 
-#include "gtksourcecompletionprovider.h"
+#include "gtksourcecompletioncontext.h"
+#include "gtksourcecompletioncell.h"
 #include "gtksourcecompletionproposal.h"
-#include "gtksourcecompletioninfo.h"
-
-/**
- * SECTION:completionprovider
- * @title: GtkSourceCompletionProvider
- * @short_description: Completion provider interface
- *
- * You must implement this interface to provide proposals to #GtkSourceCompletion
- *
- * The provider may be displayed in the completion window as a header row, showing
- * its name and optionally an icon.
- * The icon may be specified as a #GdkTexture, as an icon name or as a #GIcon by
- * implementing the corresponding get function. At most one of those get functions
- * should return a value different from %NULL, if they all return %NULL no icon
- * will be used.
- */
+#include "gtksourcecompletionprovider.h"
 
 G_DEFINE_INTERFACE (GtkSourceCompletionProvider, gtk_source_completion_provider, G_TYPE_OBJECT)
 
-/* Default implementations */
-static gchar *
-gtk_source_completion_provider_get_name_default (GtkSourceCompletionProvider *provider)
-{
-       g_return_val_if_reached (NULL);
-}
-
-static GdkTexture *
-gtk_source_completion_provider_get_icon_default (GtkSourceCompletionProvider *provider)
+static void
+fallback_populate_async (GtkSourceCompletionProvider *provider,
+                         GtkSourceCompletionContext  *context,
+                         GCancellable                *cancellable,
+                         GAsyncReadyCallback          callback,
+                         gpointer                     user_data)
 {
-       return NULL;
-}
+       GListModel *ret;
+       GError *error = NULL;
+       GTask *task;
 
-static const gchar *
-gtk_source_completion_provider_get_icon_name_default (GtkSourceCompletionProvider *provider)
-{
-       return NULL;
-}
+       task = g_task_new (provider, cancellable, callback, user_data);
+       g_task_set_source_tag (task, fallback_populate_async);
 
-static GIcon *
-gtk_source_completion_provider_get_gicon_default (GtkSourceCompletionProvider *provider)
-{
-       return NULL;
-}
+       ret = GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->populate (provider, context, &error);
 
-static void
-gtk_source_completion_provider_populate_default (GtkSourceCompletionProvider *provider,
-                                                 GtkSourceCompletionContext  *context)
-{
-       gtk_source_completion_context_add_proposals (context, provider, NULL, TRUE);
-}
+       if (ret == NULL)
+       {
+               g_task_return_error (task, g_steal_pointer (&error));
+       }
+       else
+       {
+               g_task_return_pointer (task, g_steal_pointer (&ret), g_object_unref);
+       }
 
-static GtkSourceCompletionActivation
-gtk_source_completion_provider_get_activation_default (GtkSourceCompletionProvider *provider)
-{
-       return GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE |
-              GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED;
+       g_clear_object (&task);
 }
 
-static gboolean
-gtk_source_completion_provider_match_default (GtkSourceCompletionProvider *provider,
-                                              GtkSourceCompletionContext  *context)
+static GListModel *
+fallback_populate_finish (GtkSourceCompletionProvider  *provider,
+                          GAsyncResult                 *result,
+                          GError                      **error)
 {
-       return TRUE;
+       return g_task_propagate_pointer (G_TASK (result), error);
 }
 
-static GtkWidget *
-gtk_source_completion_provider_get_info_widget_default (GtkSourceCompletionProvider *provider,
-                                                        GtkSourceCompletionProposal *proposal)
+static GListModel *
+fallback_populate (GtkSourceCompletionProvider  *provider,
+                   GtkSourceCompletionContext   *context,
+                   GError                      **error)
 {
+       g_set_error (error,
+                    G_IO_ERROR,
+                    G_IO_ERROR_NOT_SUPPORTED,
+                    "Not supported");
        return NULL;
 }
 
-static void
-gtk_source_completion_provider_update_info_default (GtkSourceCompletionProvider *provider,
-                                                    GtkSourceCompletionProposal *proposal,
-                                                    GtkWidget                   *widget)
-{
-}
-
 static gboolean
-gtk_source_completion_provider_get_start_iter_default (GtkSourceCompletionProvider *provider,
-                                                       GtkSourceCompletionContext  *context,
-                                                       GtkSourceCompletionProposal *proposal,
-                                                       GtkTextIter                 *iter)
+fallback_refilter (GtkSourceCompletionProvider *provider,
+                   GtkSourceCompletionContext  *context,
+                   GListModel                  *model)
 {
        return FALSE;
 }
 
-static gboolean
-gtk_source_completion_provider_activate_proposal_default (GtkSourceCompletionProvider *provider,
-                                                          GtkSourceCompletionProposal *proposal,
-                                                          GtkTextIter                 *iter)
+static void
+fallback_activate (GtkSourceCompletionProvider *provider,
+                   GtkSourceCompletionContext  *context,
+                   GtkSourceCompletionProposal *proposal)
 {
-       return FALSE;
 }
 
-static gint
-gtk_source_completion_provider_get_interactive_delay_default (GtkSourceCompletionProvider *provider)
+static void
+gtk_source_completion_provider_default_init (GtkSourceCompletionProviderInterface *iface)
 {
-       /* -1 means the default value in the completion object */
-       return -1;
+       iface->populate_async = fallback_populate_async;
+       iface->populate_finish = fallback_populate_finish;
+       iface->populate = fallback_populate;
+       iface->refilter = fallback_refilter;
+       iface->activate = fallback_activate;
 }
 
-static gint
-gtk_source_completion_provider_get_priority_default (GtkSourceCompletionProvider *provider)
+GdkPaintable *
+gtk_source_completion_provider_get_paintable (GtkSourceCompletionProvider *self)
 {
-       return 0;
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self), NULL);
+
+       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->get_paintable (self);
 }
 
-static void
-gtk_source_completion_provider_default_init (GtkSourceCompletionProviderInterface *iface)
+char *
+gtk_source_completion_provider_get_title (GtkSourceCompletionProvider *self)
 {
-       iface->get_name = gtk_source_completion_provider_get_name_default;
-
-       iface->get_icon = gtk_source_completion_provider_get_icon_default;
-       iface->get_icon_name = gtk_source_completion_provider_get_icon_name_default;
-       iface->get_gicon = gtk_source_completion_provider_get_gicon_default;
-
-       iface->populate = gtk_source_completion_provider_populate_default;
-
-       iface->match = gtk_source_completion_provider_match_default;
-       iface->get_activation = gtk_source_completion_provider_get_activation_default;
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self), NULL);
 
-       iface->get_info_widget = gtk_source_completion_provider_get_info_widget_default;
-       iface->update_info = gtk_source_completion_provider_update_info_default;
+       if (GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->get_title)
+               return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->get_title (self);
 
-       iface->get_start_iter = gtk_source_completion_provider_get_start_iter_default;
-       iface->activate_proposal = gtk_source_completion_provider_activate_proposal_default;
-
-       iface->get_interactive_delay = gtk_source_completion_provider_get_interactive_delay_default;
-       iface->get_priority = gtk_source_completion_provider_get_priority_default;
+       return NULL;
 }
 
-/**
- * gtk_source_completion_provider_get_name:
- * @provider: a #GtkSourceCompletionProvider.
- *
- * Get the name of the provider. This should be a translatable name for
- * display to the user. For example: _("Document word completion provider"). The
- * returned string must be freed with g_free().
- *
- * Returns: a new string containing the name of the provider.
- */
-gchar *
-gtk_source_completion_provider_get_name (GtkSourceCompletionProvider *provider)
+int
+gtk_source_completion_provider_get_priority (GtkSourceCompletionProvider *self,
+                                             GtkSourceCompletionContext  *context)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_name (provider);
-}
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self), 0);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), 0);
 
-/**
- * gtk_source_completion_provider_get_icon:
- * @provider: The #GtkSourceCompletionProvider
- *
- * Get the #GdkTexture for the icon of the @provider.
- *
- * Returns: (nullable) (transfer none): The icon to be used for the provider,
- *          or %NULL if the provider does not have a special icon.
- */
-GdkTexture *
-gtk_source_completion_provider_get_icon (GtkSourceCompletionProvider *provider)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), NULL);
+       if (GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->get_priority)
+               return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->get_priority (self, context);
 
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_icon (provider);
+       return 0;
 }
 
-/**
- * gtk_source_completion_provider_get_icon_name:
- * @provider: The #GtkSourceCompletionProvider
- *
- * Gets the icon name of @provider.
- *
- * Returns: (nullable) (transfer none): The icon name to be used for the provider,
- *          or %NULL if the provider does not have a special icon.
- *
- * Since: 3.18
- */
-const gchar *
-gtk_source_completion_provider_get_icon_name (GtkSourceCompletionProvider *provider)
+gboolean
+gtk_source_completion_provider_is_trigger (GtkSourceCompletionProvider *self,
+                                           const GtkTextIter           *iter,
+                                           gunichar                     ch)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), NULL);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self), FALSE);
+       g_return_val_if_fail (iter != NULL, FALSE);
 
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_icon_name (provider);
+       if (GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->is_trigger)
+               return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->is_trigger (self, iter, ch);
+
+       return FALSE;
 }
 
-/**
- * gtk_source_completion_provider_get_gicon:
- * @provider: The #GtkSourceCompletionProvider
- *
- * Gets the #GIcon for the icon of @provider.
- *
- * Returns: (nullable) (transfer none): The icon to be used for the provider,
- *          or %NULL if the provider does not have a special icon.
- *
- * Since: 3.18
- */
-GIcon *
-gtk_source_completion_provider_get_gicon (GtkSourceCompletionProvider *provider)
+gboolean
+gtk_source_completion_provider_key_activates (GtkSourceCompletionProvider *self,
+                                              GtkSourceCompletionContext  *context,
+                                              GtkSourceCompletionProposal *proposal,
+                                              guint                         keyval,
+                                              GdkModifierType               state)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), NULL);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self), FALSE);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), FALSE);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), FALSE);
+
+       if (GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->key_activates)
+               return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->key_activates (self, context, 
proposal, keyval, state);
 
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_gicon (provider);
+       return FALSE;
 }
 
-/**
- * gtk_source_completion_provider_populate:
- * @provider: a #GtkSourceCompletionProvider.
- * @context: a #GtkSourceCompletionContext.
- *
- * Populate @context with proposals from @provider added with the
- * gtk_source_completion_context_add_proposals() function.
- */
 void
-gtk_source_completion_provider_populate (GtkSourceCompletionProvider *provider,
-                                         GtkSourceCompletionContext  *context)
+gtk_source_completion_provider_populate_async (GtkSourceCompletionProvider *self,
+                                               GtkSourceCompletionContext  *context,
+                                               GCancellable                *cancellable,
+                                               GAsyncReadyCallback          callback,
+                                               gpointer                     user_data)
 {
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+       g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-       GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->populate (provider, context);
+       GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->populate_async (self, context, cancellable, 
callback, user_data);
 }
 
-/**
- * gtk_source_completion_provider_get_activation:
- * @provider: a #GtkSourceCompletionProvider.
- *
- * Get with what kind of activation the provider should be activated.
- *
- * Returns: a combination of #GtkSourceCompletionActivation.
- **/
-GtkSourceCompletionActivation
-gtk_source_completion_provider_get_activation (GtkSourceCompletionProvider *provider)
+GListModel *
+gtk_source_completion_provider_populate_finish (GtkSourceCompletionProvider  *self,
+                                                GAsyncResult                 *result,
+                                                GError                      **error)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), 
GTK_SOURCE_COMPLETION_ACTIVATION_NONE);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self), NULL);
 
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_activation (provider);
+       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->populate_finish (self, result, error);
 }
 
-/**
- * gtk_source_completion_provider_match:
- * @provider: a #GtkSourceCompletionProvider.
- * @context: a #GtkSourceCompletionContext.
- *
- * Get whether the provider match the context of completion detailed in
- * @context.
- *
- * Returns: %TRUE if @provider matches the completion context, %FALSE otherwise.
- */
 gboolean
-gtk_source_completion_provider_match (GtkSourceCompletionProvider *provider,
-                                      GtkSourceCompletionContext  *context)
+gtk_source_completion_provider_refilter (GtkSourceCompletionProvider *self,
+                                         GtkSourceCompletionContext  *context,
+                                         GListModel                  *model)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), TRUE);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self), FALSE);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), FALSE);
+       g_return_val_if_fail (G_IS_LIST_MODEL (model), FALSE);
 
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->match (provider, context);
-}
+       if (GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->refilter)
+               return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->refilter (self, context, model);
 
-/**
- * gtk_source_completion_provider_get_info_widget:
- * @provider: a #GtkSourceCompletionProvider.
- * @proposal: a currently selected #GtkSourceCompletionProposal.
- *
- * Get a customized info widget to show extra information of a proposal.
- * This allows for customized widgets on a proposal basis, although in general
- * providers will have the same custom widget for all their proposals and
- * @proposal can be ignored. The implementation of this function is optional.
- *
- * If this function is not implemented, the default widget is a #GtkLabel. The
- * return value of gtk_source_completion_proposal_get_info() is used as the
- * content of the #GtkLabel.
- *
- * <note>
- *   <para>
- *     If implemented, gtk_source_completion_provider_update_info()
- *     <emphasis>must</emphasis> also be implemented.
- *   </para>
- * </note>
- *
- * Returns: (nullable) (transfer none): a custom #GtkWidget to show extra
- * information about @proposal, or %NULL if the provider does not have a special
- * info widget.
- */
-GtkWidget *
-gtk_source_completion_provider_get_info_widget (GtkSourceCompletionProvider *provider,
-                                                GtkSourceCompletionProposal *proposal)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), NULL);
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
-
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_info_widget (provider, proposal);
+       return FALSE;
 }
 
-/**
- * gtk_source_completion_provider_update_info:
- * @provider: a #GtkSourceCompletionProvider.
- * @proposal: a #GtkSourceCompletionProposal.
- * @widget: a #GtkWidget provided from gtk_source_completion_provider_get_info_widget()
- *
- * Update extra information shown in @info for @proposal.
- *
- * <note>
- *   <para>
- *     This function <emphasis>must</emphasis> be implemented when
- *     gtk_source_completion_provider_get_info_widget() is implemented.
- *   </para>
- * </note>
- */
 void
-gtk_source_completion_provider_update_info (GtkSourceCompletionProvider *provider,
-                                            GtkSourceCompletionProposal *proposal,
-                                            GtkWidget                   *widget)
+gtk_source_completion_provider_display (GtkSourceCompletionProvider *self,
+                                        GtkSourceCompletionContext  *context,
+                                        GtkSourceCompletionProposal *proposal,
+                                        GtkSourceCompletionCell     *cell)
 {
-       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
        g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal));
-       g_return_if_fail (GTK_IS_WIDGET (widget));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CELL (cell));
 
-       GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->update_info (provider, proposal, widget);
+       if (GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->display)
+               GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->display (self, context, proposal, cell);
 }
 
-/**
- * gtk_source_completion_provider_get_start_iter:
- * @provider: a #GtkSourceCompletionProvider.
- * @proposal: a #GtkSourceCompletionProposal.
- * @context: a #GtkSourceCompletionContext.
- * @iter: (out): a #GtkTextIter.
- *
- * Get the #GtkTextIter at which the completion for @proposal starts. When
- * implemented, this information is used to position the completion window
- * accordingly when a proposal is selected in the completion window. The
- * @proposal text inside the completion window is aligned on @iter.
- *
- * If this function is not implemented, the word boundary is taken to position
- * the completion window. See gtk_source_completion_provider_activate_proposal()
- * for an explanation on the word boundaries.
- *
- * When the @proposal is activated, the default handler uses @iter as the start
- * of the word to replace. See
- * gtk_source_completion_provider_activate_proposal() for more information.
- *
- * Returns: %TRUE if @iter was set for @proposal, %FALSE otherwise.
- */
-gboolean
-gtk_source_completion_provider_get_start_iter (GtkSourceCompletionProvider *provider,
-                                               GtkSourceCompletionContext  *context,
-                                               GtkSourceCompletionProposal *proposal,
-                                               GtkTextIter                 *iter)
+void
+gtk_source_completion_provider_activate (GtkSourceCompletionProvider *self,
+                                         GtkSourceCompletionContext  *context,
+                                         GtkSourceCompletionProposal *proposal)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), FALSE);
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), FALSE);
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), FALSE);
-       g_return_val_if_fail (iter != NULL, FALSE);
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+       g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal));
 
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_start_iter (provider, context, 
proposal, iter);
+       if (GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->activate)
+               GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->activate (self, context, proposal);
 }
 
 /**
- * gtk_source_completion_provider_activate_proposal:
- * @provider: a #GtkSourceCompletionProvider.
- * @proposal: a #GtkSourceCompletionProposal.
- * @iter: a #GtkTextIter.
+ * gtk_source_completion_provider_list_alternates:
+ * @self: a #GtkSourceCompletionProvider
+ * @content: a #GtkSourceCompletionContext
+ * @proposal: a #GtkSourceCompletionProposal
  *
- * Activate @proposal at @iter. When this functions returns %FALSE, the default
- * activation of @proposal will take place which replaces the word at @iter
- * with the text of @proposal (see gtk_source_completion_proposal_get_text()).
+ * Providers should return a list of alternates to @proposal or %NULL if
+ * there are no alternates available. This can be used by the completion
+ * view to allow the user to move laterally through similar proposals, such
+ * as overrides of methods by the same name.
  *
- * Here is how the default activation selects the boundaries of the word to
- * replace. The end of the word is @iter. For the start of the word, it depends
- * on whether a start iter is defined for @proposal (see
- * gtk_source_completion_provider_get_start_iter()). If a start iter is defined,
- * the start of the word is the start iter. Else, the word (as long as possible)
- * will contain only alphanumerical and the "_" characters.
+ * Returns: (nullable) (transfer full) (element-type GtkSourceCompletionProposal):
+ *   a #GPtrArray of #GtkSourceCompletionProposal or %NULL.
  *
- * Returns: %TRUE to indicate that the proposal activation has been handled,
- *          %FALSE otherwise.
+ * Since: 5.0
  */
-gboolean
-gtk_source_completion_provider_activate_proposal (GtkSourceCompletionProvider *provider,
-                                                  GtkSourceCompletionProposal *proposal,
-                                                  GtkTextIter                 *iter)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), FALSE);
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), FALSE);
-
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->activate_proposal (provider, proposal, 
iter);
-}
-
-/**
- * gtk_source_completion_provider_get_interactive_delay:
- * @provider: a #GtkSourceCompletionProvider.
- *
- * Get the delay in milliseconds before starting interactive completion for
- * this provider. A value of -1 indicates to use the default value as set
- * by the #GtkSourceCompletion:auto-complete-delay property.
- *
- * Returns: the interactive delay in milliseconds.
- **/
-gint
-gtk_source_completion_provider_get_interactive_delay (GtkSourceCompletionProvider *provider)
+GPtrArray *
+gtk_source_completion_provider_list_alternates (GtkSourceCompletionProvider *self,
+                                                GtkSourceCompletionContext  *context,
+                                                GtkSourceCompletionProposal *proposal)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), -1);
-
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_interactive_delay (provider);
-}
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (self), NULL);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context), NULL);
+       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal), NULL);
 
-/**
- * gtk_source_completion_provider_get_priority:
- * @provider: a #GtkSourceCompletionProvider.
- *
- * Get the provider priority. The priority determines the order in which
- * proposals appear in the completion popup. Higher priorities are sorted
- * before lower priorities. The default priority is 0.
- *
- * Returns: the provider priority.
- **/
-gint
-gtk_source_completion_provider_get_priority (GtkSourceCompletionProvider *provider)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider), 0);
+       if (GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->list_alternates)
+               return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (self)->list_alternates (self, context, 
proposal);
 
-       return GTK_SOURCE_COMPLETION_PROVIDER_GET_IFACE (provider)->get_priority (provider);
+       return NULL;
 }
diff --git a/gtksourceview/gtksourcecompletionprovider.h b/gtksourceview/gtksourcecompletionprovider.h
index 0ece40ae..32f2444a 100644
--- a/gtksourceview/gtksourcecompletionprovider.h
+++ b/gtksourceview/gtksourcecompletionprovider.h
@@ -1,8 +1,7 @@
 /*
  * This file is part of GtkSourceView
  *
- * Copyright 2007 - 2009 Jesús Barbero Rodríguez <chuchiperriman gmail com>
- * Copyright 2009 - Jesse van den Kieboom <jessevdk gnome org>
+ * Copyright 2020 Christian Hergert <chergert redhat com>
  *
  * GtkSourceView is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,122 +15,106 @@
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #pragma once
 
-#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
-#error "Only <gtksourceview/gtksource.h> can be included directly."
-#endif
-
-#include <gtk/gtk.h>
-
-#include "gtksourcecompletioncontext.h"
 #include "gtksourcetypes.h"
 
 G_BEGIN_DECLS
 
 #define GTK_SOURCE_TYPE_COMPLETION_PROVIDER (gtk_source_completion_provider_get_type())
 
-GTK_SOURCE_AVAILABLE_IN_ALL
+GTK_SOURCE_AVAILABLE_IN_5_0
 G_DECLARE_INTERFACE (GtkSourceCompletionProvider, gtk_source_completion_provider, GTK_SOURCE, 
COMPLETION_PROVIDER, GObject)
 
-/**
- * GtkSourceCompletionProviderInterface:
- * @g_iface: The parent interface.
- * @get_name: The virtual function pointer for gtk_source_completion_provider_get_name().
- * Must be implemented.
- * @get_icon: The virtual function pointer for gtk_source_completion_provider_get_icon().
- * By default, %NULL is returned.
- * @get_icon_name: The virtual function pointer for gtk_source_completion_provider_get_icon_name().
- * By default, %NULL is returned.
- * @get_gicon: The virtual function pointer for gtk_source_completion_provider_get_gicon().
- * By default, %NULL is returned.
- * @populate: The virtual function pointer for gtk_source_completion_provider_populate().
- * Add no proposals by default.
- * @match: The virtual function pointer for gtk_source_completion_provider_match().
- * By default, %TRUE is returned.
- * @get_activation: The virtual function pointer for gtk_source_completion_provider_get_activation().
- * The combination of all #GtkSourceCompletionActivation is returned by default.
- * @get_info_widget: The virtual function pointer for gtk_source_completion_provider_get_info_widget().
- * By default, %NULL is returned.
- * @update_info: The virtual function pointer for gtk_source_completion_provider_update_info().
- * Does nothing by default.
- * @get_start_iter: The virtual function pointer for gtk_source_completion_provider_get_start_iter().
- * By default, %FALSE is returned.
- * @activate_proposal: The virtual function pointer for gtk_source_completion_provider_activate_proposal().
- * By default, %FALSE is returned.
- * @get_interactive_delay: The virtual function pointer for 
gtk_source_completion_provider_get_interactive_delay().
- * By default, -1 is returned.
- * @get_priority: The virtual function pointer for gtk_source_completion_provider_get_priority().
- * By default, 0 is returned.
- *
- * The virtual function table for #GtkSourceCompletionProvider.
- */
 struct _GtkSourceCompletionProviderInterface
 {
-       GTypeInterface g_iface;
+       GTypeInterface parent_iface;
 
-       gchar                         *(*get_name)              (GtkSourceCompletionProvider *provider);
-       GdkTexture                    *(*get_icon)              (GtkSourceCompletionProvider *provider);
-       const gchar                   *(*get_icon_name)         (GtkSourceCompletionProvider *provider);
-       GIcon                         *(*get_gicon)             (GtkSourceCompletionProvider *provider);
-       void                           (*populate)              (GtkSourceCompletionProvider *provider,
-                                                                GtkSourceCompletionContext  *context);
-       gboolean                       (*match)                 (GtkSourceCompletionProvider *provider,
-                                                                GtkSourceCompletionContext  *context);
-       GtkSourceCompletionActivation  (*get_activation)        (GtkSourceCompletionProvider *provider);
-       GtkWidget                     *(*get_info_widget)       (GtkSourceCompletionProvider *provider,
-                                                                GtkSourceCompletionProposal *proposal);
-       void                           (*update_info)           (GtkSourceCompletionProvider *provider,
-                                                                GtkSourceCompletionProposal *proposal,
-                                                                GtkWidget                   *widget);
-       gboolean                       (*get_start_iter)        (GtkSourceCompletionProvider *provider,
-                                                                GtkSourceCompletionContext  *context,
-                                                                GtkSourceCompletionProposal *proposal,
-                                                                GtkTextIter                 *iter);
-       gboolean                       (*activate_proposal)     (GtkSourceCompletionProvider *provider,
-                                                                GtkSourceCompletionProposal *proposal,
-                                                                GtkTextIter                 *iter);
-       gint                           (*get_interactive_delay) (GtkSourceCompletionProvider *provider);
-       gint                           (*get_priority)          (GtkSourceCompletionProvider *provider);
+       GdkPaintable *(*get_paintable)        (GtkSourceCompletionProvider *self);
+       char         *(*get_title)            (GtkSourceCompletionProvider  *self);
+       int           (*get_priority)         (GtkSourceCompletionProvider  *self,
+                                              GtkSourceCompletionContext   *context);
+       gboolean      (*is_trigger)           (GtkSourceCompletionProvider  *self,
+                                              const GtkTextIter            *iter,
+                                              gunichar                      ch);
+       gboolean      (*key_activates)        (GtkSourceCompletionProvider  *self,
+                                              GtkSourceCompletionContext   *context,
+                                              GtkSourceCompletionProposal  *proposal,
+                                              guint                         keyval,
+                                              GdkModifierType               state);
+       GListModel   *(*populate)             (GtkSourceCompletionProvider  *self,
+                                              GtkSourceCompletionContext   *context,
+                                              GError                      **error);
+       void          (*populate_async)       (GtkSourceCompletionProvider  *self,
+                                              GtkSourceCompletionContext   *context,
+                                              GCancellable                 *cancellable,
+                                              GAsyncReadyCallback           callback,
+                                              gpointer                      user_data);
+       GListModel   *(*populate_finish)      (GtkSourceCompletionProvider  *self,
+                                              GAsyncResult                 *result,
+                                              GError                      **error);
+       gboolean      (*refilter)             (GtkSourceCompletionProvider  *self,
+                                              GtkSourceCompletionContext   *context,
+                                              GListModel                   *model);
+       void          (*display)              (GtkSourceCompletionProvider  *self,
+                                              GtkSourceCompletionContext   *context,
+                                              GtkSourceCompletionProposal  *proposal,
+                                              GtkSourceCompletionCell      *cell);
+       void          (*activate)             (GtkSourceCompletionProvider  *self,
+                                              GtkSourceCompletionContext   *context,
+                                              GtkSourceCompletionProposal  *proposal);
+       GPtrArray    *(*list_alternates)      (GtkSourceCompletionProvider  *self,
+                                              GtkSourceCompletionContext   *context,
+                                              GtkSourceCompletionProposal  *proposal);
 };
 
-GTK_SOURCE_AVAILABLE_IN_ALL
-gchar                         *gtk_source_completion_provider_get_name              
(GtkSourceCompletionProvider *provider);
-GTK_SOURCE_AVAILABLE_IN_ALL
-GdkTexture                    *gtk_source_completion_provider_get_icon              
(GtkSourceCompletionProvider *provider);
-GTK_SOURCE_AVAILABLE_IN_3_18
-const gchar                   *gtk_source_completion_provider_get_icon_name         
(GtkSourceCompletionProvider *provider);
-GTK_SOURCE_AVAILABLE_IN_3_18
-GIcon                         *gtk_source_completion_provider_get_gicon             
(GtkSourceCompletionProvider *provider);
-GTK_SOURCE_AVAILABLE_IN_ALL
-void                           gtk_source_completion_provider_populate              
(GtkSourceCompletionProvider *provider,
-                                                                                     
GtkSourceCompletionContext  *context);
-GTK_SOURCE_AVAILABLE_IN_ALL
-GtkSourceCompletionActivation  gtk_source_completion_provider_get_activation        
(GtkSourceCompletionProvider *provider);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gboolean                       gtk_source_completion_provider_match                 
(GtkSourceCompletionProvider *provider,
-                                                                                     
GtkSourceCompletionContext  *context);
-GTK_SOURCE_AVAILABLE_IN_ALL
-GtkWidget                     *gtk_source_completion_provider_get_info_widget       
(GtkSourceCompletionProvider *provider,
-                                                                                     
GtkSourceCompletionProposal *proposal);
-GTK_SOURCE_AVAILABLE_IN_ALL
-void                           gtk_source_completion_provider_update_info           
(GtkSourceCompletionProvider *provider,
-                                                                                     
GtkSourceCompletionProposal *proposal,
-                                                                                     GtkWidget               
    *widget);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gboolean                       gtk_source_completion_provider_get_start_iter        
(GtkSourceCompletionProvider *provider,
-                                                                                     
GtkSourceCompletionContext  *context,
-                                                                                     
GtkSourceCompletionProposal *proposal,
-                                                                                     GtkTextIter             
    *iter);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gboolean                       gtk_source_completion_provider_activate_proposal     
(GtkSourceCompletionProvider *provider,
-                                                                                     
GtkSourceCompletionProposal *proposal,
-                                                                                     GtkTextIter             
    *iter);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gint                           gtk_source_completion_provider_get_interactive_delay 
(GtkSourceCompletionProvider *provider);
-GTK_SOURCE_AVAILABLE_IN_ALL
-gint                           gtk_source_completion_provider_get_priority          
(GtkSourceCompletionProvider *provider);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GdkPaintable *gtk_source_completion_provider_get_paintable     (GtkSourceCompletionProvider  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+char         *gtk_source_completion_provider_get_title         (GtkSourceCompletionProvider  *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+int           gtk_source_completion_provider_get_priority      (GtkSourceCompletionProvider  *self,
+                                                                GtkSourceCompletionContext   *context);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean      gtk_source_completion_provider_is_trigger        (GtkSourceCompletionProvider  *self,
+                                                                const GtkTextIter            *iter,
+                                                                gunichar                      ch);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean      gtk_source_completion_provider_key_activates     (GtkSourceCompletionProvider  *self,
+                                                                GtkSourceCompletionContext   *context,
+                                                                GtkSourceCompletionProposal  *proposal,
+                                                                guint                         keyval,
+                                                                GdkModifierType               state);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void          gtk_source_completion_provider_populate_async    (GtkSourceCompletionProvider  *self,
+                                                                GtkSourceCompletionContext   *context,
+                                                                GCancellable                 *cancellable,
+                                                                GAsyncReadyCallback           callback,
+                                                                gpointer                      user_data);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GListModel   *gtk_source_completion_provider_populate_finish   (GtkSourceCompletionProvider  *self,
+                                                                GAsyncResult                 *result,
+                                                                GError                      **error);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean      gtk_source_completion_provider_refilter          (GtkSourceCompletionProvider  *self,
+                                                                GtkSourceCompletionContext   *context,
+                                                                GListModel                   *model);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void          gtk_source_completion_provider_display           (GtkSourceCompletionProvider  *self,
+                                                                GtkSourceCompletionContext   *context,
+                                                                GtkSourceCompletionProposal  *proposal,
+                                                                GtkSourceCompletionCell      *cell);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void          gtk_source_completion_provider_activate          (GtkSourceCompletionProvider  *self,
+                                                                GtkSourceCompletionContext   *context,
+                                                                GtkSourceCompletionProposal  *proposal);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GPtrArray    *gtk_source_completion_provider_list_alternates   (GtkSourceCompletionProvider  *self,
+                                                                GtkSourceCompletionContext   *context,
+                                                                GtkSourceCompletionProposal  *proposal);
 
 G_END_DECLS
diff --git a/gtksourceview/gtksourcetypes-private.h b/gtksourceview/gtksourcetypes-private.h
index 97efe327..8cc31af1 100644
--- a/gtksourceview/gtksourcetypes-private.h
+++ b/gtksourceview/gtksourcetypes-private.h
@@ -23,10 +23,14 @@
 
 G_BEGIN_DECLS
 
+typedef struct _GtkSourceAssistant              GtkSourceAssistant;
+typedef struct _GtkSourceAssistantChild         GtkSourceAssistantChild;
 typedef struct _GtkSourceBufferInputStream      GtkSourceBufferInputStream;
 typedef struct _GtkSourceBufferOutputStream     GtkSourceBufferOutputStream;
 typedef struct _GtkSourceCompletionInfo         GtkSourceCompletionInfo;
-typedef struct _GtkSourceCompletionModel        GtkSourceCompletionModel;
+typedef struct _GtkSourceCompletionList         GtkSourceCompletionList;
+typedef struct _GtkSourceCompletionListBox      GtkSourceCompletionListBox;
+typedef struct _GtkSourceCompletionListBoxRow   GtkSourceCompletionListBoxRow;
 typedef struct _GtkSourceContextEngine          GtkSourceContextEngine;
 typedef struct _GtkSourceEngine                 GtkSourceEngine;
 typedef struct _GtkSourceGutterRendererLines    GtkSourceGutterRendererLines;
diff --git a/gtksourceview/gtksourcetypes.h b/gtksourceview/gtksourcetypes.h
index 9c7a6b70..5a313fa9 100644
--- a/gtksourceview/gtksourcetypes.h
+++ b/gtksourceview/gtksourcetypes.h
@@ -36,9 +36,9 @@ G_BEGIN_DECLS
  */
 
 typedef struct _GtkSourceBuffer                    GtkSourceBuffer;
-typedef struct _GtkSourceCompletionContext         GtkSourceCompletionContext;
 typedef struct _GtkSourceCompletion                GtkSourceCompletion;
-typedef struct _GtkSourceCompletionItem            GtkSourceCompletionItem;
+typedef struct _GtkSourceCompletionCell            GtkSourceCompletionCell;
+typedef struct _GtkSourceCompletionContext         GtkSourceCompletionContext;
 typedef struct _GtkSourceCompletionProposal        GtkSourceCompletionProposal;
 typedef struct _GtkSourceCompletionProvider        GtkSourceCompletionProvider;
 typedef struct _GtkSourceEncoding                  GtkSourceEncoding;
diff --git a/gtksourceview/gtksourceview.c b/gtksourceview/gtksourceview.c
index 95a8b292..ac8c13be 100644
--- a/gtksourceview/gtksourceview.c
+++ b/gtksourceview/gtksourceview.c
@@ -311,6 +311,21 @@ static gboolean       gtk_source_view_rgba_drop            (GtkDropTarget
                                                             GtkSourceView           *view);
 static void           gtk_source_view_populate_extra_menu  (GtkSourceView           *view);
 
+static GtkSourceCompletion *
+get_completion (GtkSourceView *self)
+{
+       GtkSourceViewPrivate *priv = gtk_source_view_get_instance_private (self);
+
+       g_assert (GTK_SOURCE_IS_VIEW (self));
+
+       if (priv->completion == NULL)
+       {
+               priv->completion = _gtk_source_completion_new (self);
+       }
+
+       return priv->completion;
+}
+
 static void
 gtk_source_view_constructed (GObject *object)
 {
@@ -1661,15 +1676,8 @@ set_source_buffer (GtkSourceView *view,
 static void
 gtk_source_view_show_completion_real (GtkSourceView *view)
 {
-       GtkSourceCompletion *completion;
-       GtkSourceCompletionContext *context;
-
-       completion = gtk_source_view_get_completion (view);
-       context = gtk_source_completion_create_context (completion, NULL);
-
-       gtk_source_completion_start (completion,
-                                    gtk_source_completion_get_providers (completion),
-                                    context);
+       GtkSourceCompletion *completion = get_completion (view);
+       gtk_source_completion_show (completion);
 }
 
 static void
diff --git a/gtksourceview/gtksourceview.gresource.xml b/gtksourceview/gtksourceview.gresource.xml
index 9321b920..208ab91b 100644
--- a/gtksourceview/gtksourceview.gresource.xml
+++ b/gtksourceview/gtksourceview.gresource.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gnome/gtksourceview/ui">
-    <file preprocess="xml-stripblanks">gtksourcecompletion.ui</file>
     <file preprocess="xml-stripblanks">gtksourcestyleschemechooserwidget.ui</file>
   </gresource>
 </gresources>
diff --git a/gtksourceview/meson.build b/gtksourceview/meson.build
index fc3a9cba..310ab38c 100644
--- a/gtksourceview/meson.build
+++ b/gtksourceview/meson.build
@@ -9,10 +9,10 @@ core_public_h = files([
   'gtksource.h',
   'gtksourcebuffer.h',
   'gtksourcecompletion.h',
+  'gtksourcecompletioncell.h',
   'gtksourcecompletioncontext.h',
-  'gtksourcecompletionitem.h',
-  'gtksourcecompletionproposal.h',
   'gtksourcecompletionprovider.h',
+  'gtksourcecompletionproposal.h',
   'gtksourceencoding.h',
   'gtksourcefile.h',
   'gtksourcefileloader.h',
@@ -48,10 +48,10 @@ core_public_h = files([
 core_public_c = files([
   'gtksourcebuffer.c',
   'gtksourcecompletion.c',
+  'gtksourcecompletioncell.c',
   'gtksourcecompletioncontext.c',
-  'gtksourcecompletionitem.c',
-  'gtksourcecompletionproposal.c',
   'gtksourcecompletionprovider.c',
+  'gtksourcecompletionproposal.c',
   'gtksourceencoding.c',
   'gtksourcefile.c',
   'gtksourcefileloader.c',
@@ -92,7 +92,9 @@ core_private_c = files([
   'gtksourcebufferinternal.c',
   'gtksourcebufferoutputstream.c',
   'gtksourcecompletioninfo.c',
-  'gtksourcecompletionmodel.c',
+  'gtksourcecompletionlist.c',
+  'gtksourcecompletionlistbox.c',
+  'gtksourcecompletionlistboxrow.c',
   'gtksourcecontextengine.c',
   'gtksourceengine.c',
   'gtksourcegutterrendererlines.c',


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