[gtksourceview/gtksourcecompletion] Rework of proposal populating and modelling.



commit dbe49b31fea018642a89ff8b6340e940b15b570f
Author: Jesse van den Kieboom <jessevdk gnome org>
Date:   Sun Sep 13 23:39:37 2009 +0200

    Rework of proposal populating and modelling.
    
    This is a big rework of how proposals are populated and how
    the whole workflow of completion works. There is now a special
    'context' object which glues together the completion and the providers.
    This allows proposals to be added asynchronous and better handling of
    provider matching and passing context information for proposal
    populating.

 gtksourceview/Makefile.am                   |    4 +-
 gtksourceview/gtksourcecompletion.c         |  904 ++++++++++++----------
 gtksourceview/gtksourcecompletion.h         |   46 +-
 gtksourceview/gtksourcecompletioncontext.c  |  256 ++++++
 gtksourceview/gtksourcecompletioncontext.h  |   81 ++
 gtksourceview/gtksourcecompletionmodel.c    | 1115 +++++++++++++++------------
 gtksourceview/gtksourcecompletionmodel.h    |   36 +-
 gtksourceview/gtksourcecompletionproposal.c |   35 +
 gtksourceview/gtksourcecompletionproposal.h |    8 +
 gtksourceview/gtksourcecompletionprovider.c |  108 ++--
 gtksourceview/gtksourcecompletionprovider.h |   32 +-
 gtksourceview/gtksourceview.c               |   46 +-
 tests/completion-simple.c                   |   71 +-
 tests/gsc-provider-devhelp.c                |  292 ++++++--
 tests/gsc-provider-test.c                   |  122 +++-
 15 files changed, 2025 insertions(+), 1131 deletions(-)
---
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index 7bd1ea0..c9b86ed 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -32,7 +32,8 @@ libgtksourceview_headers =			\
 	gtksourcecompletioninfo.h		\
 	gtksourcecompletionitem.h		\
 	gtksourcecompletionproposal.h		\
-	gtksourcecompletionprovider.h
+	gtksourcecompletionprovider.h 		\
+	gtksourcecompletioncontext.h
 
 NOINST_H_FILES = \
 	gtksourcecompletionutils.h		\
@@ -73,6 +74,7 @@ libgtksourceview_2_0_la_SOURCES = 	\
 	gtksourcecompletionmodel.c	\
 	gtksourcecompletionmodel.h	\
 	gtksourcecompletionutils.c	\
+	gtksourcecompletioncontext.c 	\
 	$(libgtksourceview_headers)	\
 	$(NOINST_H_FILES)
 
diff --git a/gtksourceview/gtksourcecompletion.c b/gtksourceview/gtksourcecompletion.c
index f8cd7d2..da7fd11 100644
--- a/gtksourceview/gtksourcecompletion.c
+++ b/gtksourceview/gtksourcecompletion.c
@@ -37,6 +37,7 @@
 #include <string.h>
 #include <gtksourceview/gtksourceview.h>
 #include "gtksourcecompletion-private.h"
+#include "gtksourcecompletioncontext.h"
 #include <stdarg.h>
 
 #define WINDOW_WIDTH 350
@@ -51,6 +52,7 @@ enum
 {
 	SHOW,
 	HIDE,
+	POPULATE_CONTEXT,
 	LAST_SIGNAL
 };
 
@@ -63,7 +65,6 @@ enum
 	PROP_SELECT_ON_SHOW,
 	PROP_SHOW_HEADERS,
 	
-	PROP_MINIMUM_AUTO_COMPLETE_LENGTH,
 	PROP_AUTO_COMPLETE_DELAY
 };
 
@@ -102,28 +103,33 @@ struct _GtkSourceCompletionPrivate
 	GtkSourceView *view;
 
 	GList *providers;
-	GHashTable *capability_map;
-	GList *active_providers;
+	GList *interactive_providers;
 	
+	GtkSourceCompletionContext *context;
+	GList *active_providers;
+	GList *running_providers;
+
 	guint show_timed_out_id;
 	guint auto_complete_delay;
-	guint minimum_auto_complete_length;
 	
 	gint typing_line;
 	gint typing_line_offset;
-	
-	GtkSourceCompletionProvider *filter_provider;
-	gchar *filter_criteria;
-	
-	gboolean inserting_data;
-	gboolean is_interactive;
+
 	gulong signals_ids[LAST_EXTERNAL_SIGNAL];
+	gboolean select_first;
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE(GtkSourceCompletion, gtk_source_completion, G_TYPE_OBJECT);
 
+static void update_completion (GtkSourceCompletion        *completion,
+                               GList                      *providers,
+                               GtkSourceCompletionContext *context);
+
+static void show_info_cb (GtkWidget           *widget,
+	                  GtkSourceCompletion *completion);
+
 static gboolean
 get_selected_proposal (GtkSourceCompletion          *completion,
                        GtkTreeIter                  *iter,
@@ -139,9 +145,12 @@ get_selected_proposal (GtkSourceCompletion          *completion,
 	{
 		model = GTK_TREE_MODEL (completion->priv->model_proposals);
 		
-		gtk_tree_model_get (model, &piter,
-				    GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROPOSAL,
-				    proposal, -1);
+		if (proposal)
+		{
+			gtk_tree_model_get (model, &piter,
+					    GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROPOSAL,
+					    proposal, -1);
+		}
 		
 		if (iter != NULL)
 		{
@@ -264,12 +273,13 @@ select_proposal (GtkSourceCompletion *completion,
 
 static void
 scroll_to_iter (GtkSourceCompletion *completion,
-                GtkTreeModel        *model,
                 GtkTreeIter         *iter)
 {
 	GtkTreePath *path;
 
-	path = gtk_tree_model_get_path (model, iter);
+	path = gtk_tree_model_get_path (GTK_TREE_MODEL (completion->priv->model_proposals), 
+	                                iter);
+
 	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view_proposals),
 				      path, 
 				      NULL, 
@@ -302,7 +312,7 @@ selector_first (GtkSourceCompletion *completion,
 	
 	if (hasfirst && !ret)
 	{
-		scroll_to_iter (completion, model, &first);
+		scroll_to_iter (completion, &first);
 	}
 	
 	return ret;
@@ -334,7 +344,7 @@ selector_last (GtkSourceCompletion *completion,
 	
 	if (haslast && !ret)
 	{
-		scroll_to_iter (completion, model, &last);
+		scroll_to_iter (completion, &last);
 	}
 	
 	return ret;
@@ -376,7 +386,7 @@ selector_previous (GtkSourceCompletion *completion,
 	
 	if (!ret)
 	{
-		scroll_to_iter (completion, model, &last);
+		scroll_to_iter (completion, &last);
 	}
 	
 	return ret;
@@ -417,7 +427,7 @@ selector_next (GtkSourceCompletion *completion,
 	
 	if (!ret)
 	{
-		scroll_to_iter (completion, model, &last);
+		scroll_to_iter (completion, &last);
 	}
 	
 	return ret;
@@ -449,18 +459,45 @@ select_next_proposal (GtkSourceCompletion *completion,
 	return select_proposal (completion, selector_next, GINT_TO_POINTER (rows));
 }
 
+static GtkSourceCompletionProvider *
+get_visible_provider (GtkSourceCompletion *completion)
+{
+	GtkSourceCompletionModel *model = completion->priv->model_proposals;
+	GList *visible;
+	
+	visible = gtk_source_completion_model_get_visible_providers (model);
+
+	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;
 	GList *item;
+	GtkSourceCompletionProvider *visible;
+	
+	visible = get_visible_provider (completion);
+	
 	*num = 0;
 	*current = 0;
 	
-	for (item = completion->priv->active_providers; item; item = g_list_next (item))
+	providers = gtk_source_completion_model_get_providers (completion->priv->model_proposals);
+	
+	for (item = providers; item; item = g_list_next (item))
 	{
-		if (item->data == completion->priv->filter_provider)
+		/* This works for now since we only show either all providers,
+		   or a single one */
+		if (item->data == visible)
 		{
 			*current = ++*num;
 		}
@@ -483,23 +520,26 @@ update_selection_label (GtkSourceCompletion *completion)
 	guint num;
 	gchar *name;
 	gchar *tmp;
+	GtkSourceCompletionProvider *visible;
+	
+	visible = get_visible_provider (completion);
 	
 	get_num_visible_providers (completion, &num, &pos);
 	
-	if (completion->priv->filter_provider == NULL)
+	if (visible == NULL)
 	{
-		name = g_strdup_printf("[<i>%s</i>]", _("All"));
+		name = g_strdup_printf("<b>%s</b>", _("All"));
 		
 		gtk_image_clear (GTK_IMAGE (completion->priv->selection_image));
 	}
 	else
 	{
 		name = g_markup_escape_text (
-			gtk_source_completion_provider_get_name (completion->priv->filter_provider),
+			gtk_source_completion_provider_get_name (visible),
 			-1);
 
 		gtk_image_set_from_pixbuf (GTK_IMAGE (completion->priv->selection_image),
-                           (GdkPixbuf *)gtk_source_completion_provider_get_icon (completion->priv->filter_provider));
+                           (GdkPixbuf *)gtk_source_completion_provider_get_icon (visible));
 	}
 	
 	if (num > 1)
@@ -519,19 +559,35 @@ update_selection_label (GtkSourceCompletion *completion)
 }
 
 static void
-do_refilter (GtkSourceCompletion *completion,
-             gboolean             hide_if_empty)
+visible_provider_changed (GtkSourceCompletion *completion)
 {
-	gtk_source_completion_model_refilter (completion->priv->model_proposals);
+	GtkTreeSelection *selection;
+	GtkTreeIter iter;
+	
+	update_selection_label (completion);
+	
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view_proposals));
 	
-	/* Check if there are any proposals left */
-	if (hide_if_empty && !completion->priv->inserting_data &&
-	    gtk_source_completion_model_is_empty (completion->priv->model_proposals, FALSE))
+	if (gtk_tree_selection_get_selected (selection, NULL, &iter))
 	{
-		gtk_source_completion_hide (completion);
+		GtkTreePath *path;
+
+ 		path = gtk_tree_model_get_path (GTK_TREE_MODEL (completion->priv->model_proposals), &iter);
+
+		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view_proposals),
+	                                      path,
+	                                      NULL,
+	                                      FALSE,
+	                                      0,
+	                                      0);
+		gtk_tree_path_free (path);
+	}
+	else
+	{
+		gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (completion->priv->tree_view_proposals),
+	                                       0,
+	                                       0);
 	}
-	
-	update_selection_label (completion);
 }
 
 typedef GList * (*ListSelector)(GList *);
@@ -549,42 +605,40 @@ select_provider (GtkSourceCompletion *completion,
 	GtkSourceCompletionProvider *provider;
 	guint num;
 	guint pos;
+	GList *providers;
+	GtkSourceCompletionProvider *visible;
 	
-	/* If there is only one provider, then there is no other selection */
-	if (completion->priv->active_providers->next == NULL)
-	{
-		return FALSE;
-	}
+	providers = gtk_source_completion_model_get_providers (completion->priv->model_proposals);
+	visible = get_visible_provider (completion);
 	
 	get_num_visible_providers (completion, &num, &pos);
 	
 	if (num <= 1)
 	{
-		if (completion->priv->filter_provider != NULL)
+		if (visible != NULL)
 		{
-			completion->priv->filter_provider = NULL;
-			
-			update_selection_label (completion);
-			do_refilter (completion, FALSE);
-			
+			gtk_source_completion_model_set_visible_providers (
+					completion->priv->model_proposals,
+			                NULL);
+
+			visible_provider_changed (completion);
 			return TRUE;
 		}
 
 		return FALSE;
 	}
 
-	if (completion->priv->filter_provider != NULL)
+	if (visible != NULL)
 	{
-		orig = g_list_find (completion->priv->active_providers,
-		                    completion->priv->filter_provider);
+		orig = g_list_find (providers, visible);
 	}
 	else
 	{
 		orig = NULL;
 	}
 	
-	first = cycle_first (completion->priv->active_providers);
-	last = cycle_last (completion->priv->active_providers);
+	first = cycle_first (providers);
+	last = cycle_last (providers);
 	current = orig;
 	
 	do
@@ -625,16 +679,20 @@ select_provider (GtkSourceCompletion *completion,
 	
 	if (current != NULL)
 	{
-		completion->priv->filter_provider = current->data;
+		GList *providers = g_list_append (NULL, current->data);
+
+		gtk_source_completion_model_set_visible_providers (completion->priv->model_proposals,
+		                                                   providers);
+		g_list_free (providers);
+		visible_provider_changed (completion);
 	}
 	else
 	{
-		completion->priv->filter_provider = NULL;
+		gtk_source_completion_model_set_visible_providers (completion->priv->model_proposals,
+		                                                   NULL);
+		visible_provider_changed (completion);
 	}
 	
-	update_selection_label (completion);
-	do_refilter (completion, FALSE);	
-	
 	return TRUE;
 }
 
@@ -693,51 +751,6 @@ update_info_position (GtkSourceCompletion *completion)
 	gtk_window_move (GTK_WINDOW (completion->priv->info_window), x, y);
 }
 
-static GtkSourceCompletionModelFilterFlag
-proposals_filter_func (GtkSourceCompletionModel    *model,
-                       GtkSourceCompletionProvider *provider,
-                       GtkSourceCompletionProposal *proposal,
-                       GtkSourceCompletion         *completion)
-{
-	GtkSourceCompletionModelFilterFlag ret = GTK_SOURCE_COMPLETION_MODEL_NONE;
-	gboolean visible;
-	gboolean count;
-	GtkTextIter iter;
-	
-	/* Filter on provider */
-	if (completion->priv->filter_provider != NULL && completion->priv->filter_provider != provider)
-	{
-		visible = FALSE;
-		count = TRUE;
-	}
-	else if (completion->priv->filter_criteria == NULL)
-	{
-		visible = TRUE;
-		count = FALSE;
-	}
-	else
-	{
-		visible = gtk_source_completion_provider_filter_proposal (provider,
-	                                                                  proposal,
-	                                                                  &iter,
-	                                                                  completion->priv->filter_criteria);
-
-		count = FALSE;
-	}
-	
-	if (!visible)
-	{
-		ret |= GTK_SOURCE_COMPLETION_MODEL_FILTERED;
-		
-		if (count)
-		{
-			ret |= GTK_SOURCE_COMPLETION_MODEL_COUNT;
-		}
-	}
-	
-	return ret;
-}
-
 static void
 row_activated_cb (GtkTreeView         *tree_view,
 		  GtkTreePath         *path,
@@ -767,9 +780,21 @@ update_proposal_info_real (GtkSourceCompletion         *completion,
 		/* Set to default widget */
 		info_widget = completion->priv->default_info;
 		gtk_label_set_markup (GTK_LABEL (info_widget), _("No extra information available"));
+		
+		gtk_widget_hide (GTK_WIDGET (info_window));
 	}
 	else
 	{
+		g_signal_handlers_block_by_func (completion->priv->info_window,
+		                                 G_CALLBACK (show_info_cb),
+		                                 completion);
+
+		gtk_widget_show (completion->priv->info_window);
+
+		g_signal_handlers_unblock_by_func (completion->priv->info_window,
+		                                   G_CALLBACK (show_info_cb),
+		                                   completion);
+
 		info_widget = gtk_source_completion_provider_get_info_widget (provider, 
 		                                                              proposal);
 
@@ -812,11 +837,10 @@ update_proposal_info (GtkSourceCompletion *completion)
 	{
 		model = GTK_TREE_MODEL (completion->priv->model_proposals);
 		gtk_tree_model_get (model, &iter, GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROVIDER, &provider, -1);
-		
+	
 		update_proposal_info_real (completion, provider, proposal);
-		
-		g_object_unref (proposal);
 		g_object_unref (provider);
+		g_object_unref (proposal);
 	}
 	else
 	{
@@ -828,7 +852,16 @@ static void
 selection_changed_cb (GtkTreeSelection    *selection, 
 		      GtkSourceCompletion *completion)
 {
-	if (GTK_WIDGET_VISIBLE (completion->priv->info_window))
+	if (get_selected_proposal (completion, NULL, NULL))
+	{
+		completion->priv->select_first = FALSE;
+	}
+	else if (completion->priv->select_on_show)
+	{
+		completion->priv->select_first = TRUE;
+	}
+	
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (completion->priv->info_button)))
 	{
 		update_proposal_info (completion);
 	}
@@ -874,16 +907,6 @@ show_info_after_cb (GtkWidget           *widget,
 }
 
 static void
-hide_info_cb (GtkWidget *widget,
-	      gpointer user_data)
-{
-	GtkSourceCompletion *completion = GTK_SOURCE_COMPLETION (user_data);
-
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (completion->priv->info_button),
-				      FALSE);
-}
-
-static void
 info_size_allocate_cb (GtkWidget           *widget,
                        GtkAllocation       *allocation,
                        GtkSourceCompletion *completion)
@@ -1038,20 +1061,6 @@ view_key_press_event_cb (GtkSourceView       *view,
 }
 
 static void
-refilter_proposals_with_word (GtkSourceCompletion *completion)
-{
-	GtkTextView *view;
-	
-	g_free (completion->priv->filter_criteria);
-	view = GTK_TEXT_VIEW (completion->priv->view);
-	
-	completion->priv->filter_criteria = 
-		gtk_source_completion_utils_get_word (GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (view)));
-	
-	do_refilter (completion, TRUE);
-}
-
-static void
 update_typing_offsets (GtkSourceCompletion *completion)
 {
 	GtkTextBuffer *buffer;
@@ -1075,24 +1084,18 @@ show_auto_completion (GtkSourceCompletion *completion)
 {
 	GtkTextBuffer *buffer;
 	GtkTextIter iter;
-	GtkTextIter start;
-	GtkTextIter end;
-	gchar *word;
-	GList *providers;
+	GtkSourceCompletionContext *context;
 	
 	completion->priv->show_timed_out_id = 0;
 	
-	providers = g_hash_table_lookup (completion->priv->capability_map, 
-	                                 GTK_SOURCE_COMPLETION_CAPABILITY_INTERACTIVE);
-	
-	if (!providers)
+	if (GTK_WIDGET_VISIBLE (completion->priv->window))
 	{
 		return FALSE;
 	}
-
+	
 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (completion->priv->view));
 
-	/* Check if the user has changed the cursor position.If yes, we don't complete */
+	/* Check if the user has changed the cursor position. If yes, we don't complete */
 	get_iter_at_insert (completion, &iter);
 	
 	if ((gtk_text_iter_get_line (&iter) != completion->priv->typing_line))
@@ -1100,21 +1103,13 @@ show_auto_completion (GtkSourceCompletion *completion)
 		return FALSE;
 	}
 	
-	word = gtk_source_completion_utils_get_word_iter (GTK_SOURCE_BUFFER (buffer),
-	                                                  &iter,
-							  &start,
-							  &end);
+	context = gtk_source_completion_create_context (completion, &iter);
+	g_object_set (context, "interactive", TRUE, NULL);
 	
-	/* Check minimum amount of characters */
-	if (g_utf8_strlen (word, -1) >= completion->priv->minimum_auto_complete_length)
-	{
-		gtk_source_completion_show (completion, providers,
-					    word, &start);
-		completion->priv->is_interactive = TRUE;
-	}
+	gtk_source_completion_show (completion, 
+	                            completion->priv->interactive_providers, 
+	                            context);
 
-	g_free (word);
-	
 	return FALSE;
 }
 
@@ -1126,7 +1121,6 @@ interactive_do_show (GtkSourceCompletion *completion)
 	if (completion->priv->show_timed_out_id != 0)
 	{
 		g_source_remove (completion->priv->show_timed_out_id);
-		completion->priv->show_timed_out_id = 0;
 	}
 
 	completion->priv->show_timed_out_id = 
@@ -1135,31 +1129,40 @@ interactive_do_show (GtkSourceCompletion *completion)
 			       completion);
 }
 
-static gboolean
-buffer_delete_range_cb (GtkTextBuffer       *buffer,
-                        GtkTextIter         *start,
-                        GtkTextIter         *end,
-                        GtkSourceCompletion *completion)
+static void
+update_interactive_completion (GtkSourceCompletion *completion,
+                               GtkTextIter         *iter)
 {
-	if (!GTK_WIDGET_VISIBLE (completion->priv->window))
+	if (completion->priv->context == NULL)
 	{
+		/* Schedule for interactive showing */
 		interactive_do_show (completion);
 	}
+	else if (gtk_source_completion_context_get_interactive (completion->priv->context) &&
+	         gtk_text_iter_get_line (iter) != completion->priv->typing_line)
+	{
+		gtk_source_completion_hide (completion);
+	}
 	else
 	{
-		if (gtk_text_iter_get_line (start) != completion->priv->typing_line ||
-		    (completion->priv->is_interactive && 
-		     gtk_text_iter_get_line_offset (start) < completion->priv->typing_line_offset +
-		     completion->priv->minimum_auto_complete_length))
-		{
-			gtk_source_completion_hide (completion);
-		}
-		else
-		{
-			refilter_proposals_with_word (completion);
-		}
+		/* Update iter in context */
+		g_object_set (completion->priv->context,
+		              "iter", iter,
+	                      NULL);
+
+		update_completion (completion, 
+			           completion->priv->active_providers,
+			           completion->priv->context);
 	}
-	
+}
+
+static gboolean
+buffer_delete_range_cb (GtkTextBuffer       *buffer,
+                        GtkTextIter         *start,
+                        GtkTextIter         *end,
+                        GtkSourceCompletion *completion)
+{
+	update_interactive_completion (completion, start);
 	return FALSE;
 }
 
@@ -1170,30 +1173,7 @@ buffer_insert_text_cb (GtkTextBuffer       *buffer,
                        gint                 len,
                        GtkSourceCompletion *completion)
 {
-	/* Only handle typed text */
-	if (len > 1 && completion->priv->is_interactive)
-	{
-		gtk_source_completion_hide (completion);
-		return;
-	}
-	
-	if (!GTK_WIDGET_VISIBLE (completion->priv->window))
-	{
-		interactive_do_show (completion);
-	}
-	else
-	{
-		if ((completion->priv->is_interactive && 
-		     gtk_source_completion_utils_is_separator (g_utf8_get_char (text))) ||
-		    gtk_text_iter_get_line (location) != completion->priv->typing_line)
-		{
-			gtk_source_completion_hide (completion);
-		}
-		else
-		{
-			refilter_proposals_with_word (completion);
-		}
-	}
+	update_interactive_completion (completion, location);
 }
 
 static void
@@ -1258,10 +1238,47 @@ connect_view (GtkSourceCompletion *completion)
 }
 
 static void
+cancel_completion (GtkSourceCompletion        *completion,
+                   GtkSourceCompletionContext *context)
+{
+	if (completion->priv->context == NULL)
+	{
+		if (context != NULL)
+		{
+			completion->priv->context = g_object_ref_sink (context);
+		}
+	}
+	else
+	{
+		/* Inform providers of cancellation through the context */
+		_gtk_source_completion_context_cancel (completion->priv->context);
+
+		/* Let the model know we are cancelling the population */
+		gtk_source_completion_model_cancel (completion->priv->model_proposals);
+
+		if (completion->priv->context != context)
+		{
+			g_object_unref (completion->priv->context);
+			completion->priv->context = NULL;
+		}
+		else if (context != NULL)
+		{
+			completion->priv->context = g_object_ref_sink (context);
+		}
+	
+		g_list_free (completion->priv->running_providers);
+		completion->priv->running_providers = NULL;
+	}
+}
+
+static void
 gtk_source_completion_dispose (GObject *object)
 {
 	GtkSourceCompletion *completion = GTK_SOURCE_COMPLETION (object);
 	
+	/* Cancel running completion */
+	cancel_completion (completion, NULL);
+	
 	if (completion->priv->view != NULL)
 	{
 		disconnect_view (completion);
@@ -1272,6 +1289,9 @@ gtk_source_completion_dispose (GObject *object)
 		g_list_foreach (completion->priv->providers, (GFunc)g_object_unref, NULL);
 	}
 	
+	g_list_free (completion->priv->active_providers);
+	g_list_free (completion->priv->interactive_providers);
+	
 	G_OBJECT_CLASS (gtk_source_completion_parent_class)->dispose (object);
 }
 
@@ -1285,10 +1305,8 @@ gtk_source_completion_finalize (GObject *object)
 		g_source_remove (completion->priv->show_timed_out_id);
 	}
 	
-	g_hash_table_destroy (completion->priv->capability_map);
 	g_list_free (completion->priv->providers);
-	
-	g_free (completion->priv->filter_criteria);
+	g_list_free (completion->priv->active_providers);
 	
 	G_OBJECT_CLASS (gtk_source_completion_parent_class)->finalize (object);
 }
@@ -1325,9 +1343,6 @@ gtk_source_completion_get_property (GObject    *object,
 		case PROP_AUTO_COMPLETE_DELAY:
 			g_value_set_uint (value, completion->priv->auto_complete_delay);
 			break;
-		case PROP_MINIMUM_AUTO_COMPLETE_LENGTH:
-			g_value_set_uint (value, completion->priv->minimum_auto_complete_length);
-			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -1374,9 +1389,6 @@ gtk_source_completion_set_property (GObject      *object,
 		case PROP_AUTO_COMPLETE_DELAY:
 			completion->priv->auto_complete_delay = g_value_get_uint (value);
 			break;
-		case PROP_MINIMUM_AUTO_COMPLETE_LENGTH:
-			completion->priv->minimum_auto_complete_length = g_value_get_uint (value);
-			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -1386,19 +1398,18 @@ gtk_source_completion_set_property (GObject      *object,
 static void
 gtk_source_completion_hide_default (GtkSourceCompletion *completion)
 {
-	completion->priv->filter_provider = NULL;
-
 	gtk_label_set_markup (GTK_LABEL (completion->priv->default_info), "");
 
+	cancel_completion (completion, NULL);
 	gtk_source_completion_model_clear (completion->priv->model_proposals);
 
 	g_list_free (completion->priv->active_providers);
 	completion->priv->active_providers = NULL;
 	
-	g_free (completion->priv->filter_criteria);
-	completion->priv->filter_criteria = NULL;
-	
-	completion->priv->info_visible = GTK_WIDGET_VISIBLE (completion->priv->info_window);
+	completion->priv->select_first = FALSE;
+
+	completion->priv->info_visible = 
+		gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (completion->priv->info_button));
 	
 	gtk_widget_hide (completion->priv->info_window);
 	gtk_widget_hide (completion->priv->window);
@@ -1472,7 +1483,7 @@ gtk_source_completion_class_init (GtkSourceCompletionClass *klass)
 					 g_param_spec_boolean ("remember-info-visibility",
 							      _("Remeber Info Visibility"),
 							      _("Remember the last info window visibility state"),
-							      FALSE,
+							      TRUE,
 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 	/**
 	 * GtkSourceCompletion:select-on-show:
@@ -1520,21 +1531,6 @@ gtk_source_completion_class_init (GtkSourceCompletionClass *klass)
 							    G_MAXUINT,
 							    250,
 							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-	/**
-	 * GtkSourceCompletion:min-len:
-	 *
-	 * The minimum word length to initiate interactive completion.
-	 *
-	 */
-	g_object_class_install_property (object_class,
-					 PROP_MINIMUM_AUTO_COMPLETE_LENGTH,
-					 g_param_spec_uint ("minimum-auto-complete-length",
-							    _("Minimum Auto Complete Length"),
-							    _("Minimum word length to initiate interactive completion"),
-							    0,
-							    G_MAXUINT,
-							    3,
-							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
 	/**
 	 * GtkSourceCompletion::show:
@@ -1574,6 +1570,18 @@ gtk_source_completion_class_init (GtkSourceCompletionClass *klass)
 			      g_cclosure_marshal_VOID__VOID, 
 			      G_TYPE_NONE,
 			      0);
+
+	signals[POPULATE_CONTEXT] =
+		g_signal_new ("populate-context",
+		              G_TYPE_FROM_CLASS (klass),
+		              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		              G_STRUCT_OFFSET (GtkSourceCompletionClass, populate_context),
+		              NULL, 
+		              NULL,
+		              g_cclosure_marshal_VOID__OBJECT, 
+		              G_TYPE_NONE,
+		              1,
+		              GTK_TYPE_SOURCE_COMPLETION_CONTEXT);
 }
 
 static void
@@ -1717,51 +1725,72 @@ selection_func (GtkTreeSelection    *selection,
 }
 
 static void
-on_row_inserted_cb (GtkTreeModel *tree_model,
-		    GtkTreePath  *path,
-		    GtkTreeIter  *iter,
-		    GtkSourceCompletion *completion)
+check_first_selected (GtkSourceCompletion *completion)
 {
-	if (!GTK_WIDGET_VISIBLE (completion->priv->window))
+	GtkTreeSelection *selection;
+	GtkTreeIter piter;
+	GtkTreeIter first;
+	GtkTreeModel *model;
+
+	model = GTK_TREE_MODEL (completion->priv->model_proposals);
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view_proposals));
+	
+	if (!completion->priv->select_first)
 	{
-		update_selection_label (completion);
+		return;
+	}
 	
-		if (!completion->priv->remember_info_visibility)
-			completion->priv->info_visible = FALSE;
-		
-		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (completion->priv->info_button),
-					      completion->priv->info_visible);
+	if (!gtk_tree_model_get_iter_first (model, &first))
+	{
+		return;
+	}
 	
-		g_signal_emit (completion, signals[SHOW], 0);
+	piter = first;
+	
+	while (gtk_source_completion_model_iter_is_header (completion->priv->model_proposals, &piter))
+	{
+		if (!gtk_tree_model_iter_next (model, &piter))
+		{
+			return;
+		}
 	}
 	
-	gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (completion->priv->tree_view_proposals),
-	                               0,
-	                               0);
+	gtk_tree_selection_select_iter (selection, &piter);
+	
+	gtk_tree_model_get_iter_first (model, &piter);
+	scroll_to_iter (completion, &first);
+
+	completion->priv->select_first = TRUE;
 }
 
 static void
-on_items_added_cb (GtkSourceCompletionModel *model,
-		   GtkSourceCompletion      *completion)
+on_row_inserted_cb (GtkTreeModel        *tree_model,
+                    GtkTreePath         *path,
+                    GtkTreeIter         *iter,
+                    GtkSourceCompletion *completion)
 {
-	completion->priv->inserting_data = FALSE;
-
-	/* Check if there are any completions */
-	if (gtk_source_completion_model_is_empty (model, FALSE))
+	if (!GTK_WIDGET_VISIBLE (completion->priv->window))
 	{
-		gtk_source_completion_hide (completion);
+		if (!completion->priv->remember_info_visibility)
+		{
+			completion->priv->info_visible = FALSE;
+		}
+		
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (completion->priv->info_button),
+					      completion->priv->info_visible);
+	
+		g_signal_emit (completion, signals[SHOW], 0);
 	}
+	
+	check_first_selected (completion);
 }
 
 static void
-on_filter_done_cb (GtkSourceCompletionModel *model,
-		   GtkSourceCompletion      *completion)
+on_row_deleted_cb (GtkTreeModel        *tree_model,
+                   GtkTreePath         *path,
+                   GtkSourceCompletion *completion)
 {
-	/* Check if there are any completions */
-	if (gtk_source_completion_model_is_empty (model, FALSE))
-	{
-		gtk_source_completion_hide (completion);
-	}
+	check_first_selected (completion);
 }
 
 static GtkWidget *
@@ -1773,19 +1802,7 @@ initialize_proposals_ui (GtkSourceCompletion *completion)
 	GtkWidget *scrolled_window;
 	GtkWidget *tree_view;
 	
-	completion->priv->model_proposals = 
-		gtk_source_completion_model_new ((GtkSourceCompletionModelVisibleFunc)proposals_filter_func, 
-		                                 completion);
-	
-	g_signal_connect (completion->priv->model_proposals,
-			  "items-added",
-			  G_CALLBACK (on_items_added_cb),
-			  completion);
-
-	g_signal_connect (completion->priv->model_proposals,
-			  "filter-done",
-			  G_CALLBACK (on_filter_done_cb),
-			  completion);
+	completion->priv->model_proposals = gtk_source_completion_model_new ();
 
 	gtk_source_completion_model_set_show_headers (completion->priv->model_proposals,
 				                      completion->priv->show_headers);
@@ -1796,6 +1813,11 @@ initialize_proposals_ui (GtkSourceCompletion *completion)
 	                        "row-inserted",
 	                        G_CALLBACK (on_row_inserted_cb),
 	                        completion);
+
+	g_signal_connect_after (completion->priv->model_proposals,
+	                        "row-deleted",
+	                        G_CALLBACK (on_row_deleted_cb),
+	                        completion);
 	
 	gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (tree_view), FALSE);
 	
@@ -1983,11 +2005,6 @@ initialize_ui (GtkSourceCompletion *completion)
 			  completion);
 			  
 	g_signal_connect (completion->priv->info_window,
-			  "hide",
-			  G_CALLBACK (hide_info_cb),
-			  completion);
-
-	g_signal_connect (completion->priv->info_window,
 	                  "size-allocate",
 	                  G_CALLBACK(info_size_allocate_cb),
 	                  completion);
@@ -1997,121 +2014,162 @@ static void
 gtk_source_completion_init (GtkSourceCompletion *completion)
 {
 	completion->priv = GTK_SOURCE_COMPLETION_GET_PRIVATE (completion);
-
-	completion->priv->capability_map = g_hash_table_new_full (g_str_hash, 
-	                                                          g_str_equal,
-	                                                          (GDestroyNotify)g_free,
-	                                                          (GDestroyNotify)g_list_free);
-	                                                     
-
 	initialize_ui (completion);
 }
 
 static void
-add_proposals (GtkSourceCompletion         *completion,
-               GtkSourceCompletionProvider *provider)
+update_completion (GtkSourceCompletion        *completion,
+                   GList                      *providers,
+                   GtkSourceCompletionContext *context)
 {
-	GList *proposals;
+	GtkTextIter location;
 	GList *item;
-	GtkSourceCompletionProposal *proposal;
-	GtkTextIter iter;
-
-	get_iter_at_insert (completion, &iter);
 	
-	proposals = gtk_source_completion_provider_get_proposals (provider, &iter);
+	update_typing_offsets (completion);
+	
+	gtk_source_completion_context_get_iter (context, &location);
+	
+	/* Move completion window */
+	gtk_source_completion_utils_move_to_iter (GTK_WINDOW (completion->priv->window),
+	                                          GTK_SOURCE_VIEW (completion->priv->view),
+	                                          &location);
 	
-	completion->priv->inserting_data = TRUE;
+	if (GTK_WIDGET_VISIBLE (completion->priv->info_window))
+	{
+		/* Move info window accordingly */
+		update_info_position (completion);
+	}
+	
+	/* Make sure to first cancel any running completion */
+	cancel_completion (completion, context);
+	
+	completion->priv->running_providers = g_list_copy (providers);
 	
-	for (item = proposals; item; item = g_list_next (item))
+	if (completion->priv->active_providers != providers)
 	{
-		if (GTK_IS_SOURCE_COMPLETION_PROPOSAL (item->data))
-		{
-			proposal = GTK_SOURCE_COMPLETION_PROPOSAL (item->data);
-			gtk_source_completion_model_append (completion->priv->model_proposals,
-	                                                    provider,
-	                                                    proposal);
-			g_object_unref (proposal);
-		}
+		g_list_free (completion->priv->active_providers);
+		completion->priv->active_providers = g_list_copy (providers);
 	}
-
-	gtk_source_completion_model_run_add_proposals (completion->priv->model_proposals);
 	
-	g_list_free (proposals);
-}
-
-static gchar **
-get_separate_capabilities (GtkSourceCompletionProvider *provider)
-{
-	const gchar *capabilities;
-	capabilities = gtk_source_completion_provider_get_capabilities (provider);
+	completion->priv->select_first = 
+		completion->priv->select_on_show &&
+		(!get_selected_proposal (completion, NULL, NULL) || completion->priv->select_first);
 	
-	return g_strsplit_set (capabilities, " ,", -1);
+	gtk_source_completion_model_begin (completion->priv->model_proposals,
+	                                   completion->priv->active_providers);
+	
+	for (item = providers; item != NULL; item = g_list_next (item))
+	{
+		GtkSourceCompletionProvider *provider = 
+			GTK_SOURCE_COMPLETION_PROVIDER (item->data);
+
+		gtk_source_completion_provider_populate (provider, context);
+	}
 }
 
 static void
-add_capabilities (GtkSourceCompletion          *completion,
-                  GtkSourceCompletionProvider  *provider)
+populating_done (GtkSourceCompletion        *completion,
+                 GtkSourceCompletionContext *context)
 {
-	gchar **caps = get_separate_capabilities (provider);
-	gchar **orig = caps;
-	
-	while (caps && *caps)
+	if (gtk_source_completion_model_is_empty (completion->priv->model_proposals, 
+	                                          FALSE))
 	{
-		GList *ptr = g_hash_table_lookup (completion->priv->capability_map,
-		                                  *caps);
-
-		ptr = g_list_copy (ptr);
-		ptr = g_list_append (ptr, provider);
+		/* No completion made, make sure to hide the window */
+		gtk_source_completion_hide (completion);
+	}
+	else
+	{
+		update_selection_label (completion);
+			
+		if (completion->priv->select_on_show)
+		{
+			/* CHECK: does this actually work? */
+			GtkTreeSelection *selection;
 		
-		g_hash_table_insert (completion->priv->capability_map,
-		                     g_strdup (*caps),
-		                     ptr);
+			selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view_proposals));
 		
-		++caps;
+			if (gtk_tree_selection_count_selected_rows (selection) == 0)
+			{
+				GtkTreePath *path = gtk_tree_path_new_first ();
+				gtk_tree_selection_select_path (selection, path);
+				gtk_tree_path_free (path);
+			}
+		}
 	}
+}
+
+void
+_gtk_source_completion_add_proposals (GtkSourceCompletion         *completion,
+                                      GtkSourceCompletionContext  *context,
+                                      GtkSourceCompletionProvider *provider,
+                                      GList                       *proposals,
+                                      gboolean                     finished)
+{
+	GList *item;
+
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION (completion));
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_CONTEXT (context));
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider));
+	g_return_if_fail (completion->priv->context == context);
+
+	item = g_list_find (completion->priv->running_providers, provider);
+	g_return_if_fail (item != NULL);
 	
-	if (orig)
+	gtk_source_completion_model_append (completion->priv->model_proposals, 
+	                                    provider, 
+	                                    proposals);
+	
+	if (finished)
 	{
-		g_strfreev (orig);
+		/* Let the model know this provider is done */
+		gtk_source_completion_model_end (completion->priv->model_proposals,
+		                                 provider);
+		
+		/* Remove provider from list of running providers */
+		completion->priv->running_providers = 
+			g_list_delete_link (completion->priv->running_providers,
+			                    item);
+
+		if (completion->priv->running_providers == NULL)
+		{
+			populating_done (completion, context);
+		}
 	}
 }
 
-static void
-remove_capabilities (GtkSourceCompletion          *completion,
-                     GtkSourceCompletionProvider  *provider)
+static GList *
+select_providers (GtkSourceCompletion        *completion,
+                  GList                      *providers,
+                  GtkSourceCompletionContext *context)
 {
-	gchar **caps = get_separate_capabilities (provider);
-	gchar **orig = caps;
+	/* Select providers based on selection */
+	GList *selection = NULL;
 	
-	while (caps && *caps)
+	if (providers == NULL)
+	{
+		providers = completion->priv->providers;
+	}
+
+	while (providers)
 	{
-		GList *ptr = g_hash_table_lookup (completion->priv->capability_map,
-		                                  *caps);
+		GtkSourceCompletionProvider *provider = 
+			GTK_SOURCE_COMPLETION_PROVIDER (providers->data);
 
-		ptr = g_list_copy (ptr);
-		
-		if (ptr)
+		if (gtk_source_completion_provider_match (provider, context))
 		{
-			ptr = g_list_remove (ptr, provider);
-			g_hash_table_insert (completion->priv->capability_map,
-			                     g_strdup (*caps),
-			                     ptr);
+			selection = g_list_prepend (selection, provider);
 		}
 		
-		++caps;
+		providers = g_list_next (providers);
 	}
 	
-	if (orig)
-	{
-		g_strfreev (orig);
-	}
+	return g_list_reverse (selection);
 }
 
 /**
  * gtk_source_completion_show:
  * @completion: A #GtkSourceCompletion
- * @providers: A list of #GtkSourceCompletionProvider
- * @criteria: The filter criteria
+ * @providers: A list of #GtkSourceCompletionProvider or %NULL
  * @place: The place where you want to position the popup window, or %NULL
  *
  * Shows the show completion window. If @place if %NULL the popup window will
@@ -2120,13 +2178,12 @@ remove_capabilities (GtkSourceCompletion          *completion,
  * Returns: %TRUE if it was possible to the show completion window.
  */
 gboolean
-gtk_source_completion_show (GtkSourceCompletion *completion,
-                            GList               *providers,
-                            const gchar         *criteria,
-                            GtkTextIter         *place)
+gtk_source_completion_show (GtkSourceCompletion        *completion,
+                            GList                      *providers,
+                            GtkSourceCompletionContext *context)
 {
-	GList *l;
-
+	GList *selected_providers;
+	
 	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION (completion), FALSE);
 	
 	/* Make sure to clear any active completion */
@@ -2134,89 +2191,52 @@ gtk_source_completion_show (GtkSourceCompletion *completion,
 	
 	if (providers == NULL)
 	{
+		if (g_object_is_floating (context))
+		{
+			g_object_unref (context);
+		}
+
 		gtk_source_completion_hide (completion);
 		return FALSE;
 	}
 	
-	completion->priv->filter_criteria = g_strdup (criteria);
-	update_typing_offsets (completion);
-
-	if (place == NULL)
-	{
-		gtk_source_completion_utils_move_to_cursor (GTK_WINDOW (completion->priv->window),
-							    GTK_SOURCE_VIEW (completion->priv->view));
-	}
-	else
-	{
-		gtk_source_completion_utils_move_to_iter (GTK_WINDOW (completion->priv->window),
-							  GTK_SOURCE_VIEW (completion->priv->view),
-							  place);
-	}
-
-	/* Make sure all providers are ours */
-	for (l = providers; l; l = g_list_next (l))
+	/* 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 (completion, providers, context);
+	
+	if (selected_providers == NULL)
 	{
-		if (g_list_find (completion->priv->providers,
-		                 l->data) != NULL)
+		if (g_object_is_floating (context))
 		{
-			completion->priv->active_providers =
-				g_list_prepend (completion->priv->active_providers,
-				                l->data);
-
-			add_proposals (completion, GTK_SOURCE_COMPLETION_PROVIDER (l->data));
+			g_object_unref (context);
 		}
-	}
-		
-	completion->priv->active_providers = 
-		g_list_reverse (completion->priv->active_providers);
-
-	completion->priv->is_interactive = FALSE;		
 
-	update_selection_label (completion);
+		gtk_source_completion_hide (completion);
+		return FALSE;
+	}
 	
+	update_completion (completion, selected_providers, context);
+	g_list_free (selected_providers);
+
 	return TRUE;
 }
 
+/**
+ * gtk_source_completion_get_providers:
+ * @completion: The #GtkSourceCompletion
+ *
+ * Get list of providers registered on @completion. The returned list is owned
+ * by the completion and should not be freed.
+ *
+ * Returns: list of #GtkSourceCompletionProvider
+ */
 GList *
-gtk_source_completion_get_providers (GtkSourceCompletion  *completion,
-                                     const gchar          *capabilities)
+gtk_source_completion_get_providers (GtkSourceCompletion *completion)
 {
-	gchar **caps = NULL;
-	gchar **orig;
-	GList *ret = NULL;
-	
 	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION (completion), NULL);
-
-	if (capabilities)
-	{
-		caps = g_strsplit_set (capabilities, " ,", -1);
-	}
-	
-	if (caps)
-	{
-		orig = caps;
-		
-		while (*caps)
-		{
-			GList *ptr = g_hash_table_lookup (completion->priv->capability_map,
-			                                  *caps);
-
-			ret = g_list_concat (ret, g_list_copy (ptr));
-			
-			++caps;
-		}
-		
-		g_strfreev (orig);
-	}
-	else
-	{
-		ret = g_list_copy (completion->priv->providers);
-	}
-		
-	g_list_foreach (ret, (GFunc)g_object_ref, NULL);
-
-	
-	return ret;
+	return completion->priv->providers;
 }
 
 GQuark
@@ -2287,7 +2307,11 @@ gtk_source_completion_add_provider (GtkSourceCompletion          *completion,
 	completion->priv->providers = g_list_append (completion->priv->providers, 
 	                                             g_object_ref (provider));
 
-	add_capabilities (completion, provider);
+	if (gtk_source_completion_provider_get_interactive (provider))
+	{
+		completion->priv->interactive_providers = g_list_append (completion->priv->interactive_providers,
+	                                                                 provider);
+	}
 
 	if (error)
 	{
@@ -2322,9 +2346,14 @@ gtk_source_completion_remove_provider (GtkSourceCompletion          *completion,
 
 	if (item != NULL)
 	{
-		remove_capabilities (completion, provider);
-
 		completion->priv->providers = g_list_remove_link (completion->priv->providers, item);
+		
+		if (gtk_source_completion_provider_get_interactive (provider))
+		{
+			completion->priv->interactive_providers = g_list_remove (completion->priv->interactive_providers,
+		                                                                 provider);
+		}
+
 		g_object_unref (provider);
 
 		if (error)
@@ -2396,3 +2425,30 @@ gtk_source_completion_get_view (GtkSourceCompletion *completion)
 	
 	return completion->priv->view;
 }
+
+GtkSourceCompletionContext *
+gtk_source_completion_create_context (GtkSourceCompletion *completion,
+                                      GtkTextIter         *position)
+{
+	GtkSourceCompletionContext *context;
+	
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION (completion), NULL);
+	
+	context = g_object_new (GTK_TYPE_SOURCE_COMPLETION_CONTEXT,
+	                        "completion", completion,
+	                        NULL);
+
+	if (position == NULL)
+	{
+		GtkTextIter iter;
+		
+		get_iter_at_insert (completion, &iter);
+		g_object_set (context, "iter", &iter, NULL);
+	}
+	else
+	{
+		g_object_set (context, "iter", position, NULL);
+	}
+	
+	return context;
+}
diff --git a/gtksourceview/gtksourcecompletion.h b/gtksourceview/gtksourcecompletion.h
index 8c3b638..f4e9db9 100644
--- a/gtksourceview/gtksourcecompletion.h
+++ b/gtksourceview/gtksourcecompletion.h
@@ -26,6 +26,8 @@
 #include <gtk/gtk.h>
 #include <gtksourceview/gtksourcecompletioninfo.h>
 #include <gtksourceview/gtksourcecompletionprovider.h>
+#include <gtksourceview/gtksourcecompletionmodel.h>
+#include <gtksourceview/gtksourcelanguage.h>
 
 G_BEGIN_DECLS
 
@@ -70,35 +72,47 @@ struct _GtkSourceCompletionClass
 							 GtkSourceCompletionProposal *proposal);
 	void 		(* show)			(GtkSourceCompletion         *completion);
 	void		(* hide)			(GtkSourceCompletion         *completion);
+	void		(* populate_context)		(GtkSourceCompletion         *completion,
+							 GtkSourceCompletionContext  *context);
 };
 
 GType		 gtk_source_completion_get_type			(void) G_GNUC_CONST;
 
 GQuark		 gtk_source_completion_error_quark		(void);
 
-gboolean	 gtk_source_completion_add_provider		(GtkSourceCompletion          *completion,
-								 GtkSourceCompletionProvider  *provider,
-								 GError                      **error);
+gboolean	 gtk_source_completion_add_provider		(GtkSourceCompletion           *completion,
+								 GtkSourceCompletionProvider   *provider,
+								 GError                       **error);
 
-gboolean	 gtk_source_completion_remove_provider		(GtkSourceCompletion          *completion,
-								 GtkSourceCompletionProvider  *provider,
-								 GError                      **error);
+gboolean	 gtk_source_completion_remove_provider		(GtkSourceCompletion           *completion,
+								 GtkSourceCompletionProvider   *provider,
+								 GError                       **error);
 
-GList		*gtk_source_completion_get_providers		(GtkSourceCompletion         *completion,
-                                                                 const gchar                 *capabilities);
-gboolean	 gtk_source_completion_show			(GtkSourceCompletion         *completion,
-								 GList                       *providers,
-								 const gchar                 *criteria,
-								 GtkTextIter                 *place);
+GList		*gtk_source_completion_get_providers		(GtkSourceCompletion           *completion);
 
-void		 gtk_source_completion_hide			(GtkSourceCompletion         *completion);
+gboolean	 gtk_source_completion_show			(GtkSourceCompletion           *completion,
+								 GList                         *providers,
+								 GtkSourceCompletionContext    *context);
+
+void		 gtk_source_completion_hide			(GtkSourceCompletion           *completion);
 
 GtkSourceCompletionInfo *
-		 gtk_source_completion_get_info_window		(GtkSourceCompletion         *completion);
+		 gtk_source_completion_get_info_window		(GtkSourceCompletion           *completion);
 
 struct _GtkSourceView *
-		 gtk_source_completion_get_view			(GtkSourceCompletion	     *completion);
+		 gtk_source_completion_get_view			(GtkSourceCompletion	       *completion);
+
+GtkSourceCompletionContext *
+		 gtk_source_completion_create_context		(GtkSourceCompletion           *completion,
+		 						 GtkTextIter                   *position);
 
+void		 _gtk_source_completion_add_proposals		(GtkSourceCompletion           *completion,
+								 GtkSourceCompletionContext    *context,
+								 GtkSourceCompletionProvider   *provider,
+								 GList                         *proposals,
+		 						 gboolean                       finished);
 G_END_DECLS
 
-#endif 
+#endif
+
+/* vi:ex:ts=8 */
diff --git a/gtksourceview/gtksourcecompletioncontext.c b/gtksourceview/gtksourcecompletioncontext.c
new file mode 100644
index 0000000..e395ee8
--- /dev/null
+++ b/gtksourceview/gtksourcecompletioncontext.c
@@ -0,0 +1,256 @@
+/*
+ * gtksourcecompletioncontext.c
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#include "gtksourcecompletioncontext.h"
+#include "gtksourceview-typebuiltins.h"
+#include "gtksourcecompletionprovider.h"
+#include "gtksourceview-i18n.h"
+#include "gtksourcecompletion.h"
+
+#define GTK_SOURCE_COMPLETION_CONTEXT_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GTK_TYPE_SOURCE_COMPLETION_CONTEXT, GtkSourceCompletionContextPrivate))
+
+struct _GtkSourceCompletionContextPrivate
+{
+	GtkSourceCompletion *completion;
+
+	GtkTextIter iter;
+	gboolean interactive_mode;
+	gboolean default_mode;
+};
+
+/* Properties */
+enum
+{
+	PROP_0,
+	
+	PROP_COMPLETION,
+	PROP_VIEW,
+	PROP_ITER,
+	PROP_INTERACTIVE,
+	PROP_DEFAULT
+};
+
+/* Signals */
+enum
+{
+	CANCELLED,
+	NUM_SIGNALS
+};
+
+guint context_signals[NUM_SIGNALS] = {0,};
+
+G_DEFINE_TYPE (GtkSourceCompletionContext, gtk_source_completion_context, G_TYPE_INITIALLY_UNOWNED)
+
+static void
+gtk_source_completion_context_dispose (GObject *object)
+{
+	GtkSourceCompletionContext *context = GTK_SOURCE_COMPLETION_CONTEXT (object);
+	
+	if (context->priv->completion)
+	{
+		g_object_unref (context->priv->completion);
+		context->priv->completion = NULL;
+	}
+
+	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)
+{
+	GtkSourceCompletionContext *self = GTK_SOURCE_COMPLETION_CONTEXT (object);
+	
+	switch (prop_id)
+	{
+		case PROP_COMPLETION:
+			self->priv->completion = g_value_dup_object (value);
+		break;
+		case PROP_ITER:
+			self->priv->iter = *((GtkTextIter *)g_value_get_pointer (value));
+		break;
+		case PROP_INTERACTIVE:
+			self->priv->interactive_mode = g_value_get_boolean (value);
+		break;
+		case PROP_DEFAULT:
+			self->priv->default_mode = g_value_get_boolean (value);
+		break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gtk_source_completion_context_get_property (GObject    *object,
+                                            guint       prop_id,
+                                            GValue     *value,
+                                            GParamSpec *pspec)
+{
+	GtkSourceCompletionContext *self = GTK_SOURCE_COMPLETION_CONTEXT (object);
+	
+	switch (prop_id)
+	{
+		case PROP_COMPLETION:
+			g_value_set_object (value, self->priv->completion);
+		break;
+		case PROP_VIEW:
+			g_value_set_object (value, gtk_source_completion_get_view (self->priv->completion));
+		break;
+		case PROP_ITER:
+			g_value_set_pointer (value, &(self->priv->iter));
+		break;
+		case PROP_INTERACTIVE:
+			g_value_set_boolean (value, self->priv->interactive_mode);
+		break;
+		case PROP_DEFAULT:
+			g_value_set_boolean (value, self->priv->default_mode);
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+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;
+
+	context_signals[CANCELLED] =
+		g_signal_new ("cancelled",
+		              G_TYPE_FROM_CLASS (klass),
+		              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		              G_STRUCT_OFFSET (GtkSourceCompletionContextClass, cancelled),
+		              NULL, 
+		              NULL,
+		              g_cclosure_marshal_VOID__VOID, 
+		              G_TYPE_NONE,
+		              0);
+	
+	g_object_class_install_property (object_class,
+	                                 PROP_COMPLETION,
+	                                 g_param_spec_object ("completion",
+	                                                      _("Completion"),
+	                                                      _("The completion object to which the context belongs"),
+	                                                      GTK_TYPE_SOURCE_COMPLETION,
+	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_VIEW,
+	                                 g_param_spec_object ("view",
+	                                                      _("View"),
+	                                                      _("The GtkSourceView"),
+	                                                      GTK_TYPE_SOURCE_VIEW,
+	                                                      G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_ITER,
+	                                 g_param_spec_pointer ("iter",
+	                                                       _("Iter"),
+	                                                       _("The GtkTextIter at which the completion was invoked"),
+	                                                       G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_INTERACTIVE,
+	                                 g_param_spec_boolean ("interactive",
+	                                                       _("Interactive"),
+	                                                       _("Whether the completion was invoked in interactive mode"),
+	                                                       FALSE,
+	                                                       G_PARAM_READWRITE));
+	
+	g_object_class_install_property (object_class,
+	                                 PROP_DEFAULT,
+	                                 g_param_spec_boolean ("default",
+	                                                       _("Default"),
+	                                                       _("Whether completion was invoked in default mode"),
+	                                                       FALSE,
+	                                                       G_PARAM_READWRITE));
+	
+	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionContextPrivate));
+}
+
+static void
+gtk_source_completion_context_init (GtkSourceCompletionContext *self)
+{
+	self->priv = GTK_SOURCE_COMPLETION_CONTEXT_GET_PRIVATE (self);
+}
+
+void
+gtk_source_completion_context_add_proposals (GtkSourceCompletionContext  *context,
+                                             GtkSourceCompletionProvider *provider,
+                                             GList                       *proposals,
+                                             gboolean                     finished)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_CONTEXT (context));
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider));
+	
+	_gtk_source_completion_add_proposals (context->priv->completion,
+	                                      context,
+	                                      provider,
+	                                      proposals,
+	                                      finished);
+}
+
+GtkSourceView *
+gtk_source_completion_context_get_view (GtkSourceCompletionContext *context)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_CONTEXT (context), NULL);
+	return gtk_source_completion_get_view (context->priv->completion);
+}
+
+void
+gtk_source_completion_context_get_iter (GtkSourceCompletionContext *context,
+                                        GtkTextIter                *iter)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_CONTEXT (context));
+	*iter = context->priv->iter;
+}
+
+gboolean
+gtk_source_completion_context_get_interactive (GtkSourceCompletionContext *context)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_CONTEXT (context), FALSE);
+	return context->priv->interactive_mode;
+	
+}
+
+gboolean
+gtk_source_completion_context_get_default (GtkSourceCompletionContext *context)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_CONTEXT (context), FALSE);
+	return context->priv->default_mode;
+	
+}
+
+void
+_gtk_source_completion_context_cancel (GtkSourceCompletionContext *context)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_CONTEXT (context));
+	
+	g_signal_emit (context, context_signals[CANCELLED], 0);
+}
diff --git a/gtksourceview/gtksourcecompletioncontext.h b/gtksourceview/gtksourcecompletioncontext.h
new file mode 100644
index 0000000..f93aba2
--- /dev/null
+++ b/gtksourceview/gtksourcecompletioncontext.h
@@ -0,0 +1,81 @@
+/*
+ * gtksourcecompletioncontext.h
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_COMPLETION_CONTEXT_H__
+#define __GTK_SOURCE_COMPLETION_CONTEXT_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_COMPLETION_CONTEXT				(gtk_source_completion_context_get_type ())
+#define GTK_SOURCE_COMPLETION_CONTEXT(obj)				(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_COMPLETION_CONTEXT, GtkSourceCompletionContext))
+#define GTK_SOURCE_COMPLETION_CONTEXT_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_COMPLETION_CONTEXT, GtkSourceCompletionContext const))
+#define GTK_SOURCE_COMPLETION_CONTEXT_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SOURCE_COMPLETION_CONTEXT, GtkSourceCompletionContextClass))
+#define GTK_IS_SOURCE_COMPLETION_CONTEXT(obj)			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_COMPLETION_CONTEXT))
+#define GTK_IS_SOURCE_COMPLETION_CONTEXT_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_COMPLETION_CONTEXT))
+#define GTK_SOURCE_COMPLETION_CONTEXT_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_COMPLETION_CONTEXT, GtkSourceCompletionContextClass))
+
+typedef struct _GtkSourceCompletionContext			GtkSourceCompletionContext;
+typedef struct _GtkSourceCompletionContextClass		GtkSourceCompletionContextClass;
+typedef struct _GtkSourceCompletionContextPrivate	GtkSourceCompletionContextPrivate;
+
+/* Forward declaration */
+struct _GtkSourceCompletionProvider;
+struct _GtkSourceCompletion;
+struct _GtkSourceView;
+
+struct _GtkSourceCompletionContext {
+	GInitiallyUnowned parent;
+	
+	GtkSourceCompletionContextPrivate *priv;
+};
+
+struct _GtkSourceCompletionContextClass {
+	GInitiallyUnownedClass parent_class;
+	
+	void (*cancelled) 	(GtkSourceCompletionContext          *context);
+};
+
+GType gtk_source_completion_context_get_type (void) G_GNUC_CONST;
+
+void 		 gtk_source_completion_context_add_proposals 	(GtkSourceCompletionContext          *context,
+								 struct _GtkSourceCompletionProvider *provider,
+								 GList                               *proposals,
+								 gboolean                             finished);
+
+struct _GtkSourceView *
+		 gtk_source_completion_context_get_view		(GtkSourceCompletionContext          *context);
+void		 gtk_source_completion_context_get_iter		(GtkSourceCompletionContext          *context,
+								 GtkTextIter                         *iter);
+
+gboolean	 gtk_source_completion_context_get_interactive	(GtkSourceCompletionContext          *context);
+gboolean	 gtk_source_completion_context_get_default	(GtkSourceCompletionContext          *context);
+
+void		_gtk_source_completion_context_cancel		(GtkSourceCompletionContext          *context);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_COMPLETION_CONTEXT_H__ */
+
+/* vi:ex:ts=8 */
diff --git a/gtksourceview/gtksourcecompletionmodel.c b/gtksourceview/gtksourcecompletionmodel.c
index 4c90768..0bfd443 100644
--- a/gtksourceview/gtksourcecompletionmodel.c
+++ b/gtksourceview/gtksourcecompletionmodel.c
@@ -21,9 +21,8 @@
  */
 
 #include "gtksourcecompletionmodel.h"
-
-#define ITEMS_PER_CALLBACK 500
-#define FILTER_PER_CALLBACK 1000
+#include "gtksourceview-i18n.h"
+#include "gtksourcecompletionprovider.h"
 
 #define GTK_SOURCE_COMPLETION_MODEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GTK_TYPE_SOURCE_COMPLETION_MODEL, GtkSourceCompletionModelPrivate))
 
@@ -33,17 +32,23 @@ typedef struct
 
 	GtkSourceCompletionProvider *provider;
 	GtkSourceCompletionProposal *proposal;
-	
-	GtkSourceCompletionModelFilterFlag filtered;
+
 	gulong changed_id;
+	gboolean mark;
+	
+	gboolean filtered;
 } ProposalNode;
 
 typedef struct
 {
-	GList *item;
-	guint num;
-	guint visible_items;
-} HeaderInfo;
+	GtkSourceCompletionProvider *provider;
+	GHashTable *proposals;
+	guint num_proposals;
+	gboolean filtered;
+
+	GList *first;
+	GList *last;
+} ProviderInfo;
 
 struct _GtkSourceCompletionModelPrivate
 {
@@ -51,33 +56,16 @@ struct _GtkSourceCompletionModelPrivate
 	GList *store;
 	GList *last;
 	
-	guint num;
-	GHashTable *num_per_provider;
-	
-	GtkSourceCompletionModelVisibleFunc filter;
-	gpointer filter_data;
+	GHashTable *providers_info;
+	GList *providers;
+	GList *visible_providers;
 	
+	guint num;
 	gboolean show_headers;
 	
-	guint idle_id;
-	GQueue *item_queue;
-	
-	guint idle_filter_id;
-
-	GList *next_filter_item;
-	GtkTreeRowReference *next_filter_path;
-};
-
-/* Signals */
-enum
-{
-	ITEMS_ADDED,
-	FILTER_DONE,
-	LAST_SIGNAL
+	gboolean marking;
 };
 
-static guint signals[LAST_SIGNAL] = { 0 };
-
 static void tree_model_iface_init (gpointer g_iface, gpointer iface_data);
 
 G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionModel, 
@@ -87,6 +75,24 @@ G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionModel,
                                                 tree_model_iface_init))
 
 
+static gboolean
+provider_is_visible (GtkSourceCompletionModel    *model,
+                     GtkSourceCompletionProvider *provider)
+{
+	ProviderInfo *info = g_hash_table_lookup (model->priv->providers_info,
+	                                          provider);
+
+	if (info != NULL)
+	{
+		return !info->filtered;
+	}
+	else
+	{
+		return model->priv->visible_providers == NULL ||
+		       g_list_index (model->priv->visible_providers, provider) != -1;
+	}
+}
+
 /* Interface implementation */
 static ProposalNode *
 node_from_iter (GtkTreeIter *iter)
@@ -99,18 +105,20 @@ path_from_list (GtkSourceCompletionModel *model,
                 GList                    *item)
 {
 	gint index = 0;
-	GList *ptr = model->priv->store;
+	GList *ptr;
 	ProposalNode *node;
 	
+	ptr = model->priv->store;
+
 	while (ptr && ptr != item)
 	{
 		node = (ProposalNode *)ptr->data;
-		
+	
 		if (!node->filtered)
 		{
 			++index;
 		}
-		
+	
 		ptr = g_list_next (ptr);
 	}
 	
@@ -162,13 +170,13 @@ get_iter_from_index (GtkSourceCompletionModel *model,
 	{
 		return FALSE;
 	}
-	
+
 	item = model->priv->store;
-	
+
 	while (item != NULL && index >= 0)
 	{
 		node = (ProposalNode *)item->data;
-		
+	
 		if (!node->filtered)
 		{
 			--index;
@@ -273,20 +281,21 @@ tree_model_get_value (GtkTreeModel *tree_model,
 }
 
 static gboolean
-find_first_not_filtered (GList       *item,
-                         GtkTreeIter *iter)
+find_first_not_filtered (GtkSourceCompletionModel *model,
+                         GList                    *item,
+                         GtkTreeIter              *iter)
 {
 	ProposalNode *node;
 
 	while (item)
 	{
 		node = (ProposalNode *)item->data;
-		
+	
 		if (!node->filtered)
 		{
 			break;
 		}
-		
+	
 		item = g_list_next (item);
 	}
 	
@@ -312,7 +321,9 @@ tree_model_iter_next (GtkTreeModel *tree_model,
 	
 	item = g_list_next ((GList *)iter->user_data);
 	
-	return find_first_not_filtered (item, iter);
+	return find_first_not_filtered (GTK_SOURCE_COMPLETION_MODEL (tree_model), 
+	                                item, 
+	                                iter);
 }
 
 static gboolean
@@ -330,7 +341,9 @@ tree_model_iter_children (GtkTreeModel *tree_model,
 	}
 	else
 	{
-		return find_first_not_filtered (GTK_SOURCE_COMPLETION_MODEL (tree_model)->priv->store, iter);
+		GtkSourceCompletionModel *model = GTK_SOURCE_COMPLETION_MODEL (tree_model);
+		return find_first_not_filtered (model,
+		                                model->priv->store, iter);
 	}
 }
 
@@ -433,56 +446,20 @@ tree_model_iface_init (gpointer g_iface,
 }
 
 static void
-free_node (ProposalNode *node)
+proposal_node_free (ProposalNode *node)
 {
-	g_object_unref (node->provider);
-	
 	if (node->proposal != NULL)
 	{
 		if (node->changed_id != 0)
 		{
 			g_signal_handler_disconnect (node->proposal,
-						     node->changed_id);
+			                             node->changed_id);
 		}
+		
 		g_object_unref (node->proposal);
-	}
-	
-	g_slice_free (ProposalNode, node);
-}
+	}		
 
-static void
-cancel_append (GtkSourceCompletionModel *model)
-{
-	if (model->priv->item_queue != NULL)
-	{
-		g_queue_foreach (model->priv->item_queue,
-				 (GFunc)free_node, NULL);
-		g_queue_clear (model->priv->item_queue);
-	}
-
-	if (model->priv->idle_id != 0)
-	{
-		g_source_remove (model->priv->idle_id);
-		model->priv->idle_id = 0;
-	}	
-}
-
-static void
-cancel_refilter (GtkSourceCompletionModel *model)
-{
-	if (model->priv->next_filter_path != NULL)
-	{
-		gtk_tree_row_reference_free (model->priv->next_filter_path);
-		model->priv->next_filter_path = NULL;
-	}
-	
-	if (model->priv->idle_filter_id != 0)
-	{
-		g_source_remove (model->priv->idle_filter_id);
-		model->priv->idle_filter_id = 0;
-		
-		g_signal_emit (model, signals[FILTER_DONE], 0);
-	}
+	g_slice_free (ProposalNode, node);
 }
 
 static void
@@ -490,31 +467,31 @@ gtk_source_completion_model_dispose (GObject *object)
 {
 	GtkSourceCompletionModel *model = GTK_SOURCE_COMPLETION_MODEL (object);
 
-	cancel_append (model);
-	cancel_refilter (model);
-	
-	if (model->priv->item_queue != NULL)
+	if (model->priv->providers_info != NULL)
 	{
-		g_queue_free (model->priv->item_queue);
-		model->priv->item_queue = NULL;
+		g_hash_table_destroy (model->priv->providers_info);
+		model->priv->providers_info = NULL;
 	}
 	
-	if (model->priv->num_per_provider != NULL)
-	{
-		g_hash_table_destroy (model->priv->num_per_provider);
-		model->priv->num_per_provider = NULL;
-	}
-	
-	g_list_foreach (model->priv->store, (GFunc)free_node, NULL);
+	g_list_foreach (model->priv->store, (GFunc)proposal_node_free, NULL);
+
 	g_list_free (model->priv->store);
 	model->priv->store = NULL;
-
+	model->priv->last = NULL;
+	
+	g_list_free (model->priv->providers);
+	model->priv->providers = NULL;
+	
 	G_OBJECT_CLASS (gtk_source_completion_model_parent_class)->dispose (object);
 }
 
 static void
 gtk_source_completion_model_finalize (GObject *object)
 {
+	GtkSourceCompletionModel *model = GTK_SOURCE_COMPLETION_MODEL (object);
+
+	g_list_free (model->priv->visible_providers);
+
 	G_OBJECT_CLASS (gtk_source_completion_model_parent_class)->finalize (object);
 }
 
@@ -527,35 +504,15 @@ gtk_source_completion_model_class_init (GtkSourceCompletionModelClass *klass)
 	object_class->dispose = gtk_source_completion_model_dispose;
 
 	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionModelPrivate));
-	
-	signals[ITEMS_ADDED] =
-		g_signal_new ("items-added",
-			      G_TYPE_FROM_CLASS (klass),
-			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-			      G_STRUCT_OFFSET (GtkSourceCompletionModelClass, items_added),
-			      NULL, 
-			      NULL,
-			      g_cclosure_marshal_VOID__VOID, 
-			      G_TYPE_NONE,
-			      0);
-
-	signals[FILTER_DONE] =
-		g_signal_new ("filter-done",
-			      G_TYPE_FROM_CLASS (klass),
-			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-			      G_STRUCT_OFFSET (GtkSourceCompletionModelClass, filter_done),
-			      NULL, 
-			      NULL,
-			      g_cclosure_marshal_VOID__VOID, 
-			      G_TYPE_NONE,
-			      0);
-
 }
 
 static void
-free_num (gpointer data)
+provider_info_free (gpointer data)
 {
-	g_slice_free (HeaderInfo, data);
+	ProviderInfo *info = (ProviderInfo *)data;
+	g_hash_table_destroy (info->proposals);
+
+	g_slice_free (ProviderInfo, data);
 }
 
 static void
@@ -569,512 +526,671 @@ gtk_source_completion_model_init (GtkSourceCompletionModel *self)
 	self->priv->column_types[GTK_SOURCE_COMPLETION_MODEL_COLUMN_MARKUP] = G_TYPE_STRING;
 	self->priv->column_types[GTK_SOURCE_COMPLETION_MODEL_COLUMN_ICON] = GDK_TYPE_PIXBUF;
 	
-	self->priv->num_per_provider = g_hash_table_new_full (g_direct_hash,
-	                                                      g_direct_equal,
-	                                                      NULL,
-	                                                      free_num);
-	
-	self->priv->idle_id = 0;
-	self->priv->item_queue = g_queue_new ();
-	g_queue_init (self->priv->item_queue);
+	self->priv->providers_info = g_hash_table_new_full (g_direct_hash,
+	                                                    g_direct_equal,
+	                                                    g_object_unref,
+	                                                    provider_info_free);
 }
 
 static void
-num_inc (GtkSourceCompletionModel           *model,
-         GtkSourceCompletionProvider        *provider,
-         gboolean                            inc_local,
-         gboolean                            inc_global)
+num_inc (GtkSourceCompletionModel *model,
+         ProviderInfo             *info,
+         ProposalNode             *node)
 {
-	HeaderInfo *info;
-	
-	info = g_hash_table_lookup (model->priv->num_per_provider, provider);
-	
-	if (inc_global)
+	if (!node->filtered)
 	{
 		++model->priv->num;
-		
-		if (info != NULL)
-		{
-			++info->visible_items;
-		}
 	}
-	
-	if (inc_local && info != NULL)
+
+	if (node->proposal != NULL)
 	{
-		++(info->num);
+		++info->num_proposals;
 	}
 }
 
 static void
-num_dec (GtkSourceCompletionModel           *model,
-         GtkSourceCompletionProvider        *provider,
-         gboolean                            dec_local,
-         gboolean                            dec_global)
+num_dec (GtkSourceCompletionModel *model,
+         ProviderInfo             *info,
+         ProposalNode             *node)
 {
-	HeaderInfo *info;
-	
-	info = g_hash_table_lookup (model->priv->num_per_provider, provider);
-	
-	if (dec_global)
+	if (!node->filtered)
 	{
 		--model->priv->num;
-		
-		if (info != NULL)
-		{
-			--info->visible_items;
-		}
 	}
 
-	if (dec_local && info != NULL && info->num > 0)
+	if (node->proposal != NULL && info->num_proposals > 0)
 	{
-		--(info->num);
+		--info->num_proposals;
 	}
 }
 
-static GtkSourceCompletionModelFilterFlag
-node_update_filter_state (GtkSourceCompletionModel *model, 
-                          ProposalNode             *node)
+/* Public */
+GtkSourceCompletionModel*
+gtk_source_completion_model_new (void)
 {
-	GtkSourceCompletionModelFilterFlag ret;
-	
-	if (node->proposal == NULL)
+	return g_object_new (GTK_TYPE_SOURCE_COMPLETION_MODEL, NULL);
+}
+
+static void
+on_proposal_changed (GtkSourceCompletionProposal *proposal,
+                     GList                       *item)
+{
+	GtkTreeIter iter;
+	ProposalNode *node = (ProposalNode *)item->data;
+	GtkTreePath *path;
+
+	if (!node->filtered)
 	{
-		return node->filtered;
+		iter.user_data = node;
+		path = path_from_list (node->model, item);
+
+		gtk_tree_model_row_changed (GTK_TREE_MODEL (node->model),
+		                            path,
+		                            &iter);
+		gtk_tree_path_free (path);
 	}
-	
-	ret = node->filtered;
-	
-	node->filtered = model->priv->filter (model, 
-	                                      node->provider, 
-	                                      node->proposal, 
-	                                      model->priv->filter_data);
+}
 
-	return ret;
+void
+gtk_source_completion_model_begin (GtkSourceCompletionModel *model,
+                                   GList                    *providers)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model));
+
+	if (providers != NULL)
+	{
+		model->priv->marking = !model->priv->marking;
+		
+		/* FIXME: maybe remove providers that are no longer selected now, but
+		   since this is probably a atypical case, it might be a performance
+		   hit (since providers have to be looked up in the GList). Anyway,
+		   the providers are correctly removed by the marking process */
+	}
+	else
+	{
+		gtk_source_completion_model_clear (model);
+	}
 }
 
-static void
-update_show_headers (GtkSourceCompletionModel *model,
-                     gboolean                  show)
+void
+gtk_source_completion_model_cancel (GtkSourceCompletionModel *model)
 {
-	GtkSourceCompletionProvider *provider;
-	HeaderInfo *info;
-	guint num = 0;
-	GList *items = NULL;
 	GList *item;
-	GHashTableIter hiter;
 	
-	ProposalNode *node;
-	GtkTreePath *path;
+	/* If cancelled, mark all proposals correctly so that the fast marking
+	   scheme still works */
+	for (item = model->priv->store; item != NULL; item = g_list_next (item))
+	{
+		((ProposalNode *)item->data)->mark = model->priv->marking;
+	}
+}
+
+static void
+handle_row_inserted (GtkSourceCompletionModel  *model,
+                     GList                     *item,
+                     GtkTreePath              **path)
+{
 	GtkTreeIter iter;
+	GtkTreePath *ppath = NULL;
+	GtkTreeRowReference *ref = NULL;
 	
-	if (!model->priv->show_headers)
+	if (path != NULL)
 	{
-		return;
+		ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), *path);
+	}
+	else
+	{
+		ppath = path_from_list (model, item);
 	}
 	
-	/* Check headers */
-	g_hash_table_iter_init (&hiter, model->priv->num_per_provider);
+	iter.user_data = item;
+
+	gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
+	                             path ? *path : ppath,
+	                             &iter);
+
+	if (ref != NULL)
+	{
+		gtk_tree_path_free (*path);
+		*path = gtk_tree_row_reference_get_path (ref);
+		gtk_tree_row_reference_free (ref);
+		
+		gtk_tree_path_prev (*path);
+	}
+	else
+	{
+		gtk_tree_path_free (ppath);
+	}
+}
+
+static void
+insert_node (GtkSourceCompletionModel     *model,
+             ProviderInfo                 *info,
+             GList                        *position,
+             GtkSourceCompletionProposal  *proposal,
+             GtkTreePath                 **path)
+{
+	ProposalNode *node = g_slice_new (ProposalNode);
+	GList *item;
+
+	node->model = model;
+	node->proposal = proposal ? g_object_ref (proposal) : NULL;
+	node->provider = info->provider;
+	node->changed_id = 0;
+	node->mark = model->priv->marking;
+	node->filtered = info->filtered;
 	
-	while (g_hash_table_iter_next (&hiter, (gpointer *)&provider, (gpointer *)&info))
+	if (position == NULL)
 	{
-		if (info->visible_items > 0)
+		/* Append after last item */
+		if (model->priv->store == NULL)
 		{
-			node = (ProposalNode *)info->item->data;
-			++num;
-			
-			if (show && node->filtered)
-			{
-				items = g_list_append (items, info);
-			}
-			
-			if (!show && !node->filtered)
-			{
-				items = g_list_append (items, info);
-			}
+			model->priv->store = model->priv->last = g_list_append (NULL,
+			                                                        node);
+		}
+		else
+		{
+			model->priv->last = g_list_append (model->priv->last, node);
+			model->priv->last = g_list_next (model->priv->last);
 		}
+		
+		info->last = model->priv->last;
+		
+		if (info->first == NULL)
+		{
+			info->first = info->last;
+		}
+		
+		item = model->priv->last;
 	}
-
-	if (show && num > 1 && items != NULL)
+	else
 	{
-		for (item = items; item; item = g_list_next (item))
+		/* Insert before item 'position' */
+		model->priv->store = g_list_insert_before (model->priv->store,
+		                                           position,
+		                                           node);
+		
+		item = g_list_previous (position);
+		
+		if (info->first == position)
 		{
-			info = (HeaderInfo *)item->data;
-			node = (ProposalNode *)info->item->data;
-			
-			node->filtered = GTK_SOURCE_COMPLETION_MODEL_NONE;
-			iter.user_data = info->item;
-			
-			num_inc (model, node->provider, FALSE, TRUE);
-
-			path = path_from_list (model, info->item);
-			gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
-			                             path,
-			                             &iter);
-			gtk_tree_path_free (path);
+			info->first = item;
+		}
+		
+		if (info->last->next == item)
+		{
+			info->last = item;
 		}
 	}
 	
-	if (!show && num <= 1 && items)
+	num_inc (model, info, node);
+	
+	if (proposal != NULL)
 	{
-		info = (HeaderInfo *)items->data;
-		node = (ProposalNode *)info->item->data;
-		
-		num_dec (model, node->provider, FALSE, TRUE);
+		g_hash_table_insert (info->proposals, proposal, node);
+	}
 
-		node->filtered = GTK_SOURCE_COMPLETION_MODEL_FILTERED;
-		path = path_from_list (model, info->item);
-		gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
-		gtk_tree_path_free (path);
+	if (!node->filtered)
+	{
+		handle_row_inserted (model, item, path);
+	}
+	
+	if (proposal != NULL)
+	{
+		node->changed_id = g_signal_connect (node->proposal, 
+	                                         "changed", 
+	                                         G_CALLBACK (on_proposal_changed),
+	                                         item);
 	}
+}
 
-	g_list_free (items);
+static void
+insert_header (GtkSourceCompletionModel    *model,
+               ProviderInfo                *info)
+{
+	if (info == NULL)
+	{
+		return;
+	}
+	
+	if (info->first != NULL)
+	{
+		/* Insert header just before 'first' */
+		insert_node (model, info, info->first, NULL, NULL);
+	}
+	else
+	{
+		/* Just append the header after the last current item */
+		insert_node (model, info, g_list_next (model->priv->last), NULL, NULL);
+
+		info->first = model->priv->last;
+		info->last = model->priv->last;
+	}
 }
 
 static void
-refilter_headers (GtkSourceCompletionModel *model)
+handle_row_deleted (GtkSourceCompletionModel  *model,
+                    GList                     *item,
+                    GtkTreePath              **path)
 {
-	GtkSourceCompletionProvider *provider;
-	HeaderInfo *info;
-	GHashTableIter hiter;
-	ProposalNode *node;
-	GtkTreePath *path;
+	GtkTreePath *ppath = NULL;
 
-	g_hash_table_iter_init (&hiter, model->priv->num_per_provider);
+	if (path == NULL)
+	{
+		ppath = path_from_list (model, item);
+	}
+	else
+	{
+		/* Create a copy here because row_deleted might modify it */
+		ppath = gtk_tree_path_copy (*path);
+	}
 	
-	while (g_hash_table_iter_next (&hiter, (gpointer *)&provider, (gpointer *)&info))
+	gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), ppath);
+	gtk_tree_path_free (ppath);
+}
+
+static void
+remove_node (GtkSourceCompletionModel  *model,
+             ProviderInfo              *info,
+             GList                     *item,
+             GtkTreePath              **path)
+{
+	ProposalNode *node = (ProposalNode *)item->data;
+	GtkTreePath *ppath = NULL;
+
+	if (item == info->first)
 	{
-		node = (ProposalNode *)info->item->data;
-		
-		if (!node->filtered)
+		if (info->first != info->last)
 		{
-			node->filtered = GTK_SOURCE_COMPLETION_MODEL_FILTERED;
-			num_dec (model, provider, FALSE, TRUE);
-			
-			path = path_from_list (model, info->item);
-			gtk_tree_model_row_deleted (GTK_TREE_MODEL (model),
-			                            path);
-			gtk_tree_path_free (path);
+			info->first = g_list_next (info->first);
 		}
+		else
+		{
+			info->first = info->last = NULL;
+		}
+	}
+	else if (item == info->last)
+	{
+		info->last = g_list_previous (info->last);
 	}
+	
+	if (item == model->priv->last)
+	{
+		model->priv->last = g_list_previous (item);
+	}
+	
+	num_dec (model, info, node);
 
-	if (model->priv->show_headers)
+	if (path == NULL)
 	{
-		update_show_headers (model, TRUE);
-		return;
+		ppath = path_from_list (model, item);
 	}
+	
+	model->priv->store = g_list_delete_link (model->priv->store,
+	                                         item);	
+	
+	if (node->proposal != NULL)
+	{
+		g_hash_table_remove (info->proposals, node->proposal);
+	}
+	
+	handle_row_deleted (model, item, path ? path : &ppath);	
+	
+	if (ppath != NULL)
+	{
+		gtk_tree_path_free (ppath);
+	}
+
+	proposal_node_free (node);	
 }
 
-/* Public */
-GtkSourceCompletionModel*
-gtk_source_completion_model_new (GtkSourceCompletionModelVisibleFunc func,
-                                 gpointer                            userdata)
+static void
+update_header_visibility_each (GtkSourceCompletionProvider *provider,
+                               ProviderInfo                *info,
+                               GtkSourceCompletionModel    *model)
 {
-	GtkSourceCompletionModel *model = g_object_new (GTK_TYPE_SOURCE_COMPLETION_MODEL, NULL);
-	
-	model->priv->filter = func;
-	model->priv->filter_data = userdata;
+	if (model->priv->show_headers)
+	{
+		insert_header (model, info);
+	}
+	else
+	{
+		remove_node (model, info, info->first, NULL);
+	}
+}
 
-	return model;
+static void
+update_header_visibility (GtkSourceCompletionModel *model)
+{
+	g_hash_table_foreach (model->priv->providers_info,
+	                      (GHFunc)update_header_visibility_each,
+	                      model);
 }
 
 static void
-append_list (GtkSourceCompletionModel *model,
-             ProposalNode             *node)
+update_provider_visibility_show_hide (GtkSourceCompletionModel *model,
+                                      ProviderInfo             *info,
+                                      gboolean                  show)
 {
 	GList *item;
+	GtkTreePath *path = NULL;
 	
-	item = g_list_append (model->priv->last, node);
+	item = info->first;
+	info->filtered = !show;
 	
-	if (model->priv->store == NULL)
+	while (item)
 	{
-		model->priv->store = item;
+		ProposalNode *node = (ProposalNode *)item->data;
+		
+		if (node->proposal != NULL || model->priv->show_headers)
+		{
+			node->filtered = !show;
+
+			if (path == NULL)
+			{
+				path = path_from_list (model, item);
+			}
+
+			if (show)
+			{
+				++model->priv->num;
+
+				handle_row_inserted (model, item, &path);
+				gtk_tree_path_next (path);
+			}
+			else
+			{
+				--model->priv->num;
+				handle_row_deleted (model, item, &path);
+			}
+		}
+
+		if (item == info->last)
+		{
+			break;
+		}
+
+		item = g_list_next (item);
 	}
-	else
+	
+	if (path != NULL)
 	{
-		item = item->next;
+		gtk_tree_path_free (path);
 	}
-	
-	model->priv->last = item;
 }
 
 static void
-on_proposal_changed (GtkSourceCompletionProposal *proposal,
-                     GList                       *item)
+update_provider_visibility_each (GtkSourceCompletionProvider *provider,
+                                 ProviderInfo                *info,
+                                 GtkSourceCompletionModel    *model)
 {
-	GtkTreeIter iter;
-	ProposalNode *node = (ProposalNode *)item->data;
-	GtkTreePath *path;
-
-	if (!node->filtered)
+	if (info->filtered == (model->priv->visible_providers != NULL && 
+	                       g_list_index (model->priv->visible_providers, info->provider) == -1))
 	{
-		iter.user_data = node;
-		path = path_from_list (node->model, item);
-
-		gtk_tree_model_row_changed (GTK_TREE_MODEL (node->model),
-		                            path,
-		                            &iter);
-		gtk_tree_path_free (path);
+		return;
 	}
+
+	update_provider_visibility_show_hide (model, info, info->filtered);
+}
+
+static void
+update_provider_visibility (GtkSourceCompletionModel *model)
+{
+	g_hash_table_foreach (model->priv->providers_info,
+	                      (GHFunc)update_provider_visibility_each,
+	                      model);
 }
 
 static gboolean
-idle_append (gpointer data)
+remove_unmarked (GtkSourceCompletionModel    *model,
+                 GtkSourceCompletionProvider *provider)
 {
-	GtkSourceCompletionModel *model = GTK_SOURCE_COMPLETION_MODEL (data);
-	HeaderInfo *info;
-	GtkTreePath *path;
 	GList *item;
-	gint i = 0;
+	GtkTreePath *path = NULL;
+	ProviderInfo *info = g_hash_table_lookup (model->priv->providers_info, 
+	                                          provider);
+
+	if (!info)
+	{
+		return FALSE;
+	}
 	
-	while (i < ITEMS_PER_CALLBACK)
+	item = info->first;
+
+	while (item)
 	{
-		ProposalNode *node = (ProposalNode *)g_queue_pop_head (model->priv->item_queue);
-		ProposalNode *header = NULL;
-		GtkTreeIter iter;
+		ProposalNode *node = (ProposalNode *)item->data;
 		
-		if (node == NULL)
+		if (node->provider != provider)
 		{
-			/* If we are here we added all elements of the queue */
-			g_signal_emit (model, signals[ITEMS_ADDED], 0);
-			
-			model->priv->idle_id = 0;
-			
-			return FALSE;
+			break;
 		}
 		
-		/* Check if it is a header */
-		if (g_hash_table_lookup (model->priv->num_per_provider, node->provider) == NULL)
+		if (path == NULL)
 		{
-			header = g_slice_new (ProposalNode);
-			header->provider = g_object_ref (node->provider);
-			header->proposal = NULL;
-			header->filtered = GTK_SOURCE_COMPLETION_MODEL_FILTERED;
-			
-			append_list (model, header);
-			
-			info = g_slice_new (HeaderInfo);
-			info->item = model->priv->last;
-			info->num = 0;
-			info->visible_items = 0;
-			
-			g_hash_table_insert (model->priv->num_per_provider, node->provider, info);
+			path = path_from_list (model, item);
 		}
-		
-		node_update_filter_state (model, node);
-		
-		append_list (model, node);
-		
-		item = model->priv->last;
-		iter.user_data = item;
-
-		num_inc (model, 
-			 node->provider, 
-			 !node->filtered || (node->filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT),
-			 !node->filtered);
-
-		if (!node->filtered)
+	
+		if (node->proposal != NULL && node->mark != model->priv->marking)
 		{
-			path = path_from_list (model, item);
-			gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
-			gtk_tree_path_free (path);
+			GList *next = g_list_next (item);
 			
-			if (header != NULL)
-			{
-				update_show_headers (model, TRUE);
-			}
+			// Remove the node here
+			remove_node (model, info, item, &path);
+			item = next;
+		}
+		else
+		{
+			gtk_tree_path_next (path);
+			item = g_list_next (item);
 		}
+	}
+	
+	if (path != NULL)
+	{
+		gtk_tree_path_free (path);
+	}
+	
+	if (info->num_proposals == 0 && info->first != NULL && model->priv->show_headers)
+	{
+		remove_node (model, info, info->first, NULL);
+	}
+	
+	if (info->num_proposals == 0)
+	{
+		g_hash_table_remove (model->priv->providers_info, provider);
 		
-		node->changed_id = g_signal_connect (node->proposal, 
-	                                             "changed", 
-	                                             G_CALLBACK (on_proposal_changed),
-	                                             item);
+		model->priv->providers = g_list_remove (model->priv->providers,
+		                                        provider);
+
+		model->priv->visible_providers = g_list_remove (model->priv->visible_providers,
+		                                                provider);
 		
-		i++;
+		return FALSE;
 	}
 	
 	return TRUE;
 }
 
-void
-gtk_source_completion_model_run_add_proposals (GtkSourceCompletionModel *model)
+static ProviderInfo *
+add_provider_info (GtkSourceCompletionModel    *model,
+                   GtkSourceCompletionProvider *provider)
 {
-	if (idle_append (model))
+	ProviderInfo *info;
+	
+	info = g_slice_new0 (ProviderInfo);
+	info->provider = provider;
+	info->proposals = g_hash_table_new ((GHashFunc)gtk_source_completion_proposal_hash, 
+	                                    (GEqualFunc)gtk_source_completion_proposal_equal);
+
+	info->filtered = !provider_is_visible (model, provider);
+
+	g_hash_table_insert (model->priv->providers_info, 
+		             g_object_ref (provider),
+		             info);
+	
+	if (model->priv->show_headers && !info->filtered)
 	{
-		model->priv->idle_id =
-			g_idle_add ((GSourceFunc)idle_append,
-				    model);
+		insert_header (model, info);
 	}
+	else
+	{
+		info->first = info->last = NULL;
+	}
+	
+	model->priv->providers = g_list_append (model->priv->providers,
+		                                    provider);
+
+	return info;
 }
 
 void
 gtk_source_completion_model_append (GtkSourceCompletionModel    *model,
                                     GtkSourceCompletionProvider *provider,
-                                    GtkSourceCompletionProposal *proposal)
+                                    GList                       *proposals)
 {
-	ProposalNode *node;
+	GList *item;
+	ProviderInfo *info;
+	GtkTreePath *path = NULL;
 	
 	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model));
 	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider));
-	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_PROPOSAL (proposal));
 	
+	if (proposals == NULL || !GTK_IS_SOURCE_COMPLETION_PROPOSAL (proposals->data))
+	{
+		return;
+	}
+	
+	info = g_hash_table_lookup (model->priv->providers_info, provider);
+	
+	if (!info)
+	{
+		/* First batch for 'provider', add provider info */
+		info = add_provider_info (model, provider);
+	}
+	
+	for (item = proposals; item != NULL; item = g_list_next (item))
+	{
+		GtkSourceCompletionProposal *proposal;
+		ProposalNode *node;
+		
+		if (!GTK_IS_SOURCE_COMPLETION_PROPOSAL (item->data))
+		{
+			continue;
+		}
+		
+		proposal = GTK_SOURCE_COMPLETION_PROPOSAL (item->data);
+		node = g_hash_table_lookup (info->proposals, proposal);
 
-	node = g_slice_new (ProposalNode);
-	node->provider = g_object_ref (provider);
-	node->proposal = g_object_ref (proposal);
-	node->changed_id = 0;
+		if (node)
+		{
+			node->mark = model->priv->marking;
+			
+			if (path != NULL && ((ProposalNode *)info->last->data) == node)
+			{
+				gtk_tree_path_next (path);
+			}
+		}
+		else
+		{
+			GList *insert_before = g_list_next (info->last);
+			
+			/* Insert proposal into model, after last item of provider */
+			if (path == NULL)
+			{
+				if (insert_before)
+				{
+					path = path_from_list (model, insert_before);
+				}
+				else
+				{
+					path = gtk_tree_path_new_from_indices (model->priv->num, -1);
+				}
+			}
+			
+			insert_node (model, info, insert_before, proposal, &path);
+			gtk_tree_path_next (path);
+		}
+	}
 	
-	g_queue_push_tail (model->priv->item_queue, node);
+	if (path != NULL)
+	{
+		gtk_tree_path_free (path);
+	}
+}
+
+void
+gtk_source_completion_model_end (GtkSourceCompletionModel    *model,
+                                 GtkSourceCompletionProvider *provider)
+{
+	/* Remove unmarked proposals, returns TRUE if there are any proposals
+	 * left for 'provider'. If so, we add 'provider' to the list of 
+	 * currently active providers
+	 */
+	if (!remove_unmarked (model, provider))
+	{
+		model->priv->providers = g_list_remove (model->priv->providers,
+		                                        provider);
+	}
 }
 
 void
 gtk_source_completion_model_clear (GtkSourceCompletionModel *model)
 {
 	GtkTreePath *path;
-	ProposalNode *node;
-	GList *list;
-	GtkSourceCompletionModelFilterFlag filtered;
+	ProviderInfo *info = NULL;
 	
 	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model));
 	
-	/* Clear the queue of missing elements to append */
-	cancel_append (model);
-	cancel_refilter (model);
-	
 	path = gtk_tree_path_new_first ();
-	list = model->priv->store;
 	
 	while (model->priv->store)
 	{
+		ProposalNode *node;
+
 		node = (ProposalNode *)model->priv->store->data;
-		filtered = node->filtered;
 
-		free_node (node);
-		
-		model->priv->store = model->priv->store->next;
+		model->priv->store = g_list_delete_link (model->priv->store, model->priv->store);
 		
 		if (model->priv->store == NULL)
 		{
 			model->priv->last = NULL;
 		}
 		
-		num_dec (model, 
-		         node->provider, 
-		         (!filtered || (filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT)) && node->proposal != NULL,
-		         !filtered);
-		
-		if (!filtered)
+		if (info == NULL || info->provider != node->provider)
 		{
-			gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+			info = g_hash_table_lookup (model->priv->providers_info, node->provider);
 		}
-	}
-	
-	g_list_free (list);
-	gtk_tree_path_free (path);
-	
-	g_hash_table_remove_all (model->priv->num_per_provider);
-}
 
-static gboolean
-idle_refilter (GtkSourceCompletionModel *model)
-{
-	guint i = 0;
-	GtkTreePath *path;
-	GtkTreeIter iter;
-	ProposalNode *node;
-	GtkSourceCompletionModelFilterFlag filtered;
-
-	if (model->priv->next_filter_path)
-	{
-		path = gtk_tree_row_reference_get_path (model->priv->next_filter_path);
-		gtk_tree_row_reference_free (model->priv->next_filter_path);
-		model->priv->next_filter_path = NULL;
-	}
-	else
-	{
-		path = gtk_tree_path_new_first ();
-	}
-	
-	while (i < FILTER_PER_CALLBACK && model->priv->next_filter_item != NULL)
-	{
-		iter.user_data = model->priv->next_filter_item;
-
-		node = (ProposalNode *)model->priv->next_filter_item->data;
-		filtered = node_update_filter_state (model, node);
+		num_dec (model, info, node);
 		
-		if ((filtered != 0) == (node->filtered != 0))
-		{
-			/* Keep the same, so increase path */
-			if (!filtered)
-			{
-				gtk_tree_path_next (path);
-			}
-		}
-		else if (filtered)
-		{
-			/* Was filtered, but not any more, so insert it */
-			num_inc (model, 
-			         node->provider,
-			         !(filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT),
-			         TRUE);
-			         
-			gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
-			                             path,
-			                             &iter);
-			gtk_tree_path_next (path);
-		}
-		else
+		if (!node->filtered)
 		{
-			/* Was not filtered, but is now, so remove it */
-			num_dec (model, 
-			         node->provider,
-			         !(node->filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT),
-			         TRUE);
-
-			gtk_tree_model_row_deleted (GTK_TREE_MODEL (model),
-			                            path);
+			gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
 		}
-		
-		model->priv->next_filter_item = g_list_next (model->priv->next_filter_item);
-		++i;
-	}
 
-	refilter_headers (model);
-
-	if (model->priv->next_filter_item == NULL)
-	{
-		model->priv->idle_filter_id = 0;
-		gtk_tree_path_free (path);
-		
-		g_signal_emit (model, signals[FILTER_DONE], 0);
-
-		return FALSE;
+		proposal_node_free (node);
 	}
+
+	gtk_tree_path_free (path);
 	
-	if (gtk_tree_path_prev (path))
-	{
-		model->priv->next_filter_path = gtk_tree_row_reference_new (GTK_TREE_MODEL (model),
-		                                                            path);
-	}
+	g_hash_table_remove_all (model->priv->providers_info);
+	g_list_free (model->priv->providers);
 	
-	gtk_tree_path_free (path);
+	model->priv->providers = NULL;
 
-	return TRUE;
+	g_list_free (model->priv->visible_providers);
+	model->priv->visible_providers = NULL;
 }
 
-void
-gtk_source_completion_model_refilter (GtkSourceCompletionModel *model)
+static void
+provider_has_proposals (GtkSourceCompletionProvider *provider,
+                        ProviderInfo                *info,
+                        gboolean                    *isempty)
 {
-	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model));
-	
-	/* Cancel any running filter */
-	cancel_refilter (model);
-	
-	model->priv->next_filter_item = model->priv->store;
-	
-	if (idle_refilter (model))
+	if (info->num_proposals != 0)
 	{
-		model->priv->idle_filter_id = g_idle_add ((GSourceFunc)idle_refilter, 
-		                                          model);
+		*isempty = FALSE;
 	}
 }
 
@@ -1082,29 +1198,34 @@ gboolean
 gtk_source_completion_model_is_empty (GtkSourceCompletionModel *model,
                                       gboolean                  invisible)
 {
-	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), FALSE);
+	gboolean isempty = TRUE;
 	
-	if (invisible)
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), FALSE);
+
+	if (!invisible)
 	{
-		return model->priv->num == 0;
+		g_hash_table_foreach (model->priv->providers_info,
+		                      (GHFunc)provider_has_proposals,
+		                      &isempty);
 	}
-	else
+	else if (model->priv->num != 0)
 	{
-		return model->priv->num == 0 || 
-		       gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL) == 0;
+		isempty = FALSE;
 	}
+
+	return isempty;
 }
 
 guint
 gtk_source_completion_model_n_proposals (GtkSourceCompletionModel    *model,
                                          GtkSourceCompletionProvider *provider)
 {
-	HeaderInfo *info;
+	ProviderInfo *info;
 	
 	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), 0);
 	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider), 0);
 	
-	info = g_hash_table_lookup (model->priv->num_per_provider, provider);
+	info = g_hash_table_lookup (model->priv->providers_info, provider);
 	
 	if (info == NULL)
 	{
@@ -1112,7 +1233,7 @@ gtk_source_completion_model_n_proposals (GtkSourceCompletionModel    *model,
 	}
 	else
 	{
-		return info->num;
+		return info->num_proposals;
 	}
 }
 
@@ -1125,7 +1246,7 @@ gtk_source_completion_model_set_show_headers (GtkSourceCompletionModel *model,
 	if (model->priv->show_headers != show_headers)
 	{
 		model->priv->show_headers = show_headers;
-		refilter_headers (model);
+		update_header_visibility (model);
 	}
 }
 
@@ -1156,7 +1277,6 @@ gtk_source_completion_model_iter_previous (GtkSourceCompletionModel *model,
 	{
 		item = g_list_previous (item);
 	} while (item && ((ProposalNode *)item->data)->filtered);
-
 	
 	if (item != NULL)
 	{
@@ -1194,3 +1314,32 @@ gtk_source_completion_model_iter_last (GtkSourceCompletionModel *model,
 		return FALSE;
 	}
 }
+
+GList *
+gtk_source_completion_model_get_providers (GtkSourceCompletionModel *model)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), NULL);
+	
+	return model->priv->providers;
+}
+
+void 
+gtk_source_completion_model_set_visible_providers (GtkSourceCompletionModel *model,
+                                                   GList                    *providers)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model));
+	
+	g_list_free (model->priv->visible_providers);
+	model->priv->visible_providers = g_list_copy (providers);
+	
+	update_provider_visibility (model);
+}
+
+GList *
+gtk_source_completion_model_get_visible_providers (GtkSourceCompletionModel *model)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), NULL);
+	
+	return model->priv->visible_providers;
+}
+
diff --git a/gtksourceview/gtksourcecompletionmodel.h b/gtksourceview/gtksourcecompletionmodel.h
index bb112ae..546fb51 100644
--- a/gtksourceview/gtksourcecompletionmodel.h
+++ b/gtksourceview/gtksourcecompletionmodel.h
@@ -49,18 +49,8 @@ struct _GtkSourceCompletionModel {
 
 struct _GtkSourceCompletionModelClass {
 	GObjectClass parent_class;
-	
-	void	(*items_added)		(GtkSourceCompletionModel *model);
-	void	(*filter_done)		(GtkSourceCompletionModel *model);
 };
 
-typedef enum
-{
-	GTK_SOURCE_COMPLETION_MODEL_NONE,
-	GTK_SOURCE_COMPLETION_MODEL_FILTERED = 1 << 0,
-	GTK_SOURCE_COMPLETION_MODEL_COUNT = 1 << 1
-} GtkSourceCompletionModelFilterFlag;
-
 enum
 {
 	GTK_SOURCE_COMPLETION_MODEL_COLUMN_LABEL,
@@ -71,33 +61,34 @@ enum
 	GTK_SOURCE_COMPLETION_MODEL_N_COLUMNS
 };
 
-typedef GtkSourceCompletionModelFilterFlag (* GtkSourceCompletionModelVisibleFunc) (GtkSourceCompletionModel    *model,
-                                                          GtkSourceCompletionProvider *provider,
-                                                          GtkSourceCompletionProposal *proposal,
-                                                          gpointer                     userdata);
-
 GType gtk_source_completion_model_get_type (void) G_GNUC_CONST;
 
 GtkSourceCompletionModel *
-		gtk_source_completion_model_new 	(GtkSourceCompletionModelVisibleFunc func,
-							 gpointer                            userdata);
-
-void		gtk_source_completion_model_run_add_proposals (GtkSourceCompletionModel *model);
+		gtk_source_completion_model_new 	(void);
 
+void		gtk_source_completion_model_begin	(GtkSourceCompletionModel           *model,
+                                                         GList                              *providers);
 void		gtk_source_completion_model_append 	(GtkSourceCompletionModel           *model,
 							 GtkSourceCompletionProvider        *provider,
-							 GtkSourceCompletionProposal        *proposal);
+							 GList                              *proposals);
+void		gtk_source_completion_model_end		(GtkSourceCompletionModel           *model,
+							 GtkSourceCompletionProvider        *provider);
+void		gtk_source_completion_model_cancel	(GtkSourceCompletionModel           *model);
 
 gboolean	gtk_source_completion_model_is_empty 	(GtkSourceCompletionModel           *model,
                                                          gboolean                            invisible);
 
+void            gtk_source_completion_model_set_visible_providers (GtkSourceCompletionModel *model,
+                                                                   GList                    *providers);
+GList          *gtk_source_completion_model_get_visible_providers (GtkSourceCompletionModel *model);
+
+
+GList          *gtk_source_completion_model_get_providers (GtkSourceCompletionModel         *model);
 guint		gtk_source_completion_model_n_proposals (GtkSourceCompletionModel           *model,
                                                          GtkSourceCompletionProvider        *provider);
 
 void 		gtk_source_completion_model_clear 	(GtkSourceCompletionModel           *model);
 
-void		gtk_source_completion_model_refilter	(GtkSourceCompletionModel           *model);
-
 void 		gtk_source_completion_model_set_show_headers (GtkSourceCompletionModel      *model,
 							      gboolean                       show_headers);
 
@@ -114,3 +105,4 @@ G_END_DECLS
 
 #endif /* __GTK_SOURCE_COMPLETION_MODEL_H__ */
 
+/* vi:ex:ts=8 */
diff --git a/gtksourceview/gtksourcecompletionproposal.c b/gtksourceview/gtksourcecompletionproposal.c
index fd1352e..5cdb12f 100644
--- a/gtksourceview/gtksourcecompletionproposal.c
+++ b/gtksourceview/gtksourcecompletionproposal.c
@@ -74,6 +74,19 @@ gtk_source_completion_proposal_get_info_default (GtkSourceCompletionProposal *pr
 	return NULL;
 }
 
+static guint
+gtk_source_completion_proposal_hash_default	(GtkSourceCompletionProposal *proposal)
+{
+	return g_direct_hash (proposal);
+}
+
+static gboolean
+gtk_source_completion_proposal_equal_default (GtkSourceCompletionProposal *proposal,
+                                              GtkSourceCompletionProposal *other)
+{
+	return g_direct_equal (proposal, other);
+}
+
 static void 
 gtk_source_completion_proposal_init (GtkSourceCompletionProposalIface *iface)
 {
@@ -85,6 +98,8 @@ gtk_source_completion_proposal_init (GtkSourceCompletionProposalIface *iface)
 	
 	iface->get_icon = gtk_source_completion_proposal_get_icon_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)
 	{
@@ -136,6 +151,9 @@ gtk_source_completion_proposal_get_type ()
 						"GtkSourceCompletionProposal",
 						&g_define_type_info,
 						0);
+
+		g_type_interface_add_prerequisite (gtk_source_completion_proposal_type_id,
+		                                   G_TYPE_OBJECT);
 	}
 	
 	return gtk_source_completion_proposal_type_id;
@@ -226,6 +244,23 @@ gtk_source_completion_proposal_get_info (GtkSourceCompletionProposal *proposal)
 	return GTK_SOURCE_COMPLETION_PROPOSAL_GET_INTERFACE (proposal)->get_info (proposal);
 }
 
+guint
+gtk_source_completion_proposal_hash	(GtkSourceCompletionProposal *proposal)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROPOSAL (proposal), 0);
+	return GTK_SOURCE_COMPLETION_PROPOSAL_GET_INTERFACE (proposal)->hash (proposal);
+}
+
+gboolean
+gtk_source_completion_proposal_equal (GtkSourceCompletionProposal *proposal,
+                                      GtkSourceCompletionProposal *other)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROPOSAL (proposal), FALSE);
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROPOSAL (other), FALSE);
+	
+	return GTK_SOURCE_COMPLETION_PROPOSAL_GET_INTERFACE (proposal)->equal (proposal, other);
+}
+
 /**
  * gtk_source_completion_proposal_changed:
  * @proposal: A #GtkSourceCompletionProposal
diff --git a/gtksourceview/gtksourcecompletionproposal.h b/gtksourceview/gtksourcecompletionproposal.h
index abb147a..89a123a 100644
--- a/gtksourceview/gtksourcecompletionproposal.h
+++ b/gtksourceview/gtksourcecompletionproposal.h
@@ -49,6 +49,10 @@ struct _GtkSourceCompletionProposalIface
 	GdkPixbuf	*(*get_icon)	(GtkSourceCompletionProposal *proposal);
 	const gchar	*(*get_info)	(GtkSourceCompletionProposal *proposal);
 	
+	guint		 (*hash)	(GtkSourceCompletionProposal *proposal);
+	gboolean	 (*equal)	(GtkSourceCompletionProposal *proposal,
+					 GtkSourceCompletionProposal *other);
+	
 	/* Signals */
 	void		 (*changed)	(GtkSourceCompletionProposal *proposal);
 };
@@ -64,6 +68,10 @@ const gchar		*gtk_source_completion_proposal_get_info	(GtkSourceCompletionPropos
 
 void			 gtk_source_completion_proposal_changed		(GtkSourceCompletionProposal *proposal);
 
+guint			 gtk_source_completion_proposal_hash		(GtkSourceCompletionProposal *proposal);
+gboolean		 gtk_source_completion_proposal_equal           (GtkSourceCompletionProposal *proposal,
+									 GtkSourceCompletionProposal *other);
+
 G_END_DECLS
 
 #endif /* __GTK_SOURCE_COMPLETION_PROPOSAL_H__ */
diff --git a/gtksourceview/gtksourcecompletionprovider.c b/gtksourceview/gtksourcecompletionprovider.c
index 6b5c0c2..df18511 100644
--- a/gtksourceview/gtksourcecompletionprovider.c
+++ b/gtksourceview/gtksourcecompletionprovider.c
@@ -51,26 +51,30 @@ gtk_source_completion_provider_get_icon_default (GtkSourceCompletionProvider *pr
 	return NULL;
 }
 
-static GList *
-gtk_source_completion_provider_get_proposals_default (GtkSourceCompletionProvider *provider,
-                                                      GtkTextIter                 *iter)
+static void
+gtk_source_completion_provider_populate_default (GtkSourceCompletionProvider *provider,
+                                                 GtkSourceCompletionContext  *context)
 {
-	return NULL;
+	gtk_source_completion_context_add_proposals (context, provider, NULL, TRUE);
 }
 
 static gboolean
-gtk_source_completion_provider_filter_proposal_default (GtkSourceCompletionProvider *provider,
-                                                        GtkSourceCompletionProposal *proposal,
-                                                        GtkTextIter                 *iter,
-                                                        const gchar                 *criteria)
+gtk_source_completion_provider_get_interactive_default (GtkSourceCompletionProvider *provider)
 {
 	return TRUE;
 }
 
-static const gchar *
-gtk_source_completion_provider_get_capabilities_default (GtkSourceCompletionProvider *provider)
+static gboolean
+gtk_source_completion_provider_get_default_default (GtkSourceCompletionProvider *provider)
+{
+	return TRUE;
+}
+
+static gboolean
+gtk_source_completion_provider_match_default (GtkSourceCompletionProvider *provider,
+                                              GtkSourceCompletionContext  *context)
 {
-	return GTK_SOURCE_COMPLETION_CAPABILITY_AUTOMATIC;
+	return TRUE;
 }
 
 static GtkWidget *
@@ -103,10 +107,11 @@ gtk_source_completion_provider_base_init (GtkSourceCompletionProviderIface *ifac
 	iface->get_name = gtk_source_completion_provider_get_name_default;
 	iface->get_icon = gtk_source_completion_provider_get_icon_default;
 
-	iface->get_proposals = gtk_source_completion_provider_get_proposals_default;
-	iface->filter_proposal = gtk_source_completion_provider_filter_proposal_default;
-	
-	iface->get_capabilities = gtk_source_completion_provider_get_capabilities_default;
+	iface->populate = gtk_source_completion_provider_populate_default;
+
+	iface->match = gtk_source_completion_provider_match_default;
+	iface->get_interactive = gtk_source_completion_provider_get_interactive_default;
+	iface->get_default = gtk_source_completion_provider_get_default_default;
 	
 	iface->get_info_widget = gtk_source_completion_provider_get_info_widget_default;
 	iface->update_info = gtk_source_completion_provider_update_info_default;
@@ -144,6 +149,9 @@ gtk_source_completion_provider_get_type ()
 							"GtkSourceCompletionProvider", 
 							&g_define_type_info, 
 							0);
+
+		g_type_interface_add_prerequisite (gtk_source_completion_provider_type_id,
+		                                   G_TYPE_OBJECT);
 	}
 
 	return gtk_source_completion_provider_type_id;
@@ -182,68 +190,52 @@ gtk_source_completion_provider_get_icon (GtkSourceCompletionProvider *provider)
 }
 
 /**
- * gtk_source_completion_provider_get_proposals:
+ * gtk_source_completion_provider_populate:
  * @provider: The #GtkSourceCompletionProvider
- * @iter: A #GtkTextIter
+ * @context: The #GtkSourceCompletionContext
  *
- * Get proposals from the provider for completion.
+ * Populate @context with proposals from @provider
  *
- * Returns: a list of #GtkSourceViewProposal or NULL if there are no proposals.
- *          The returned list and the contained #GtkSourceViewProposal are 
- *          owned by the caller and will be freed when no longer needed.
  */
-GList * 
-gtk_source_completion_provider_get_proposals (GtkSourceCompletionProvider *provider,
-                                              GtkTextIter                 *iter)
+void
+gtk_source_completion_provider_populate (GtkSourceCompletionProvider *provider,
+                                         GtkSourceCompletionContext  *context)
 {
-	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider), NULL);
-	return GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->get_proposals (provider, iter);
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider));
+	GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->populate (provider, context);
 }
 
-/**
- * gtk_source_completion_provider_filter_proposal:
- * @provider: The #GtkSourceCompletionProvider
- * @proposal: A #GtkSourceCompletionProposal
- * @iter: A #GtkTextIter
- * @criteria: A string representing the filter criteria
- *
- * Determines whether to filter @proposal based on @criteria. It is guaranteed
- * that @criteria is always a valid UTF-8 string (and never %NULL). 
- * Implementations are not restricted to using @criteria as a filter criteria.
- * They may also use @iter to do their own matching.
- *
- * Returns: %TRUE if @proposal conforms to @criteria and should be show,
- *          or %FALSE if @proposal should be hidden.
- */
 gboolean
-gtk_source_completion_provider_filter_proposal (GtkSourceCompletionProvider *provider,
-                                                GtkSourceCompletionProposal *proposal,
-                                                GtkTextIter                 *iter,
-                                                const gchar                 *criteria)
+gtk_source_completion_provider_get_default (GtkSourceCompletionProvider *provider)
 {
 	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider), FALSE);
-	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROPOSAL (proposal), FALSE);
-	g_return_val_if_fail (criteria != NULL, FALSE);
+	return GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->get_default (provider);
+}
 
-	return GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->filter_proposal (provider, 
-	                                                                                 proposal, 
-	                                                                                 iter, 
-	                                                                                 criteria);
+gboolean
+gtk_source_completion_provider_get_interactive (GtkSourceCompletionProvider *provider)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider), FALSE);
+	return GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->get_interactive (provider);
 }
 
 /**
- * gtk_source_completion_provider_get_capabilities:
+ * gtk_source_completion_provider_match:
  * @provider: The #GtkSourceCompletionProvider
+ * @context: The #GtkSourceCompletionContext
  *
- * A list of capabilities this provider supports
+ * Get whether the provider match the context of completion detailed in
+ * @context.
  *
- * Returns: list of capabilities.
+ * Returns: %TRUE if @provider matches the completion context, %FALSE otherwise
  */
-const gchar *
-gtk_source_completion_provider_get_capabilities (GtkSourceCompletionProvider *provider)
+gboolean
+gtk_source_completion_provider_match (GtkSourceCompletionProvider *provider,
+                                      GtkSourceCompletionContext  *context)
 {
-	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider), NULL);
-	return GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->get_capabilities (provider);
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider), TRUE);
+	return GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->match (provider,
+	                                                                       context);
 }
 
 /**
diff --git a/gtksourceview/gtksourcecompletionprovider.h b/gtksourceview/gtksourcecompletionprovider.h
index 719a6e5..6077ba7 100644
--- a/gtksourceview/gtksourcecompletionprovider.h
+++ b/gtksourceview/gtksourcecompletionprovider.h
@@ -26,8 +26,11 @@
 #include <glib.h>
 #include <glib-object.h>
 #include <gtk/gtk.h>
+
 #include <gtksourceview/gtksourcecompletionproposal.h>
 #include <gtksourceview/gtksourcecompletioninfo.h>
+#include <gtksourceview/gtksourcelanguage.h>
+#include <gtksourceview/gtksourcecompletioncontext.h>
 
 G_BEGIN_DECLS
 
@@ -48,14 +51,14 @@ struct _GtkSourceCompletionProviderIface
 	
 	const gchar	*(*get_name)       	(GtkSourceCompletionProvider *provider);
 	GdkPixbuf	*(*get_icon)       	(GtkSourceCompletionProvider *provider);
-	GList 		*(*get_proposals) 	(GtkSourceCompletionProvider *provider,
-						 GtkTextIter                 *iter);
-	gboolean 	 (*filter_proposal) 	(GtkSourceCompletionProvider *provider,
-						 GtkSourceCompletionProposal *proposal,
-						 GtkTextIter                 *iter,
-						 const gchar                 *criteria);
+	void 		 (*populate) 		(GtkSourceCompletionProvider *provider,
+						 GtkSourceCompletionContext  *context);
 
-	const gchar     *(*get_capabilities)	(GtkSourceCompletionProvider *provider);
+	gboolean 	 (*match)		(GtkSourceCompletionProvider *provider,
+	                                         GtkSourceCompletionContext  *context);
+
+	gboolean         (*get_interactive)	(GtkSourceCompletionProvider *provider);
+	gboolean         (*get_default)		(GtkSourceCompletionProvider *provider);
 
 	GtkWidget 	*(*get_info_widget)	(GtkSourceCompletionProvider *provider,
 						 GtkSourceCompletionProposal *proposal);
@@ -75,15 +78,14 @@ const gchar	*gtk_source_completion_provider_get_name	(GtkSourceCompletionProvide
 
 GdkPixbuf	*gtk_source_completion_provider_get_icon	(GtkSourceCompletionProvider *provider);
 
-GList		*gtk_source_completion_provider_get_proposals	(GtkSourceCompletionProvider *provider,
-								 GtkTextIter                 *iter);
+void		 gtk_source_completion_provider_populate	(GtkSourceCompletionProvider *provider,
+								 GtkSourceCompletionContext  *context);
 
-gboolean	 gtk_source_completion_provider_filter_proposal	(GtkSourceCompletionProvider *provider,
-								 GtkSourceCompletionProposal *proposal,
-								 GtkTextIter                 *iter,
-								 const gchar                 *criteria);
+gboolean	 gtk_source_completion_provider_get_interactive (GtkSourceCompletionProvider *provider);
+gboolean	 gtk_source_completion_provider_get_default     (GtkSourceCompletionProvider *provider);
 
-const gchar 	*gtk_source_completion_provider_get_capabilities (GtkSourceCompletionProvider *provider);
+gboolean	 gtk_source_completion_provider_match 		(GtkSourceCompletionProvider *provider,
+		                                                 GtkSourceCompletionContext  *context);
 
 GtkWidget	*gtk_source_completion_provider_get_info_widget	(GtkSourceCompletionProvider *provider,
 								 GtkSourceCompletionProposal *proposal);
@@ -99,3 +101,5 @@ gboolean	 gtk_source_completion_provider_activate_proposal (GtkSourceCompletionP
 G_END_DECLS
 
 #endif
+
+/* vi:ex:ts=8 */
diff --git a/gtksourceview/gtksourceview.c b/gtksourceview/gtksourceview.c
index 4dbb012..dc2fe99 100644
--- a/gtksourceview/gtksourceview.c
+++ b/gtksourceview/gtksourceview.c
@@ -1004,27 +1004,49 @@ gtk_source_view_redo (GtkSourceView *view)
 	}
 }
 
+static GList *
+get_default_providers (GtkSourceCompletion *completion)
+{
+	GList *item;
+	GList *ret = NULL;
+	
+	item = gtk_source_completion_get_providers (completion);
+	
+	while (item)
+	{
+		GtkSourceCompletionProvider *provider;
+		
+		provider = GTK_SOURCE_COMPLETION_PROVIDER (item->data);
+		
+		if (gtk_source_completion_provider_get_default (provider))
+		{
+			ret = g_list_prepend (ret, provider);
+		}
+		
+		item = g_list_next (item);
+	}
+	
+	return g_list_reverse (ret);
+}
+
 static void
 gtk_source_view_show_completion_real (GtkSourceView *view)
 {
 	GtkSourceCompletion *completion;
+	GtkSourceCompletionContext *context;
 	GList *providers;
-	gchar *word;
-
+	
 	completion = gtk_source_view_get_completion (view);
+	context = gtk_source_completion_create_context (completion, NULL);
 	
-	providers = gtk_source_completion_get_providers (completion, 
-	                                                 GTK_SOURCE_COMPLETION_CAPABILITY_AUTOMATIC);
-
-	/* Show automatic providers at the cursor with the current word as the
-	   criteria */
-	word = gtk_source_completion_utils_get_word (
-			GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))));
+	g_object_set (context, "default", TRUE, NULL);
+	
+	providers = get_default_providers (completion);
 
-	gtk_source_completion_show (completion, providers, word, NULL);
+	gtk_source_completion_show (completion, 
+	                            providers, 
+	                            context);
 
-	g_free (word);
-	g_list_foreach (providers, (GFunc)g_object_unref, NULL);
 	g_list_free (providers);
 }
 
diff --git a/tests/completion-simple.c b/tests/completion-simple.c
index 13ae9f0..2466d05 100644
--- a/tests/completion-simple.c
+++ b/tests/completion-simple.c
@@ -28,6 +28,7 @@
 #include <gtksourceview/gtksourceview.h>
 #include <gtksourceview/gtksourcecompletion.h>
 #include <gtksourceview/gtksourcecompletioninfo.h>
+#include <gtksourceview/gtksourcelanguagemanager.h>
 
 #include "gsc-provider-test.h"
 
@@ -57,28 +58,28 @@ destroy_cb (GtkObject *object,
 }
 
 static void
-activate_toggled_cb (GtkToggleButton *button,
+remember_toggled_cb (GtkToggleButton *button,
 		     gpointer user_data)
 {
-	g_object_set (comp, "active",
+	g_object_set (comp, "remember-info-visibility",
 		      gtk_toggle_button_get_active (button),
 		      NULL);
 }
 
 static void
-remember_toggled_cb (GtkToggleButton *button,
-		     gpointer user_data)
+select_on_show_toggled_cb (GtkToggleButton *button,
+			   gpointer user_data)
 {
-	g_object_set (comp, "remember-info-visibility",
+	g_object_set (comp, "select-on-show",
 		      gtk_toggle_button_get_active (button),
 		      NULL);
 }
 
 static void
-select_on_show_toggled_cb (GtkToggleButton *button,
+show_headers_toggled_cb (GtkToggleButton *button,
 			   gpointer user_data)
 {
-	g_object_set (comp, "select-on-show",
+	g_object_set (comp, "show-headers",
 		      gtk_toggle_button_get_active (button),
 		      NULL);
 }
@@ -103,17 +104,28 @@ key_press (GtkWidget   *widget,
 	return FALSE;
 }
 
+static void
+toggle_active_property (gpointer     source,
+                        gpointer     dest,
+                        const gchar *name)
+{
+	gboolean value;
+	
+	g_object_get (source, name, &value, NULL);
+	g_object_set (dest, "active", value, NULL);
+}
+
 static GtkWidget*
 create_window (void)
 {
 	GtkWidget *window;
 	GtkWidget *vbox;
 	GtkWidget *hbox;
-	GtkWidget *activate;
 	GtkWidget *remember;
 	GtkWidget *select_on_show;
-	GtkWidget *label;
-	
+	GtkWidget *show_headers;
+	GtkSourceCompletion *completion;
+		
 	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 	gtk_window_resize (GTK_WINDOW (window), 600, 400);
 	
@@ -124,16 +136,19 @@ create_window (void)
 	GtkWidget *scroll = gtk_scrolled_window_new (NULL, NULL);
 	gtk_container_add (GTK_CONTAINER (scroll), view);
 	
-	activate = gtk_check_button_new_with_label ("Active");
 	remember = gtk_check_button_new_with_label ("Remember info visibility");
 	select_on_show = gtk_check_button_new_with_label ("Select first on show");
-	label = gtk_label_new ("F9 filter by \"sp\"");
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (activate), TRUE);
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (remember), FALSE);
-	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
-	gtk_box_pack_start (GTK_BOX (hbox), activate, FALSE, FALSE, 0);
+	show_headers = gtk_check_button_new_with_label ("Show headers");
+	
+	completion = gtk_source_view_get_completion (GTK_SOURCE_VIEW (view));
+	
+	toggle_active_property (completion, remember, "remember-info-visibility");
+	toggle_active_property (completion, select_on_show, "select-on-show");
+	toggle_active_property (completion, show_headers, "show-headers");
+	
 	gtk_box_pack_start (GTK_BOX (hbox), remember, FALSE, FALSE, 0);
 	gtk_box_pack_start (GTK_BOX (hbox), select_on_show, FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), show_headers, FALSE, FALSE, 0);
 	
 	gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
 	gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
@@ -146,16 +161,16 @@ create_window (void)
 	g_signal_connect (window, "destroy",
 			  G_CALLBACK (destroy_cb),
 			   NULL);
-	g_signal_connect (activate, "toggled",
-			  G_CALLBACK (activate_toggled_cb),
-			  NULL);
 	g_signal_connect (remember, "toggled",
 			  G_CALLBACK (remember_toggled_cb),
 			  NULL);
 	g_signal_connect (select_on_show, "toggled",
 			  G_CALLBACK (select_on_show_toggled_cb),
 			  NULL);
-	
+	g_signal_connect (show_headers, "toggled",
+			  G_CALLBACK (show_headers_toggled_cb),
+			  NULL);
+
 	return window;
 }
 
@@ -183,16 +198,16 @@ create_completion(void)
 	
 	comp = gtk_source_view_get_completion (GTK_SOURCE_VIEW (view));
 	
-	icon = get_icon_from_theme (GTK_STOCK_NETWORK);
+/*	icon = get_icon_from_theme (GTK_STOCK_NETWORK);*/
 
-	prov_test1 = gsc_provider_test_new ("Networking", icon);
+/*	prov_test1 = gsc_provider_test_new ("Networking", icon);*/
 
-	if (icon != NULL)
-	{
-		g_object_unref (icon);
-	}
-	
-	gtk_source_completion_add_provider (comp, GTK_SOURCE_COMPLETION_PROVIDER (prov_test1), NULL);
+/*	if (icon != NULL)*/
+/*	{*/
+/*		g_object_unref (icon);*/
+/*	}*/
+/*	*/
+/*	gtk_source_completion_add_provider (comp, GTK_SOURCE_COMPLETION_PROVIDER (prov_test1), NULL);*/
 	
 	icon = get_icon_from_theme (GTK_STOCK_OPEN);
 	prov_test1 = gsc_provider_test_new ("Open Files", icon);
diff --git a/tests/gsc-provider-devhelp.c b/tests/gsc-provider-devhelp.c
index 17d55c1..41ef3d7 100644
--- a/tests/gsc-provider-devhelp.c
+++ b/tests/gsc-provider-devhelp.c
@@ -7,15 +7,41 @@
 
 #define GSC_PROVIDER_DEVHELP_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GSC_TYPE_PROVIDER_DEVHELP, GscProviderDevhelpPrivate))
 
+#define PROCESS_BATCH 300
+#define MAX_ITEMS 5000
+
+typedef struct
+{
+	GObject parent;
+	
+	DhLink *link;
+} GscDevhelpItem;
+
+typedef struct
+{
+	GObjectClass parent_class;
+} GscDevhelpItemClass;
+
 struct _GscProviderDevhelpPrivate
 {
 	DhBase *dhbase;
 	GtkWidget *view;
+	GdkPixbuf *icon;
 
+	GList *dhptr;
 	GList *proposals;
+	
+	GtkSourceCompletionContext *context;
+	GList *idleptr;
+	guint idle_id;
+	guint cancel_id;
+	guint counter;
 };
 
+GType gsc_devhelp_item_get_type (void);
+
 static void gsc_provider_devhelp_iface_init (GtkSourceCompletionProviderIface *iface);
+static void gsc_devhelp_item_iface_init (gpointer g_iface, gpointer iface_data);
 
 G_DEFINE_TYPE_WITH_CODE (GscProviderDevhelp, 
 			 gsc_provider_devhelp, 
@@ -23,40 +49,211 @@ G_DEFINE_TYPE_WITH_CODE (GscProviderDevhelp,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_SOURCE_COMPLETION_PROVIDER,
 				 		gsc_provider_devhelp_iface_init))
 
+G_DEFINE_TYPE_WITH_CODE (GscDevhelpItem, 
+			 gsc_devhelp_item, 
+			 G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_SOURCE_COMPLETION_PROPOSAL,
+			 			gsc_devhelp_item_iface_init))
+
+static const gchar *
+gsc_devhelp_item_get_text_impl (GtkSourceCompletionProposal *proposal)
+{
+	return dh_link_get_name (((GscDevhelpItem *)proposal)->link);
+}
+
+static void
+gsc_devhelp_item_iface_init (gpointer g_iface, 
+                             gpointer iface_data)
+{
+	GtkSourceCompletionProposalIface *iface = (GtkSourceCompletionProposalIface *)g_iface;
+	
+	/* Interface data getter implementations */
+	iface->get_label = gsc_devhelp_item_get_text_impl;
+	iface->get_text = gsc_devhelp_item_get_text_impl;
+}
+
+static void
+gsc_devhelp_item_init (GscDevhelpItem *object)
+{
+}
+
+static void
+gsc_devhelp_item_class_init (GscDevhelpItemClass *klass)
+{
+}
+
 static const gchar * 
 gsc_provider_devhelp_get_name (GtkSourceCompletionProvider *self)
 {
 	return "Devhelp";
 }
 
-static GList * 
-gsc_provider_devhelp_get_proposals (GtkSourceCompletionProvider *provider,
-                                    GtkTextIter                 *iter)
+static void
+population_finished (GscProviderDevhelp *devhelp)
 {
-	GscProviderDevhelp *devhelp = GSC_PROVIDER_DEVHELP (provider);
+	if (devhelp->priv->idle_id != 0)
+	{
+		g_source_remove (devhelp->priv->idle_id);
+		devhelp->priv->idle_id = 0;
+	}
+	
+	if (devhelp->priv->context != NULL)
+	{
+		if (devhelp->priv->cancel_id)
+		{
+			g_signal_handler_disconnect (devhelp->priv->context, devhelp->priv->cancel_id);
+			devhelp->priv->cancel_id = 0;
+		}
+	
+		g_object_unref (devhelp->priv->context);
+		devhelp->priv->context = NULL;
+	}
+}
+
+static void
+fill_proposals (GscProviderDevhelp *devhelp)
+{
+	GList *item;
+	GList *ret = NULL;
+
+	if (devhelp->priv->dhbase == NULL)
+	{
+		devhelp->priv->dhbase = dh_base_new ();
+		devhelp->priv->view = dh_assistant_view_new ();
+
+		dh_assistant_view_set_base (DH_ASSISTANT_VIEW (devhelp->priv->view), 
+		                            devhelp->priv->dhbase);
+		                            
+		gtk_widget_set_size_request (devhelp->priv->view, 400, 300);
+	}
 	
-	g_list_foreach (devhelp->priv->proposals, (GFunc)g_object_ref, NULL);
+	for (item = dh_base_get_keywords (devhelp->priv->dhbase); item; item = g_list_next (item))
+	{
+		GscDevhelpItem *proposal = g_object_new (gsc_devhelp_item_get_type (), 
+		                                         NULL);
+
+		proposal->link = (DhLink *)item->data;
+		ret = g_list_prepend (ret, proposal);			                
+	}
+	
+	devhelp->priv->proposals = g_list_reverse (ret);
+	devhelp->priv->idleptr = devhelp->priv->proposals;
+}
+
+static gboolean
+is_word_char (gunichar ch)
+{
+	return g_unichar_isprint (ch) && (g_unichar_isalnum (ch) || ch == '_' || ch == ':');
+}
 
-	return g_list_copy (devhelp->priv->proposals);
+static gchar *
+get_word_at_iter (GtkTextIter *iter)
+{
+	GtkTextIter start = *iter;
+	gint line = gtk_text_iter_get_line (iter);
+	
+	if (!gtk_text_iter_backward_char (&start))
+	{
+		return NULL;
+	}
+	
+	while (line == gtk_text_iter_get_line (&start) && 
+	       is_word_char (gtk_text_iter_get_char (&start)) &&
+	       gtk_text_iter_backward_char (&start))
+	;
+	
+	if (gtk_text_iter_equal (iter, &start))
+	{
+		return NULL;
+	}
+	
+	return gtk_text_iter_get_text (&start, iter);
 }
 
 static gboolean
-gsc_provider_devhelp_filter_proposal (GtkSourceCompletionProvider *provider,
-                                      GtkSourceCompletionProposal *proposal,
-                                      GtkTextIter                 *iter,
-                                      const gchar                 *criteria)
+add_in_idle (GscProviderDevhelp *devhelp)
 {
-	const gchar *item;
+	guint idx = 0;
+	GList *ret = NULL;
+	GtkTextIter iter;
+	gchar *word;
+	gboolean finished;
 	
-	item = gtk_source_completion_proposal_get_label (proposal);
-	return g_str_has_prefix (item, criteria);
+	if (devhelp->priv->proposals == NULL)
+	{
+		fill_proposals (devhelp);
+	}
+
+	gtk_source_completion_context_get_iter (devhelp->priv->context,
+	                                        &iter);
+	word = get_word_at_iter (&iter);
+	
+	if (word == NULL)
+	{
+		gtk_source_completion_context_add_proposals (devhelp->priv->context,
+	                                                 GTK_SOURCE_COMPLETION_PROVIDER (devhelp),
+	                                                 NULL,
+	                                                 TRUE);
+		population_finished (devhelp);
+		return FALSE;
+	}
+	
+	while (idx < PROCESS_BATCH && devhelp->priv->idleptr != NULL)
+	{
+		GtkSourceCompletionProposal *proposal;
+		
+		proposal = GTK_SOURCE_COMPLETION_PROPOSAL (devhelp->priv->idleptr->data);
+		
+		if (g_str_has_prefix (gtk_source_completion_proposal_get_text (proposal),
+		                      word))
+		{
+			ret = g_list_prepend (ret, proposal);
+			
+			if (++devhelp->priv->counter >= MAX_ITEMS)
+			{
+				break;
+			}
+		}
+		
+		++idx;
+		devhelp->priv->idleptr = g_list_next (devhelp->priv->idleptr);
+	}
+	
+	finished = devhelp->priv->idleptr == NULL || devhelp->priv->counter >= MAX_ITEMS;
+	ret = g_list_reverse (ret);
+	
+	gtk_source_completion_context_add_proposals (devhelp->priv->context,
+	                                             GTK_SOURCE_COMPLETION_PROVIDER (devhelp),
+	                                             ret,
+	                                             finished);
+	
+	if (finished)
+	{
+		population_finished (devhelp);
+	}
+	
+	g_free (word);
+
+	return !finished;
 }
 
-static const gchar *
-gsc_provider_devhelp_get_capabilities(GtkSourceCompletionProvider *provider)
+static void
+gsc_provider_devhelp_populate (GtkSourceCompletionProvider *provider,
+                               GtkSourceCompletionContext  *context)
 {
-	return GTK_SOURCE_COMPLETION_CAPABILITY_AUTOMATIC ","
-	       GTK_SOURCE_COMPLETION_CAPABILITY_INTERACTIVE;
+	GscProviderDevhelp *devhelp = GSC_PROVIDER_DEVHELP (provider);
+	
+	devhelp->priv->cancel_id = 
+		g_signal_connect_swapped (context, 
+			                      "cancelled", 
+			                      G_CALLBACK (population_finished), 
+			                      provider);
+	
+	devhelp->priv->counter = 0;
+	devhelp->priv->idleptr = devhelp->priv->proposals;
+	devhelp->priv->context = g_object_ref (context);
+	devhelp->priv->idle_id = g_idle_add ((GSourceFunc)add_in_idle,
+	                                     devhelp);
 }
 
 static GtkWidget *
@@ -72,23 +269,27 @@ gsc_provider_devhelp_update_info (GtkSourceCompletionProvider *provider,
                                   GtkSourceCompletionInfo     *info)
 {
 	GscProviderDevhelp *self = GSC_PROVIDER_DEVHELP (provider);
-	const gchar *uri;
-	
-	uri = gtk_source_completion_proposal_get_label (proposal);
+	const gchar *uri = dh_link_get_name (((GscDevhelpItem *)proposal)->link);
 
 	dh_assistant_view_search (DH_ASSISTANT_VIEW (self->priv->view), uri);
 }
 
+static GdkPixbuf *
+gsc_provider_devhelp_get_icon (GtkSourceCompletionProvider *provider)
+{
+	return GSC_PROVIDER_DEVHELP (provider)->priv->icon;
+}
+
 static void
 gsc_provider_devhelp_iface_init (GtkSourceCompletionProviderIface *iface)
 {
 	iface->get_name = gsc_provider_devhelp_get_name;
-	iface->get_proposals = gsc_provider_devhelp_get_proposals;
-	iface->get_capabilities = gsc_provider_devhelp_get_capabilities;
-	iface->filter_proposal = gsc_provider_devhelp_filter_proposal;
+	iface->populate = gsc_provider_devhelp_populate;
 	
 	iface->get_info_widget = gsc_provider_devhelp_get_info_widget;
 	iface->update_info = gsc_provider_devhelp_update_info;
+	
+	iface->get_icon = gsc_provider_devhelp_get_icon;
 }
 
 static void
@@ -96,18 +297,34 @@ gsc_provider_devhelp_finalize (GObject *object)
 {
 	GscProviderDevhelp *provider = GSC_PROVIDER_DEVHELP (object);
 	
+	
+	
 	g_object_unref (provider->priv->dhbase);
 	g_list_foreach (provider->priv->proposals, (GFunc)g_object_unref, NULL);
-
+	
+	if (provider->priv->icon)
+	{
+		g_object_unref (provider->priv->icon);
+	}
+	
 	G_OBJECT_CLASS (gsc_provider_devhelp_parent_class)->finalize (object);
 }
 
 static void
+gsc_provider_devhelp_dispose (GObject *object)
+{
+	population_finished (GSC_PROVIDER_DEVHELP (object));
+	
+	G_OBJECT_CLASS (gsc_provider_devhelp_parent_class)->dispose (object);
+}
+
+static void
 gsc_provider_devhelp_class_init (GscProviderDevhelpClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	
 	object_class->finalize = gsc_provider_devhelp_finalize;
+	object_class->dispose = gsc_provider_devhelp_dispose;
 
 	g_type_class_add_private (object_class, sizeof(GscProviderDevhelpPrivate));
 }
@@ -115,35 +332,12 @@ gsc_provider_devhelp_class_init (GscProviderDevhelpClass *klass)
 static void
 gsc_provider_devhelp_init (GscProviderDevhelp *self)
 {
-	GList *item;
-	GList *ret;
-	
 	self->priv = GSC_PROVIDER_DEVHELP_GET_PRIVATE (self);
 	
-	self->priv->dhbase = dh_base_new ();
-	
-	ret = NULL;
-	
-	for (item = dh_base_get_keywords (self->priv->dhbase); item; item = g_list_next (item))
-	{
-		DhLink *link = (DhLink *)item->data;
-		
-		ret = g_list_prepend (ret, gtk_source_completion_item_new (dh_link_get_name (link),
-									   dh_link_get_name (link),
-									   NULL,
-									   dh_link_get_uri (link)));
-	}
-	
-	
-	self->priv->view = dh_assistant_view_new ();
-	dh_assistant_view_set_base (DH_ASSISTANT_VIEW (self->priv->view), self->priv->dhbase);
-	
-	gtk_widget_set_size_request (self->priv->view, 400, 300);
-
-	self->priv->proposals = g_list_reverse (ret);
+	self->priv->icon = gdk_pixbuf_new_from_file ("/usr/share/icons/hicolor/16x16/apps/devhelp.png", NULL);
 }
 
-GscProviderDevhelp*
+GscProviderDevhelp *
 gsc_provider_devhelp_new ()
 {
 	GscProviderDevhelp *ret = g_object_new (GSC_TYPE_PROVIDER_DEVHELP, NULL);
diff --git a/tests/gsc-provider-test.c b/tests/gsc-provider-test.c
index 62502c6..e72e1e1 100644
--- a/tests/gsc-provider-test.c
+++ b/tests/gsc-provider-test.c
@@ -30,6 +30,8 @@ struct _GscProviderTestPrivate
 	gchar *name;
 	GdkPixbuf *icon;
 	GdkPixbuf *proposal_icon;
+	
+	GList *proposals;
 };
 
 G_DEFINE_TYPE_WITH_CODE (GscProviderTest,
@@ -50,7 +52,6 @@ gsc_provider_test_get_icon (GtkSourceCompletionProvider *self)
 	return GSC_PROVIDER_TEST (self)->priv->icon;
 }
 
-
 static GList *
 append_item (GList *list, const gchar *name, GdkPixbuf *icon, const gchar *info)
 {
@@ -61,37 +62,111 @@ append_item (GList *list, const gchar *name, GdkPixbuf *icon, const gchar *info)
 }
 
 static GList *
-gsc_provider_test_get_proposals (GtkSourceCompletionProvider *base,
-                                 GtkTextIter                 *iter)
+get_proposals (GscProviderTest *provider)
 {
-	GscProviderTest *provider = GSC_PROVIDER_TEST (base);
-	GList *list = NULL;
+	if (provider->priv->proposals == NULL)
+	{
+		GList *list = NULL;
+	
+		list = append_item (list, "aaabbccc", provider->priv->icon, "Info proposal 1.1");
+		list = append_item (list, "aaaddccc", provider->priv->icon, "Info proposal 1.2");
+		list = append_item (list, "aabbddd", provider->priv->icon, "Info proposal 1.3");
+		list = append_item (list, "bbddaa", provider->priv->icon, "Info proposal 1.3");
 	
-	list = append_item (list, "aa", provider->priv->proposal_icon, "Info proposal 1.1");
-	list = append_item (list, "ab", provider->priv->proposal_icon, "Info proposal 1.2");
-	list = append_item (list, "bc", provider->priv->proposal_icon, "Info proposal 1.3");
-	list = append_item (list, "bd", provider->priv->proposal_icon, "Info proposal 1.3");
+		provider->priv->proposals = list;
+	}
 	
-	return list;
+	return provider->priv->proposals;
+}
+
+static gboolean
+gsc_provider_test_match (GtkSourceCompletionProvider *provider,
+                         GtkSourceCompletionContext  *context)
+{
+	return TRUE;
 }
 
 static gboolean
-gsc_provider_test_filter_proposal (GtkSourceCompletionProvider *provider,
-                                   GtkSourceCompletionProposal *proposal,
-                                   GtkTextIter                 *iter,
-                                   const gchar                 *criteria)
+is_word_char (gunichar ch)
+{
+	return g_unichar_isprint (ch) && (g_unichar_isalnum (ch) || ch == g_utf8_get_char ("_"));
+}
+
+static gchar *
+get_word_at_iter (GtkTextIter *iter)
 {
-	const gchar *label;
+	GtkTextIter start = *iter;
+	gint line = gtk_text_iter_get_line (iter);
+	
+	if (!gtk_text_iter_ends_word (iter))
+	{
+		return NULL;
+	}
+	
+	if (!gtk_text_iter_backward_char (&start))
+	{
+		return NULL;
+	}
+	
+	while (line == gtk_text_iter_get_line (&start) && 
+	       is_word_char (gtk_text_iter_get_char (&start)) &&
+	       gtk_text_iter_backward_char (&start))
+	;
+	
+	if (gtk_text_iter_equal (iter, &start))
+	{
+		return NULL;
+	}
 	
-	label = gtk_source_completion_proposal_get_label (proposal);
-	return g_str_has_prefix (label, criteria);
+	return gtk_text_iter_get_text (&start, iter);
 }
 
-static const gchar *
-gsc_provider_test_get_capabilities (GtkSourceCompletionProvider *provider)
+static void
+gsc_provider_test_populate (GtkSourceCompletionProvider *provider,
+                            GtkSourceCompletionContext  *context)
 {
-	return GTK_SOURCE_COMPLETION_CAPABILITY_INTERACTIVE ","
-	       GTK_SOURCE_COMPLETION_CAPABILITY_AUTOMATIC;
+	GscProviderTest *base = GSC_PROVIDER_TEST (provider);
+	GList *proposals = get_proposals (base);
+	GList *ret = NULL;
+	
+	GtkTextIter iter;
+	gchar *word;
+	
+	gtk_source_completion_context_get_iter (context, &iter);
+	word = get_word_at_iter (&iter);
+	
+	if (word == NULL && !gtk_source_completion_context_get_default (context))
+	{
+		gtk_source_completion_context_add_proposals (context,
+		                                             provider,
+		                                             NULL,
+		                                             TRUE);
+		return;
+	}
+	
+	/* Filter proposals */
+	while (proposals)
+	{
+		GtkSourceCompletionProposal *item = GTK_SOURCE_COMPLETION_PROPOSAL (proposals->data);
+		
+		if (word == NULL || 
+		    g_str_has_prefix (gtk_source_completion_proposal_get_text (item),
+		                      word))
+		{
+			ret = g_list_prepend (ret, item);
+		}
+
+		proposals = g_list_next (proposals);
+	}
+	
+	ret = g_list_reverse (ret);	
+	gtk_source_completion_context_add_proposals (context, 
+	                                             provider, 
+	                                             ret,
+	                                             TRUE);
+
+	g_list_free (ret);
+	g_free (word);
 }
 
 static void 
@@ -130,9 +205,8 @@ gsc_provider_test_iface_init (GtkSourceCompletionProviderIface *iface)
 	iface->get_name = gsc_provider_test_get_name;
 	iface->get_icon = gsc_provider_test_get_icon;
 
-	iface->get_proposals = gsc_provider_test_get_proposals;
-	iface->filter_proposal = gsc_provider_test_filter_proposal;
-	iface->get_capabilities = gsc_provider_test_get_capabilities;
+	iface->populate = gsc_provider_test_populate;
+	iface->match = gsc_provider_test_match;
 }
 
 static void 



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