[gtksourceview/gtksourcecompletion] Added general purpose words completion provider



commit 571cecdd915b0154abaa84a4b40cdb304c4a4dfb
Author: Jesse van den Kieboom <jessevdk gnome org>
Date:   Mon Sep 21 12:23:47 2009 +0200

    Added general purpose words completion provider
    
    This is basicly a rewrite of the words provider from the test case.
    You can register multiple buffers with the provider to monitor and words
    will be completed from all buffers through a shared words library.

 configure.ac                                       |   10 +
 gtksourceview/Makefile.am                          |    9 +-
 gtksourceview/completion-providers/Makefile.am     |    1 +
 .../completion-providers/words/Makefile.am         |   33 +
 .../words/gtksourcecompletionwords.c               |  506 ++++++++
 .../words/gtksourcecompletionwords.h               |   67 ++
 .../words/gtksourcecompletionwordsbuffer.c         |  701 +++++++++++
 .../words/gtksourcecompletionwordsbuffer.h         |   66 ++
 .../words/gtksourcecompletionwordslibrary.c        |  352 ++++++
 .../words/gtksourcecompletionwordslibrary.h        |   86 ++
 .../words/gtksourcecompletionwordsproposal.c       |  141 +++
 .../words/gtksourcecompletionwordsproposal.h       |   65 ++
 tests/Makefile.am                                  |    4 +-
 tests/completion-simple.c                          |   33 +-
 tests/gsc-provider-words.c                         | 1217 --------------------
 tests/gsc-provider-words.h                         |   36 -
 16 files changed, 2043 insertions(+), 1284 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 157e3a5..b2868e7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,6 +88,14 @@ fi
 
 AM_CONDITIONAL(USE_DEVHELP, test "x$have_devhelp" = "xyes")
 
+dnl Check to enable completion providers
+AC_ARG_ENABLE(providers,
+	    AC_HELP_STRING([--enable-completion-providers],
+	    		   [Enable building completion providers (words)]),,
+	    enable_providers=yes)
+
+AM_CONDITIONAL(ENABLE_PROVIDERS, test "x$enable_providers" = "xyes")
+
 # i18N stuff
 GETTEXT_PACKAGE=gtksourceview-2.0
 AC_SUBST(GETTEXT_PACKAGE)
@@ -107,6 +115,8 @@ Makefile
 gtksourceview-zip
 po/Makefile.in
 gtksourceview/Makefile
+gtksourceview/completion-providers/Makefile
+gtksourceview/completion-providers/words/Makefile
 gtksourceview/language-specs/Makefile
 docs/Makefile
 docs/reference/Makefile
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index 76f4c7d..0a02ec4 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -1,5 +1,9 @@
 SUBDIRS = language-specs
 
+if ENABLE_PROVIDERS
+SUBDIRS += completion-providers
+endif
+
 INCLUDES = 				\
 	-DDATADIR=\""$(datadir)"\" 	\
 	-DG_LOG_DOMAIN=\"GtkSourceView\"\
@@ -85,7 +89,10 @@ libgtksourceview_2_0_la_SOURCES = 	\
 nodist_libgtksourceview_2_0_la_SOURCES =\
 	$(BUILT_SOURCES)
 
-libgtksourceview_2_0_la_LIBADD = $(DEP_LIBS)
+completion_providers = 							\
+	completion-providers/words/libgtksourcecompletionwords.la
+
+libgtksourceview_2_0_la_LIBADD = $(DEP_LIBS) $(completion_providers)
 libgtksourceview_2_0_la_LDFLAGS = -no-undefined -export-symbols-regex "^gtk_source_.*"
 libgtksourceview_2_0_includedir = $(includedir)/gtksourceview-2.0/gtksourceview
 
diff --git a/gtksourceview/completion-providers/Makefile.am b/gtksourceview/completion-providers/Makefile.am
new file mode 100644
index 0000000..de2a31a
--- /dev/null
+++ b/gtksourceview/completion-providers/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = words
diff --git a/gtksourceview/completion-providers/words/Makefile.am b/gtksourceview/completion-providers/words/Makefile.am
new file mode 100644
index 0000000..bd4b286
--- /dev/null
+++ b/gtksourceview/completion-providers/words/Makefile.am
@@ -0,0 +1,33 @@
+INCLUDES = 				\
+	-DDATADIR=\""$(datadir)"\" 	\
+	-DG_LOG_DOMAIN=\"GtkSourceView\"\
+	-I$(top_srcdir) -I$(srcdir) 	\
+	$(DISABLE_DEPRECATED_CFLAGS)	\
+	$(WARN_CFLAGS) 			\
+	$(DEP_CFLAGS)
+
+noinst_LTLIBRARIES = libgtksourcecompletionwords.la
+
+NOINST_H_FILES =				\
+	gtksourcecompletionwordslibrary.h	\
+	gtksourcecompletionwordsproposal.h	\
+	gtksourcecompletionwordsbuffer.h
+
+libgtksourcecompletionwords_headers = 		\
+	gtksourcecompletionwords.h
+
+libgtksourcecompletionwords_la_SOURCES =	\
+	gtksourcecompletionwords.c		\
+	gtksourcecompletionwordslibrary.c	\
+	gtksourcecompletionwordsproposal.c	\
+	gtksourcecompletionwordsbuffer.c	\
+	$(libgtksourcecompletionwords_headers)	\
+	$(NOINST_H_FILES)
+
+libgtksourcecompletionwords_includedir = \
+	$(includedir)/gtksourceview-2.0/gtksourceview/completion-providers/words
+
+libgtksourcecompletionwords_include_HEADERS =		\
+	$(libgtksourcecompletionwords_headers)
+
+-include $(top_srcdir)/git.mk
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwords.c b/gtksourceview/completion-providers/words/gtksourcecompletionwords.c
new file mode 100644
index 0000000..9f16ee1
--- /dev/null
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwords.c
@@ -0,0 +1,506 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecompletionproviderwords.c
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * gtksourceview 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.
+ *
+ * gtksourceview is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gtksourceview; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#include "gtksourcecompletionwords.h"
+#include "gtksourcecompletionwordslibrary.h"
+#include "gtksourcecompletionwordsbuffer.h"
+
+#include <gtksourceview/gtksourcecompletion.h>
+#include <gtksourceview/gtksourceview-i18n.h>
+#include <string.h>
+
+#define GTK_SOURCE_COMPLETION_WORDS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GSC_TYPE_PROVIDER_WORDS, GtkSourceCompletionWordsPrivate))
+
+#define BUFFER_KEY "GtkSourceCompletionWordsBufferKey"
+
+enum
+{
+	PROP_0,
+	
+	PROP_NAME,
+	PROP_ICON,
+	PROP_PROPOSALS_BATCH_SIZE,
+	PROP_SCAN_BATCH_SIZE
+};
+
+struct _GtkSourceCompletionWordsPrivate
+{
+	gchar *name;
+	GdkPixbuf *icon;
+
+	gchar *word;
+	gint word_len;
+	guint idle_id;
+	
+	GtkSourceCompletionContext *context;
+	GSequenceIter *populate_iter;
+
+	guint cancel_id;
+	
+	guint proposals_batch_size;
+	guint scan_batch_size;
+	
+	GtkSourceCompletionWordsLibrary *library;
+	GList *buffers;
+};
+
+typedef struct
+{
+	GObjectClass parent_class;
+} GscProposalWordsClass;
+
+static void gtk_source_completion_words_iface_init (GtkSourceCompletionProviderIface *iface);
+
+GType gsc_proposal_words_get_type (void);
+
+G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionWords,
+			 gtk_source_completion_words,
+			 G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_SOURCE_COMPLETION_PROVIDER,
+				 		gtk_source_completion_words_iface_init))
+
+static const gchar * 
+gtk_source_completion_words_get_name (GtkSourceCompletionProvider *self)
+{
+	return GTK_SOURCE_COMPLETION_WORDS (self)->priv->name;
+}
+
+static GdkPixbuf * 
+gtk_source_completion_words_get_icon (GtkSourceCompletionProvider *self)
+{
+	return GTK_SOURCE_COMPLETION_WORDS (self)->priv->icon;
+}
+
+static gboolean
+gtk_source_completion_words_match (GtkSourceCompletionProvider *provider,
+                                   GtkSourceCompletionContext  *context)
+{
+	return TRUE;
+}
+
+static gboolean
+is_word_char (gunichar ch)
+{
+	return g_unichar_isprint (ch) && 
+	       (g_unichar_isalnum (ch) || ch == g_utf8_get_char ("_"));
+}
+
+static void
+population_finished (GtkSourceCompletionWords *words)
+{
+	if (words->priv->idle_id != 0)
+	{
+		g_source_remove (words->priv->idle_id);
+		words->priv->idle_id = 0;
+	}
+	
+	g_free (words->priv->word);
+	words->priv->word = NULL;
+	
+	if (words->priv->context != NULL)
+	{
+		if (words->priv->cancel_id)
+		{
+			g_signal_handler_disconnect (words->priv->context, 
+			                             words->priv->cancel_id);
+			words->priv->cancel_id = 0;
+		}
+	
+		g_object_unref (words->priv->context);
+		words->priv->context = NULL;
+	}
+}
+
+static gchar *
+get_word_at_iter (GtkSourceCompletionWords *words,
+                  GtkTextIter              *iter)
+{
+	GtkTextIter start = *iter;
+	gint line = gtk_text_iter_get_line (iter);
+	gboolean went_back = TRUE;
+	
+	if (!gtk_text_iter_backward_char (&start))
+	{
+		return NULL;
+	}
+
+	while (went_back &&
+	       line == gtk_text_iter_get_line (&start) && 
+	       is_word_char (gtk_text_iter_get_char (&start)))
+	{
+		went_back = gtk_text_iter_backward_char (&start);
+	}
+	
+	if (went_back)
+	{
+		gtk_text_iter_forward_char (&start);
+	}
+	
+	if (gtk_text_iter_equal (iter, &start))
+	{
+		return NULL;
+	}
+
+	return gtk_text_iter_get_text (&start, iter);
+}
+
+static gboolean
+add_in_idle (GtkSourceCompletionWords *words)
+{
+	guint idx = 0;
+	GList *ret = NULL;
+	gboolean finished;
+	
+	/* Don't complete empty string (when word == NULL) */
+	if (words->priv->word == NULL)
+	{
+		gtk_source_completion_context_add_proposals (words->priv->context,
+	                                                     GTK_SOURCE_COMPLETION_PROVIDER (words),
+	                                                     NULL,
+	                                                     TRUE);
+		population_finished (words);
+		return FALSE;
+	}
+	
+	if (words->priv->populate_iter == NULL)
+	{
+		words->priv->populate_iter = 
+			gtk_source_completion_words_library_find_first (words->priv->library,
+			                                                words->priv->word,
+			                                                words->priv->word_len);
+	}
+
+	while (idx < words->priv->proposals_batch_size && 
+	       words->priv->populate_iter)
+	{
+		GtkSourceCompletionWordsProposal *proposal = 
+				gtk_source_completion_words_library_get_proposal (words->priv->populate_iter);
+		
+		/* Only add non-exact matches */
+		if (strcmp (gtk_source_completion_words_proposal_get_word (proposal),
+		            words->priv->word) != 0)
+		{
+			ret = g_list_prepend (ret, proposal);
+		}
+
+		words->priv->populate_iter = 
+				gtk_source_completion_words_library_find_next (words->priv->populate_iter,
+		                                                               words->priv->word,
+		                                                               words->priv->word_len);
+		++idx;
+	}
+	
+	ret = g_list_reverse (ret);
+	finished = words->priv->populate_iter == NULL;
+	
+	gtk_source_completion_context_add_proposals (words->priv->context,
+	                                             GTK_SOURCE_COMPLETION_PROVIDER (words),
+	                                             ret,
+	                                             finished);
+	
+	if (finished)
+	{
+		gtk_source_completion_words_library_unlock (words->priv->library);
+		population_finished (words);
+	}
+
+	return !finished;
+}
+
+static void
+gtk_source_completion_words_populate (GtkSourceCompletionProvider *provider,
+                                      GtkSourceCompletionContext  *context)
+{
+	GtkSourceCompletionWords *words = GTK_SOURCE_COMPLETION_WORDS (provider);
+	GtkTextIter iter;
+	gchar *word;
+
+	gtk_source_completion_context_get_iter (context, &iter);
+
+	g_free (words->priv->word);
+	word = get_word_at_iter (words, &iter);
+	
+	if (word == NULL)
+	{
+		gtk_source_completion_context_add_proposals (context,
+		                                             provider,
+		                                             NULL,
+		                                             TRUE);
+		return;
+	}
+
+	words->priv->cancel_id = 
+		g_signal_connect_swapped (context, 
+			                  "cancelled", 
+			                   G_CALLBACK (population_finished), 
+			                   provider);
+
+	words->priv->context = g_object_ref (context);
+
+	words->priv->word = word;
+	words->priv->word_len = strlen (word);
+	
+	/* Do first right now */
+	if (add_in_idle (words))
+	{
+		gtk_source_completion_words_library_lock (words->priv->library);
+		words->priv->idle_id = g_idle_add ((GSourceFunc)add_in_idle,
+		                                   words);
+	}	
+}
+
+static void
+remove_buffer (GtkSourceCompletionWordsBuffer *buffer)
+{
+	g_object_set_data (G_OBJECT (gtk_source_completion_words_buffer_get_buffer (buffer)),
+	                   BUFFER_KEY,
+	                   NULL);
+
+	g_object_unref (buffer);
+}
+
+static void
+gtk_source_completion_words_dispose (GObject *object)
+{
+	GtkSourceCompletionWords *provider = GTK_SOURCE_COMPLETION_WORDS (object);
+	population_finished (provider);
+	
+	g_list_foreach (provider->priv->buffers, (GFunc)remove_buffer, NULL);
+	g_list_free (provider->priv->buffers);
+	
+	g_free (provider->priv->name);
+	provider->priv->name = NULL;
+	
+	if (provider->priv->icon)
+	{
+		g_object_unref (provider->priv->icon);
+		provider->priv->icon = NULL;
+	}
+	
+	if (provider->priv->library)
+	{
+		g_object_unref (provider->priv->library);
+		provider->priv->library = NULL;
+	}
+	
+	G_OBJECT_CLASS (gtk_source_completion_words_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_completion_words_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+	GtkSourceCompletionWords *self = GTK_SOURCE_COMPLETION_WORDS (object);
+	
+	switch (prop_id)
+	{
+		case PROP_NAME:
+			g_free (self->priv->name);
+			self->priv->name = g_value_dup_string (value);
+			
+			if (self->priv->name == NULL)
+			{
+				self->priv->name = g_strdup (_("Document Words"));
+			}
+		break;
+		case PROP_ICON:
+			if (self->priv->icon)
+			{
+				g_object_unref (self->priv->icon);
+			}
+			
+			self->priv->icon = g_value_dup_object (value);
+		break;
+		case PROP_PROPOSALS_BATCH_SIZE:
+			self->priv->proposals_batch_size = g_value_get_uint (value);
+		break;
+		case PROP_SCAN_BATCH_SIZE:
+			self->priv->scan_batch_size = g_value_get_uint (value);
+		break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gtk_source_completion_words_get_property (GObject    *object,
+                                          guint       prop_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+	GtkSourceCompletionWords *self = GTK_SOURCE_COMPLETION_WORDS (object);
+	
+	switch (prop_id)
+	{
+		case PROP_NAME:
+			g_value_set_string (value, self->priv->name);
+		break;
+		case PROP_ICON:
+			g_value_set_object (value, self->priv->icon);
+		break;
+		case PROP_PROPOSALS_BATCH_SIZE:
+			g_value_set_uint (value, self->priv->proposals_batch_size);
+		break;
+		case PROP_SCAN_BATCH_SIZE:
+			g_value_set_uint (value, self->priv->scan_batch_size);
+		break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void 
+gtk_source_completion_words_class_init (GtkSourceCompletionWordsClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = gtk_source_completion_words_dispose;
+
+	object_class->set_property = gtk_source_completion_words_set_property;
+	object_class->get_property = gtk_source_completion_words_get_property;
+	
+	g_object_class_install_property (object_class,
+	                                 PROP_NAME,
+	                                 g_param_spec_string ("name",
+	                                                      _("Name"),
+	                                                      _("The provider name"),
+	                                                      NULL,
+	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	
+	g_object_class_install_property (object_class,
+	                                 PROP_ICON,
+	                                 g_param_spec_object ("icon",
+	                                                      _("Icon"),
+	                                                      _("The provider icon"),
+	                                                      GDK_TYPE_PIXBUF,
+	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_PROPOSALS_BATCH_SIZE,
+	                                 g_param_spec_uint ("proposals-batch-size",
+	                                                    _("Proposals Batch Size"),
+	                                                    _("Number of proposals added in one batch"),
+	                                                    1,
+	                                                    G_MAXUINT,
+	                                                    300,
+	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_SCAN_BATCH_SIZE,
+	                                 g_param_spec_uint ("scan-batch-size",
+	                                                    _("Scan Batch Size"),
+	                                                    _("Number of lines scanned in one batch"),
+	                                                    1,
+	                                                    G_MAXUINT,
+	                                                    20,
+	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	
+	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionWordsPrivate));
+}
+
+static gboolean
+gtk_source_completion_words_get_start_iter (GtkSourceCompletionProvider *provider,
+                                            GtkSourceCompletionProposal *proposal,
+                                            GtkTextIter                 *iter)
+{
+	//GtkSourceCompletionWords *words = GTK_SOURCE_COMPLETION_WORDS (provider);
+
+	/* TODO: implement */
+	return FALSE;
+}
+
+static void
+gtk_source_completion_words_iface_init (GtkSourceCompletionProviderIface *iface)
+{
+	iface->get_name = gtk_source_completion_words_get_name;
+	iface->get_icon = gtk_source_completion_words_get_icon;
+
+	iface->populate = gtk_source_completion_words_populate;
+	iface->match = gtk_source_completion_words_match;
+
+	iface->get_start_iter = gtk_source_completion_words_get_start_iter;
+}
+
+static void 
+gtk_source_completion_words_init (GtkSourceCompletionWords *self)
+{	
+	self->priv = GTK_SOURCE_COMPLETION_WORDS_GET_PRIVATE (self);
+	
+	self->priv->library = gtk_source_completion_words_library_new ();
+}
+
+GtkSourceCompletionWords *
+gtk_source_completion_words_new (gchar const *name,
+                                 GdkPixbuf   *icon)
+{
+	return g_object_new (GSC_TYPE_PROVIDER_WORDS, 
+	                     "name", name, 
+	                     "icon", icon, 
+	                     NULL);
+}
+
+void
+gtk_source_completion_words_register (GtkSourceCompletionWords *words,
+                                      GtkTextBuffer            *buffer)
+{
+	GtkSourceCompletionWordsBuffer *buf;
+
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS (words));
+	g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+	
+	buf = g_object_get_data (G_OBJECT (buffer), BUFFER_KEY);
+	
+	if (buf != NULL)
+	{
+		return;
+	}
+	
+	buf = gtk_source_completion_words_buffer_new (words->priv->library,
+	                                              buffer,
+	                                              words->priv->scan_batch_size);
+
+	g_object_set_data (G_OBJECT (buf), BUFFER_KEY, buffer);
+	words->priv->buffers = g_list_prepend (words->priv->buffers, buf);
+}
+
+void
+gtk_source_completion_words_unregister (GtkSourceCompletionWords *words,
+                                        GtkTextBuffer            *buffer)
+{
+	GtkSourceCompletionWordsBuffer *buf;
+	
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS (words));
+	g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+	
+	buf = g_object_get_data (G_OBJECT (buffer), BUFFER_KEY);
+	
+	if (buf != NULL)
+	{
+		remove_buffer (buf);
+		words->priv->buffers = g_list_remove (words->priv->buffers, buf);
+	}
+}
+
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwords.h b/gtksourceview/completion-providers/words/gtksourcecompletionwords.h
new file mode 100644
index 0000000..02de97a
--- /dev/null
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwords.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecompletionproviderwords.h
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * gtksourceview 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.
+ *
+ * gtksourceview is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gtksourceview; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_COMPLETION_WORDS_H__
+#define __GTK_SOURCE_COMPLETION_WORDS_H__
+
+#include <gtksourceview/gtksourcecompletionprovider.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GSC_TYPE_PROVIDER_WORDS				(gtk_source_completion_words_get_type ())
+#define GTK_SOURCE_COMPLETION_WORDS(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GSC_TYPE_PROVIDER_WORDS, GtkSourceCompletionWords))
+#define GTK_SOURCE_COMPLETION_WORDS_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GSC_TYPE_PROVIDER_WORDS, GtkSourceCompletionWords const))
+#define GTK_SOURCE_COMPLETION_WORDS_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GSC_TYPE_PROVIDER_WORDS, GtkSourceCompletionWordsClass))
+#define GTK_IS_SOURCE_COMPLETION_WORDS(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSC_TYPE_PROVIDER_WORDS))
+#define GTK_IS_SOURCE_COMPLETION_WORDS_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GSC_TYPE_PROVIDER_WORDS))
+#define GTK_SOURCE_COMPLETION_WORDS_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GSC_TYPE_PROVIDER_WORDS, GtkSourceCompletionWordsClass))
+
+typedef struct _GtkSourceCompletionWords		GtkSourceCompletionWords;
+typedef struct _GtkSourceCompletionWordsClass		GtkSourceCompletionWordsClass;
+typedef struct _GtkSourceCompletionWordsPrivate		GtkSourceCompletionWordsPrivate;
+
+struct _GtkSourceCompletionWords {
+	GObject parent;
+	
+	GtkSourceCompletionWordsPrivate *priv;
+};
+
+struct _GtkSourceCompletionWordsClass {
+	GObjectClass parent_class;
+};
+
+GType gtk_source_completion_words_get_type (void) G_GNUC_CONST;
+
+GtkSourceCompletionWords *
+		gtk_source_completion_words_new 	(gchar const              *name,
+		                                         GdkPixbuf                *icon);
+		
+void 		gtk_source_completion_words_register 	(GtkSourceCompletionWords *words,
+                                                         GtkTextBuffer            *buffer);
+
+void 		gtk_source_completion_words_unregister 	(GtkSourceCompletionWords *words,
+                                                         GtkTextBuffer            *buffer);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_COMPLETION_WORDS_H__ */
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwordsbuffer.c b/gtksourceview/completion-providers/words/gtksourcecompletionwordsbuffer.c
new file mode 100644
index 0000000..17ba908
--- /dev/null
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwordsbuffer.c
@@ -0,0 +1,701 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecompletionwordsbuffer.c
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * gtksourceview 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.
+ *
+ * gtksourceview is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gtksourceview; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#include "gtksourcecompletionwordsbuffer.h"
+
+#define GTK_SOURCE_COMPLETION_WORDS_BUFFER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER, GtkSourceCompletionWordsBufferPrivate))
+
+#define REGION_FROM_LIST(list) ((ScanRegion *)list->data)
+
+typedef struct
+{
+	guint start;
+	guint end;
+} ScanRegion;
+
+enum
+{
+	EXT_INSERT_TEXT,
+	EXT_INSERT_TEXT_AFTER,
+	EXT_DELETE_RANGE,
+	EXT_DELETE_RANGE_AFTER,
+	NUM_EXT_SIGNALS
+};
+
+struct _GtkSourceCompletionWordsBufferPrivate
+{
+	GtkSourceCompletionWordsLibrary *library;
+	GtkTextBuffer *buffer;
+	
+	GList *lines;
+	gint current_insert_line;
+	
+	GList *scan_regions;
+	guint idle_scan_id;
+	
+	guint ext_signal_handlers[NUM_EXT_SIGNALS];
+	guint scan_batch_size;
+	
+	guint lock_handler_id;
+	guint unlock_handler_id;
+};
+
+G_DEFINE_TYPE (GtkSourceCompletionWordsBuffer, gtk_source_completion_words_buffer, G_TYPE_OBJECT)
+
+static ScanRegion *
+scan_region_new (gint start,
+                 gint end)
+{
+	ScanRegion *region = (ScanRegion *)g_slice_new (ScanRegion);
+	
+	region->start = start;
+	region->end = end;
+	
+	return region;
+}
+
+static void
+scan_region_free (ScanRegion *region)
+{
+	g_slice_free (ScanRegion, region);
+}
+
+static void
+remove_proposal (GtkSourceCompletionWordsProposal *proposal,
+                 GtkSourceCompletionWordsBuffer   *buffer)
+{
+	gtk_source_completion_words_library_remove_word (buffer->priv->library,
+	                                                 proposal);
+}
+
+static void
+remove_line (GList                          *line,
+             GtkSourceCompletionWordsBuffer *buffer)
+{
+	g_list_foreach (line, (GFunc)remove_proposal, buffer);
+	g_list_free (line);
+}
+
+static void
+gtk_source_completion_words_buffer_dispose (GObject *object)
+{
+	GtkSourceCompletionWordsBuffer *buffer =
+			GTK_SOURCE_COMPLETION_WORDS_BUFFER (object);
+
+	if (buffer->priv->library)
+	{
+		g_signal_handler_disconnect (buffer->priv->library,
+		                             buffer->priv->lock_handler_id);
+		g_signal_handler_disconnect (buffer->priv->library,
+		                             buffer->priv->unlock_handler_id);
+
+		g_object_unref (buffer->priv->library);
+		buffer->priv->library = NULL;
+	}
+	
+	if (buffer->priv->buffer)
+	{
+		gint i;
+		
+		for (i = 0; i < NUM_EXT_SIGNALS; ++i)
+		{
+			g_signal_handler_disconnect (buffer->priv->buffer,
+			                             buffer->priv->ext_signal_handlers[i]);
+		}
+		
+		g_object_unref (buffer->priv->buffer);
+		buffer->priv->buffer = NULL;
+	}
+	
+	if (buffer->priv->idle_scan_id)
+	{
+		g_source_remove (buffer->priv->idle_scan_id);
+		buffer->priv->idle_scan_id = 0;
+	}
+	
+	g_list_foreach (buffer->priv->scan_regions, (GFunc)scan_region_free, NULL);
+	g_list_free (buffer->priv->scan_regions);
+	
+	buffer->priv->scan_regions = NULL;
+	
+	g_list_foreach (buffer->priv->lines, (GFunc)remove_line, buffer);
+	g_list_free (buffer->priv->lines);
+
+	buffer->priv->lines = NULL;
+
+	G_OBJECT_CLASS (gtk_source_completion_words_buffer_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_completion_words_buffer_class_init (GtkSourceCompletionWordsBufferClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = gtk_source_completion_words_buffer_dispose;
+
+	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionWordsBufferPrivate));
+}
+
+static void
+gtk_source_completion_words_buffer_init (GtkSourceCompletionWordsBuffer *self)
+{
+	self->priv = GTK_SOURCE_COMPLETION_WORDS_BUFFER_GET_PRIVATE (self);
+}
+
+static void
+on_insert_text_cb (GtkTextBuffer                  *textbuffer,
+                   GtkTextIter                    *location,
+                   gchar const                    *text,
+                   gint                            len,
+                   GtkSourceCompletionWordsBuffer *buffer)
+{
+	buffer->priv->current_insert_line = gtk_text_iter_get_line (location);
+}
+
+static gboolean
+is_word_char (gunichar ch)
+{
+	return g_unichar_isprint (ch) && 
+	       (g_unichar_isalnum (ch) || ch == g_utf8_get_char ("_"));
+}
+
+static gboolean
+iter_at_word_start (GtkTextIter *iter)
+{
+	GtkTextIter prev = *iter;
+	
+	if (!gtk_text_iter_starts_word (iter) ||
+	    g_unichar_isdigit (gtk_text_iter_get_char (iter)))
+	{
+		return FALSE;
+	}
+	
+	if (!gtk_text_iter_is_start (&prev) && 
+	    !gtk_text_iter_starts_line (&prev))
+	{
+		gtk_text_iter_backward_char (&prev);
+		
+		return !is_word_char (gtk_text_iter_get_char (&prev));
+	}
+	else
+	{
+		return TRUE;
+	}
+}
+
+static gboolean
+iter_at_word_end (GtkTextIter *iter)
+{
+	if (!gtk_text_iter_ends_word (iter) || 
+	    g_unichar_isdigit (gtk_text_iter_get_char (iter)))
+	{
+		return FALSE;
+	}
+	
+	if (!gtk_text_iter_is_end (iter) && 
+	    !gtk_text_iter_ends_line (iter))
+	{
+		return !is_word_char (gtk_text_iter_get_char (iter));
+	}
+	else
+	{
+		return TRUE;
+	}
+}
+
+static GList *
+scan_line (GtkSourceCompletionWordsBuffer *buffer,
+           gint                            line)
+{
+	GtkTextIter start;
+	GtkTextIter end;
+	GList *ret = NULL;
+	
+	gtk_text_buffer_get_iter_at_line (buffer->priv->buffer, &start, line);
+	
+	while (gtk_text_iter_get_line (&start) == line)
+	{
+		gchar *word;
+		
+		/* Forward start to next word start */
+		while (!iter_at_word_start (&start) && 
+		       !gtk_text_iter_ends_line (&start))
+		{
+			if (!gtk_text_iter_forward_char (&start))
+			{
+				break;
+			}
+		}
+		
+		if (gtk_text_iter_ends_line (&start))
+		{
+			break;
+		}
+		
+		end = start;
+		
+		if (!gtk_text_iter_forward_char (&end))
+		{
+			break;
+		}
+		
+		/* Forward end to next word end */
+		while (!iter_at_word_end (&end))
+		{
+			if (!gtk_text_iter_forward_char (&end))
+			{
+				break;
+			}
+		}
+		
+		if (gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&start) > 2)
+		{
+			GtkSourceCompletionWordsProposal *proposal;
+			
+			word = gtk_text_iter_get_text (&start, &end);
+			proposal = gtk_source_completion_words_library_add_word (buffer->priv->library,
+			                                                         word);
+			
+			if (proposal != NULL)
+			{
+				ret = g_list_prepend (ret, proposal);
+				g_free (word);
+			}
+		}
+		
+		start = end;
+		
+		if (!gtk_text_iter_forward_char (&start))
+		{
+			break;
+		}
+	}
+	
+	return g_list_reverse (ret);
+}
+
+static gboolean
+idle_scan_regions (GtkSourceCompletionWordsBuffer *buffer)
+{
+	guint num = 0;
+	GList *ptr = buffer->priv->lines;
+	guint prev = 0;
+	gboolean finished;
+	
+	/* Scan a few lines */
+	while (buffer->priv->scan_regions)
+	{
+		ScanRegion *region = REGION_FROM_LIST (buffer->priv->scan_regions);
+
+		gint span = (region->end - region->start) + 1;
+		gint doscan = MIN(buffer->priv->scan_batch_size - num, span);
+		gint i;
+		
+		ptr = g_list_nth (ptr, region->start - prev);
+		
+		for (i = 0; i < doscan; ++i)
+		{
+			/* First remove this line */
+			remove_line ((GList *)ptr->data, buffer);
+			
+			/* Then scan it which adds words */
+			ptr->data = scan_line (buffer, region->start + i);
+
+			ptr = g_list_next (ptr);
+		}
+		
+		prev = region->start + doscan;
+		
+		if (doscan == span)
+		{
+			/* Simply remove the region */
+			scan_region_free (region);
+
+			buffer->priv->scan_regions = 
+				g_list_delete_link (buffer->priv->scan_regions,
+				                    buffer->priv->scan_regions);
+		}
+		else
+		{
+			/* Update the region and break */
+			region->start = region->start + doscan;
+			break;
+		}
+		
+		num += doscan;
+	}
+
+	finished = buffer->priv->scan_regions == NULL;
+	
+	if (finished)
+	{
+		buffer->priv->idle_scan_id = 0;
+	}
+	
+	return !finished;
+}
+
+static void
+remove_scan_regions (GtkSourceCompletionWordsBuffer *buffer,
+                     gint                            start,
+                     gint                            end)
+{
+	GList *item;
+	gint span = end - start + 1;
+	
+	for (item = buffer->priv->scan_regions; item; item = g_list_next (item))
+	{
+		ScanRegion *region = REGION_FROM_LIST (item);
+		
+		if (region->start >= start && region->end <= end)
+		{
+			GList *remove = item;
+			scan_region_free (region);
+
+			item = g_list_previous (item);
+			
+			/* Remove whole thing */
+			buffer->priv->scan_regions = 
+				g_list_delete_link (buffer->priv->scan_regions,
+				                    remove);
+		}
+		else if (region->start >= start)
+		{
+			/* Start in region */
+			region->end = start;
+		}
+		else if (region->end <= end)
+		{
+			/* End in region */
+			region->start = start;
+			region->end -= span;
+		}
+		else if (region->start > start)
+		{
+			region->start -= span;
+			region->end -= span;
+		}
+	}
+}
+
+static void
+remove_range (GtkSourceCompletionWordsBuffer *buffer,
+              gint                            start,
+              gint                            end)
+{
+	if (start > end)
+	{
+		return;
+	}
+	
+	remove_scan_regions (buffer, start, end);
+
+	GList *line = g_list_nth (buffer->priv->lines, start);
+	
+	while (start <= end && line)
+	{
+		GList *cur = line;
+		
+		/* Remove proposals */
+		remove_line ((GList *)line->data, buffer);
+		line = g_list_next (cur);
+
+		buffer->priv->lines = g_list_delete_link (buffer->priv->lines,
+		                                          cur);
+		++start;
+	}
+}
+
+static void
+add_scan_region (GtkSourceCompletionWordsBuffer *buffer,
+                 gint                            start,
+                 gint                            end)
+{
+	GList *item;
+	GList *merge_start = NULL;
+	gint line_count = gtk_text_buffer_get_line_count (buffer->priv->buffer);
+	
+	if (end >= line_count)
+	{
+		end = line_count - 1;
+	}
+	
+	if (start > end)
+	{
+		return;
+	}
+	
+	for (item = buffer->priv->scan_regions; item; item = g_list_next (item))
+	{
+		ScanRegion *region = (ScanRegion *)item->data;
+		
+		if (merge_start != NULL)
+		{
+			if (end < region->start)
+			{
+				break;
+			}
+		}
+		else if (start >= region->start)
+		{
+			merge_start = item;
+		}
+	}
+	
+	if (merge_start == NULL)
+	{
+		/* Simply prepend */
+		buffer->priv->scan_regions = 
+			g_list_prepend (buffer->priv->scan_regions,
+			                scan_region_new (start, end));
+	}
+	else
+	{
+		GList *insert_at;
+
+		if (end < REGION_FROM_LIST (merge_start)->start)
+		{
+			/* In this case, merge from next(merge_start) to item */
+			merge_start = g_list_next (merge_start);
+		}
+		
+		insert_at = g_list_previous (merge_start);
+		
+		start = MIN(start, REGION_FROM_LIST (merge_start)->start);
+		
+		if (item)
+		{
+			end = MIN(end, REGION_FROM_LIST (item)->end);
+		}
+		
+		while (merge_start != item)
+		{
+			GList *tmp = merge_start;
+			
+			merge_start = g_list_next (merge_start);
+			scan_region_free (REGION_FROM_LIST (tmp));
+
+			buffer->priv->scan_regions = 
+				g_list_delete_link (buffer->priv->scan_regions,
+				                    tmp);
+		}
+		
+		/* Insert new region */
+		insert_at = g_list_next (insert_at);
+		
+		if (insert_at != NULL)
+		{
+			buffer->priv->scan_regions = 
+				g_list_insert_before (buffer->priv->scan_regions,
+				                      insert_at,
+				                      scan_region_new (start, end));
+		}
+		else
+		{
+			buffer->priv->scan_regions = 
+					g_list_append (buffer->priv->scan_regions,
+						       scan_region_new (start, end));
+		}
+	}
+	
+	if (buffer->priv->idle_scan_id == 0 && 
+	    !gtk_source_completion_words_library_is_locked (buffer->priv->library))
+	{
+		buffer->priv->idle_scan_id = 
+			g_idle_add ((GSourceFunc)idle_scan_regions,
+			            buffer);
+	}
+}
+
+static void
+handle_text_inserted (GtkSourceCompletionWordsBuffer *buffer,
+                      gint                            start,
+                      gint                            end)
+{
+	gint pos = start;
+	GList *ptr = NULL;
+	
+	if (start > end)
+	{
+		ptr = g_list_nth (buffer->priv->lines,
+		                  start + 1);
+	}
+	
+	while (pos < end)
+	{
+		/* Insert new empty lines */
+		if (ptr == NULL)
+		{
+			buffer->priv->lines = 
+				g_list_append (buffer->priv->lines,
+				               NULL);
+		}
+		else
+		{
+			buffer->priv->lines = 
+				g_list_insert_before (buffer->priv->lines,
+					              ptr,
+					              NULL);
+		}
+
+		++pos;
+	}
+	
+	/* Invalidate new region */
+	add_scan_region (buffer, 
+	                 start,
+	                 end);
+}
+
+static void
+on_insert_text_after_cb (GtkTextBuffer                  *text_buffer,
+                         GtkTextIter                    *location,
+                         gchar const                    *text,
+                         gint                            len,
+                         GtkSourceCompletionWordsBuffer *buffer)
+{
+	handle_text_inserted (buffer,
+	                      buffer->priv->current_insert_line,
+	                      gtk_text_iter_get_line (location));
+}
+
+static void
+on_delete_range_cb (GtkTextBuffer                  *text_buffer,
+                    GtkTextIter                    *start,
+                    GtkTextIter                    *end,
+                    GtkSourceCompletionWordsBuffer *buffer)
+{
+	gint start_line = gtk_text_iter_get_line (start);
+	gint end_line = gtk_text_iter_get_line (end);
+	
+	/* Simply remove everything from lines start + 1 to end */
+	remove_range (buffer, start_line + 1, end_line);
+}
+
+static void
+on_delete_range_after_cb (GtkTextBuffer                  *text_buffer,
+                          GtkTextIter                    *start,
+                          GtkTextIter                    *end,
+                          GtkSourceCompletionWordsBuffer *buffer)
+{
+	gint start_line = gtk_text_iter_get_line (start);
+	
+	/* Add start line to scan regions */
+	add_scan_region (buffer, start_line, start_line);
+}
+
+static void
+connect_buffer (GtkSourceCompletionWordsBuffer *buffer)
+{
+	buffer->priv->ext_signal_handlers[EXT_INSERT_TEXT] =
+		g_signal_connect (buffer->priv->buffer,
+			          "insert-text",
+			          G_CALLBACK (on_insert_text_cb),
+			          buffer);
+
+	buffer->priv->ext_signal_handlers[EXT_INSERT_TEXT_AFTER] =
+		g_signal_connect_after (buffer->priv->buffer,
+			                "insert-text",
+			                G_CALLBACK (on_insert_text_after_cb),
+			                buffer);
+
+	buffer->priv->ext_signal_handlers[EXT_DELETE_RANGE] =
+		g_signal_connect (buffer->priv->buffer,
+			          "delete-range",
+			          G_CALLBACK (on_delete_range_cb),
+			          buffer);
+
+	buffer->priv->ext_signal_handlers[EXT_DELETE_RANGE_AFTER] =
+		g_signal_connect_after (buffer->priv->buffer,
+			                "delete-range",
+			                G_CALLBACK (on_delete_range_after_cb),
+			                buffer);
+
+	/* Start initial scan */
+	handle_text_inserted (buffer,
+	                      0,
+	                      gtk_text_buffer_get_line_count (buffer->priv->buffer));
+}
+
+static void
+on_library_lock (GtkSourceCompletionWordsBuffer *buffer)
+{
+	if (buffer->priv->idle_scan_id != 0)
+	{
+		g_source_remove (buffer->priv->idle_scan_id);
+		buffer->priv->idle_scan_id = 0;
+	}
+}
+
+static void
+on_library_unlock (GtkSourceCompletionWordsBuffer *buffer)
+{
+	if (buffer->priv->idle_scan_id == 0 &&
+	    buffer->priv->scan_regions != NULL)
+	{
+		buffer->priv->idle_scan_id = 
+			g_idle_add ((GSourceFunc)idle_scan_regions,
+			            buffer);
+	}
+}
+
+GtkSourceCompletionWordsBuffer *
+gtk_source_completion_words_buffer_new (GtkSourceCompletionWordsLibrary *library,
+                                        GtkTextBuffer                   *buffer,
+                                        guint                            scan_batch_size)
+{
+	GtkSourceCompletionWordsBuffer *ret;
+	
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY (library), NULL);
+	g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+	g_return_val_if_fail (scan_batch_size > 0, NULL);
+	
+	ret = g_object_new (GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER, NULL);
+
+	ret->priv->library = g_object_ref (library);
+	ret->priv->buffer = g_object_ref (buffer);
+	ret->priv->scan_batch_size = scan_batch_size;
+	
+	ret->priv->lock_handler_id =
+		g_signal_connect_swapped (ret->priv->library,
+			                  "lock",
+			                  G_CALLBACK (on_library_lock),
+			                  ret);
+
+	ret->priv->unlock_handler_id =
+		g_signal_connect_swapped (ret->priv->library,
+			                  "unlock",
+			                  G_CALLBACK (on_library_unlock),
+			                  ret);
+	
+	connect_buffer (ret);
+
+	return ret;
+}
+
+GtkTextBuffer *
+gtk_source_completion_words_buffer_get_buffer (GtkSourceCompletionWordsBuffer *buffer)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_BUFFER (buffer), NULL);
+	
+	return buffer->priv->buffer;
+}
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwordsbuffer.h b/gtksourceview/completion-providers/words/gtksourcecompletionwordsbuffer.h
new file mode 100644
index 0000000..775fd0b
--- /dev/null
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwordsbuffer.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecompletionwordsbuffer.h
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * gtksourceview 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.
+ *
+ * gtksourceview is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gtksourceview; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_COMPLETION_WORDS_BUFFER_H__
+#define __GTK_SOURCE_COMPLETION_WORDS_BUFFER_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gtksourcecompletionwordslibrary.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER			(gtk_source_completion_words_buffer_get_type ())
+#define GTK_SOURCE_COMPLETION_WORDS_BUFFER(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER, GtkSourceCompletionWordsBuffer))
+#define GTK_SOURCE_COMPLETION_WORDS_BUFFER_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER, GtkSourceCompletionWordsBuffer const))
+#define GTK_SOURCE_COMPLETION_WORDS_BUFFER_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER, GtkSourceCompletionWordsBufferClass))
+#define GTK_IS_SOURCE_COMPLETION_WORDS_BUFFER(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER))
+#define GTK_IS_SOURCE_COMPLETION_WORDS_BUFFER_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER))
+#define GTK_SOURCE_COMPLETION_WORDS_BUFFER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_BUFFER, GtkSourceCompletionWordsBufferClass))
+
+typedef struct _GtkSourceCompletionWordsBuffer			GtkSourceCompletionWordsBuffer;
+typedef struct _GtkSourceCompletionWordsBufferClass		GtkSourceCompletionWordsBufferClass;
+typedef struct _GtkSourceCompletionWordsBufferPrivate		GtkSourceCompletionWordsBufferPrivate;
+
+struct _GtkSourceCompletionWordsBuffer {
+	GObject parent;
+	
+	GtkSourceCompletionWordsBufferPrivate *priv;
+};
+
+struct _GtkSourceCompletionWordsBufferClass {
+	GObjectClass parent_class;
+};
+
+GType gtk_source_completion_words_buffer_get_type (void) G_GNUC_CONST;
+
+GtkSourceCompletionWordsBuffer *
+		 gtk_source_completion_words_buffer_new 	(GtkSourceCompletionWordsLibrary *library,
+								 GtkTextBuffer                   *buffer,
+								 guint                            process_batch);
+
+GtkTextBuffer 	*gtk_source_completion_words_buffer_get_buffer	(GtkSourceCompletionWordsBuffer  *buffer);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_COMPLETION_WORDS_BUFFER_H__ */
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwordslibrary.c b/gtksourceview/completion-providers/words/gtksourcecompletionwordslibrary.c
new file mode 100644
index 0000000..458c04e
--- /dev/null
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwordslibrary.c
@@ -0,0 +1,352 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecompletionwordslibrary.c
+ * This file is part of gtksourceview 
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * gtksourceview is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * gtksourceview is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "gtksourcecompletionwordslibrary.h"
+
+#include <string.h>
+
+#define GTK_SOURCE_COMPLETION_WORDS_LIBRARY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GTK_TYPE_SOURCE_COMPLETION_WORDS_LIBRARY, GtkSourceCompletionWordsLibraryPrivate))
+
+enum
+{
+	LOCK,
+	UNLOCK,
+	NUM_SIGNALS
+};
+
+struct _GtkSourceCompletionWordsLibraryPrivate
+{
+	GSequence *store;
+	gboolean locked;
+};
+
+static guint signals[NUM_SIGNALS] = {0,};
+
+G_DEFINE_TYPE (GtkSourceCompletionWordsLibrary, gtk_source_completion_words_library, G_TYPE_OBJECT)
+
+static void
+gtk_source_completion_words_library_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (gtk_source_completion_words_library_parent_class)->finalize (object);
+}
+
+static void
+gtk_source_completion_words_library_class_init (GtkSourceCompletionWordsLibraryClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	
+	object_class->finalize = gtk_source_completion_words_library_finalize;
+
+	signals[LOCK] =
+		g_signal_new ("lock",
+		              G_TYPE_FROM_CLASS (klass),
+		              G_SIGNAL_RUN_LAST,
+		              0,
+		              NULL, 
+		              NULL,
+		              g_cclosure_marshal_VOID__VOID, 
+		              G_TYPE_NONE,
+		              0);
+	
+	signals[UNLOCK] =
+		g_signal_new ("unlock",
+		              G_TYPE_FROM_CLASS (klass),
+		              G_SIGNAL_RUN_LAST,
+		              0,
+		              NULL, 
+		              NULL,
+		              g_cclosure_marshal_VOID__VOID, 
+		              G_TYPE_NONE,
+		              0);
+
+	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionWordsLibraryPrivate));
+}
+
+static void
+gtk_source_completion_words_library_init (GtkSourceCompletionWordsLibrary *self)
+{
+	self->priv = GTK_SOURCE_COMPLETION_WORDS_LIBRARY_GET_PRIVATE (self);
+	
+	self->priv->store = g_sequence_new ((GDestroyNotify)g_object_unref);
+}
+
+GtkSourceCompletionWordsLibrary *
+gtk_source_completion_words_library_new (void)
+{
+	return g_object_new (GTK_TYPE_SOURCE_COMPLETION_WORDS_LIBRARY, NULL);
+}
+
+static gint
+compare_two_items (GtkSourceCompletionWordsProposal *a,
+                   GtkSourceCompletionWordsProposal *b,
+                   gpointer                          data)
+{
+	return strcmp (gtk_source_completion_words_proposal_get_word (a),
+	               gtk_source_completion_words_proposal_get_word (b));
+}
+
+static gint
+compare_items (GtkSourceCompletionWordsProposal *a,
+               GtkSourceCompletionWordsProposal *b,
+               gchar const                      *word)
+{
+	gchar const *m1 = 
+		gtk_source_completion_words_proposal_get_word (a == NULL ? b : a);
+	
+	return strcmp (m1, word);
+}
+
+static gboolean
+iter_match_prefix (GSequenceIter *iter,
+                   gchar const   *word,
+                   gint           len)
+{
+	GtkSourceCompletionWordsProposal *item;
+	
+	item = gtk_source_completion_words_library_get_proposal (iter);
+	
+	return strncmp (gtk_source_completion_words_proposal_get_word (item),
+	                word, 
+	                len != -1 ? len : strlen (word)) == 0;
+}
+
+GtkSourceCompletionWordsProposal *
+gtk_source_completion_words_library_get_proposal (GSequenceIter *iter)
+{
+	if (iter == NULL)
+	{
+		return NULL;
+	}
+	
+	return GTK_SOURCE_COMPLETION_WORDS_PROPOSAL (g_sequence_get (iter));
+}
+
+GSequenceIter *
+gtk_source_completion_words_library_find_first (GtkSourceCompletionWordsLibrary *library,
+                                                gchar const                     *word,
+                                                gint                             len)
+{
+	GSequenceIter *iter;
+	GSequenceIter *prev;
+	
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY (library), NULL);
+	g_return_val_if_fail (word != NULL, NULL);
+
+	iter = g_sequence_search (library->priv->store,
+	                          NULL,
+	                          (GCompareDataFunc)compare_items,
+	                          (gpointer)word);
+
+	if (iter == NULL)
+	{
+		return NULL;
+	}
+	
+	if (len == -1)
+	{
+		len = strlen (word);
+	}
+	
+	/* Test if this position might be after the last match */
+	if (!g_sequence_iter_is_begin (iter) && 
+	    (g_sequence_iter_is_end (iter) || 
+	     !iter_match_prefix (iter, word, len)))
+	{
+		iter = g_sequence_iter_prev (iter);
+	
+		/* Maybe there is actually nothing in the sequence */
+		if (g_sequence_iter_is_end (iter) || 
+		    !iter_match_prefix (iter, word, len))
+		{
+			return NULL;
+		}
+	}
+	
+	if (g_sequence_iter_is_end (iter))
+	{
+		return NULL;
+	}
+	
+	/* Go back while it matches */
+	while (iter &&
+	       (prev = g_sequence_iter_prev (iter)) && 
+	       iter_match_prefix (prev, word, len))
+	{
+		iter = prev;
+		
+		if (g_sequence_iter_is_begin (iter))
+		{
+			break;
+		}
+	}
+	
+	return iter;
+}
+
+GSequenceIter *
+gtk_source_completion_words_library_find_next (GSequenceIter *iter,
+                                               gchar const   *word,
+                                               gint           len)
+{
+	g_return_val_if_fail (iter != NULL, NULL);
+	g_return_val_if_fail (word != NULL, NULL);
+
+	iter = g_sequence_iter_next (iter);
+
+	if (!iter || g_sequence_iter_is_end (iter))
+	{
+		return NULL;
+	}
+
+	return iter_match_prefix (iter, word, len) ? iter : NULL;
+}
+
+GSequenceIter *
+gtk_source_completion_words_library_find (GtkSourceCompletionWordsLibrary  *library,
+					  GtkSourceCompletionWordsProposal *proposal)
+{
+	GSequenceIter *iter;
+	GtkSourceCompletionWordsProposal *other;
+	gchar const *word = gtk_source_completion_words_proposal_get_word (proposal);
+	gint len = strlen (word);
+	
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY (library), NULL);
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_PROPOSAL (proposal), NULL);
+	
+	iter = gtk_source_completion_words_library_find_first (library, word, len);
+	
+	if (!iter)
+	{
+		return NULL;
+	}
+	
+	do
+	{
+		other = gtk_source_completion_words_library_get_proposal (iter);
+		
+		if (proposal == other)
+		{
+			return iter;
+		}
+		
+		iter = g_sequence_iter_next (iter);
+	} while (!g_sequence_iter_is_end (iter) &&
+	         strcmp (gtk_source_completion_words_proposal_get_word (other),
+	                 word) == 0);
+
+	return NULL;
+}
+
+static void
+on_proposal_unused (GtkSourceCompletionWordsProposal *proposal,
+                    GtkSourceCompletionWordsLibrary  *library)
+{
+	GSequenceIter *iter = gtk_source_completion_words_library_find (library, 
+	                                                                proposal);
+	
+	if (iter != NULL)
+	{
+		g_sequence_remove (iter);
+	}
+}
+
+GtkSourceCompletionWordsProposal *
+gtk_source_completion_words_library_add_word (GtkSourceCompletionWordsLibrary *library,
+                                              gchar const                     *word)
+{
+	GtkSourceCompletionWordsProposal *proposal;
+	GSequenceIter *iter;
+	
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY (library), NULL);
+	g_return_val_if_fail (word != NULL, NULL);
+	
+	/* Check if word already exists */
+	iter = gtk_source_completion_words_library_find_first (library, word, -1);
+	
+	if (iter)
+	{
+		proposal = gtk_source_completion_words_library_get_proposal (iter);
+		
+		if (strcmp (gtk_source_completion_words_proposal_get_word (proposal),
+		            word) == 0)
+		{
+			/* Already exists, increase the use count */
+			gtk_source_completion_words_proposal_use (proposal);
+			return proposal;
+		}
+	}
+	
+	if (library->priv->locked)
+	{
+		return NULL;
+	}
+
+	proposal = gtk_source_completion_words_proposal_new (word);
+	
+	g_signal_connect (proposal,
+	                  "unused",
+	                  G_CALLBACK (on_proposal_unused),
+	                  library);
+	
+	/* Insert proposal into binary tree of words */
+	g_sequence_insert_sorted (library->priv->store,
+				  proposal,
+				  (GCompareDataFunc)compare_two_items,
+				  NULL);
+
+	return proposal;
+}
+
+void
+gtk_source_completion_words_library_remove_word (GtkSourceCompletionWordsLibrary  *library,
+                                                 GtkSourceCompletionWordsProposal *proposal)
+{	
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY (library));
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_PROPOSAL (proposal));
+
+	gtk_source_completion_words_proposal_unuse (proposal);
+}
+
+void
+gtk_source_completion_words_library_lock (GtkSourceCompletionWordsLibrary *library)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY (library));
+	
+	library->priv->locked = TRUE;
+	g_signal_emit (library, signals[LOCK], 0);
+}
+
+void
+gtk_source_completion_words_library_unlock (GtkSourceCompletionWordsLibrary *library)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY (library));
+	
+	library->priv->locked = FALSE;
+	g_signal_emit (library, signals[UNLOCK], 0);
+}
+
+gboolean
+gtk_source_completion_words_library_is_locked (GtkSourceCompletionWordsLibrary *library)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY (library), TRUE);
+	
+	return library->priv->locked;
+}
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwordslibrary.h b/gtksourceview/completion-providers/words/gtksourcecompletionwordslibrary.h
new file mode 100644
index 0000000..544f377
--- /dev/null
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwordslibrary.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecompletionwordslibrary.h
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * gtksourceview 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.
+ *
+ * gtksourceview is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gtksourceview; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_COMPLETION_WORDS_LIBRARY_H__
+#define __GTK_SOURCE_COMPLETION_WORDS_LIBRARY_H__
+
+#include <glib-object.h>
+#include "gtksourcecompletionwordsproposal.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_COMPLETION_WORDS_LIBRARY			(gtk_source_completion_words_library_get_type ())
+#define GTK_SOURCE_COMPLETION_WORDS_LIBRARY(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_COMPLETION_PROVIDER_WORDS_LIBRARY, GtkSourceCompletionWordsLibrary))
+#define GTK_SOURCE_COMPLETION_WORDS_LIBRARY_CONST(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_LIBRARY, GtkSourceCompletionWordsLibrary const))
+#define GTK_SOURCE_COMPLETION_WORDS_LIBRARY_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SOURCE_COMPLETION_WORDS_LIBRARY, GtkSourceCompletionWordsLibraryClass))
+#define GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY(obj)			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_LIBRARY))
+#define GTK_IS_SOURCE_COMPLETION_WORDS_LIBRARY_CLASS(klass)		(G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_COMPLETION_WORDS_LIBRARY))
+#define GTK_SOURCE_COMPLETION_WORDS_LIBRARY_GET_CLASS(obj)		(G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_LIBRARY, GtkSourceCompletionWordsLibraryClass))
+
+typedef struct _GtkSourceCompletionWordsLibrary			GtkSourceCompletionWordsLibrary;
+typedef struct _GtkSourceCompletionWordsLibraryClass		GtkSourceCompletionWordsLibraryClass;
+typedef struct _GtkSourceCompletionWordsLibraryPrivate		GtkSourceCompletionWordsLibraryPrivate;
+
+struct _GtkSourceCompletionWordsLibrary {
+	GObject parent;
+
+	GtkSourceCompletionWordsLibraryPrivate *priv;
+};
+
+struct _GtkSourceCompletionWordsLibraryClass {
+	GObjectClass parent_class;
+};
+
+GType gtk_source_completion_words_library_get_type (void) G_GNUC_CONST;
+
+GtkSourceCompletionWordsLibrary *
+		gtk_source_completion_words_library_new			(void);
+
+/* Finding */
+GSequenceIter	*gtk_source_completion_words_library_find 		(GtkSourceCompletionWordsLibrary  *library,
+									 GtkSourceCompletionWordsProposal *proposal);
+GSequenceIter	*gtk_source_completion_words_library_find_first		(GtkSourceCompletionWordsLibrary  *library,
+									 gchar const                      *word,
+									 gint                              len);
+
+GSequenceIter	*gtk_source_completion_words_library_find_next		(GSequenceIter                    *iter,
+									 gchar const                      *word,
+									 gint                              len);
+
+/* Getting */
+GtkSourceCompletionWordsProposal *
+		 gtk_source_completion_words_library_get_proposal 	(GSequenceIter                    *iter);
+
+/* Adding/removing */
+GtkSourceCompletionWordsProposal *
+		 gtk_source_completion_words_library_add_word 		(GtkSourceCompletionWordsLibrary  *library,
+                                              				 gchar const                      *word);
+void		 gtk_source_completion_words_library_remove_word 	(GtkSourceCompletionWordsLibrary  *library,
+                                                 			 GtkSourceCompletionWordsProposal *proposal);
+
+gboolean	 gtk_source_completion_words_library_is_locked 		(GtkSourceCompletionWordsLibrary *library);
+void		 gtk_source_completion_words_library_lock 		(GtkSourceCompletionWordsLibrary *library);
+void		 gtk_source_completion_words_library_unlock 		(GtkSourceCompletionWordsLibrary *library);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_COMPLETION_WORDS_LIBRARY_H__ */
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwordsproposal.c b/gtksourceview/completion-providers/words/gtksourcecompletionwordsproposal.c
new file mode 100644
index 0000000..b2a2295
--- /dev/null
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwordsproposal.c
@@ -0,0 +1,141 @@
+/*
+ * gtksourcecompletionwordsproposal.c
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * gtksourceview 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.
+ *
+ * gtksourceview is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gtksourceview; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#include "gtksourcecompletionwordsproposal.h"
+
+#define GTK_SOURCE_COMPLETION_WORDS_PROPOSAL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL, GtkSourceCompletionWordsProposalPrivate))
+
+struct _GtkSourceCompletionWordsProposalPrivate
+{
+	gchar *word;
+	gint use_count;
+};
+
+enum
+{
+	UNUSED,
+	NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = {0,};
+
+static void gtk_source_completion_proposal_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionWordsProposal, 
+			 gtk_source_completion_words_proposal, 
+			 G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_SOURCE_COMPLETION_PROPOSAL,
+			 			gtk_source_completion_proposal_iface_init))
+
+static const gchar *
+gtk_source_completion_words_proposal_get_text (GtkSourceCompletionProposal *proposal)
+{
+	return GTK_SOURCE_COMPLETION_WORDS_PROPOSAL(proposal)->priv->word;
+}
+
+static void
+gtk_source_completion_proposal_iface_init (gpointer g_iface, 
+                                           gpointer iface_data)
+{
+	GtkSourceCompletionProposalIface *iface = (GtkSourceCompletionProposalIface *)g_iface;
+	
+	/* Interface data getter implementations */
+	iface->get_label = gtk_source_completion_words_proposal_get_text;
+	iface->get_text = gtk_source_completion_words_proposal_get_text;
+}
+			 			
+static void
+gtk_source_completion_words_proposal_finalize (GObject *object)
+{
+	GtkSourceCompletionWordsProposal *proposal;
+	
+	proposal = GTK_SOURCE_COMPLETION_WORDS_PROPOSAL (object);
+	g_free (proposal->priv->word);
+
+	G_OBJECT_CLASS (gtk_source_completion_words_proposal_parent_class)->finalize (object);
+}
+
+static void
+gtk_source_completion_words_proposal_class_init (GtkSourceCompletionWordsProposalClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	
+	object_class->finalize = gtk_source_completion_words_proposal_finalize;
+
+	signals[UNUSED] =
+		g_signal_new ("unused",
+		              G_TYPE_FROM_CLASS (klass),
+		              G_SIGNAL_RUN_LAST,
+		              0,
+		              NULL, 
+		              NULL,
+		              g_cclosure_marshal_VOID__VOID, 
+		              G_TYPE_NONE,
+		              0);
+
+	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionWordsProposalPrivate));
+}
+
+static void
+gtk_source_completion_words_proposal_init (GtkSourceCompletionWordsProposal *self)
+{
+	self->priv = GTK_SOURCE_COMPLETION_WORDS_PROPOSAL_GET_PRIVATE (self);
+	
+	self->priv->use_count = 1;
+}
+
+GtkSourceCompletionWordsProposal *
+gtk_source_completion_words_proposal_new (gchar const *word)
+{
+	GtkSourceCompletionWordsProposal *proposal =
+		g_object_new (GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL, NULL);
+	
+	proposal->priv->word = g_strdup (word);
+	return proposal;
+}
+
+void
+gtk_source_completion_words_proposal_use (GtkSourceCompletionWordsProposal *proposal)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_PROPOSAL (proposal));
+	
+	g_atomic_int_inc (&proposal->priv->use_count);
+}
+
+void
+gtk_source_completion_words_proposal_unuse (GtkSourceCompletionWordsProposal *proposal)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_PROPOSAL (proposal));
+	
+	if (g_atomic_int_dec_and_test (&proposal->priv->use_count))
+	{
+		g_signal_emit (proposal, signals[UNUSED], 0);
+	}
+}
+
+gchar const *
+gtk_source_completion_words_proposal_get_word (GtkSourceCompletionWordsProposal *proposal)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS_PROPOSAL (proposal), NULL);
+	return proposal->priv->word;
+}
+
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwordsproposal.h b/gtksourceview/completion-providers/words/gtksourcecompletionwordsproposal.h
new file mode 100644
index 0000000..807af09
--- /dev/null
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwordsproposal.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecompletionwordsproposal.h
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * gtksourceview 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.
+ *
+ * gtksourceview is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gtksourceview; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_COMPLETION_WORDS_PROPOSAL_H__
+#define __GTK_SOURCE_COMPLETION_WORDS_PROPOSAL_H__
+
+#include <glib-object.h>
+#include <gtksourceview/gtksourcecompletionproposal.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL			(gtk_source_completion_words_proposal_get_type ())
+#define GTK_SOURCE_COMPLETION_WORDS_PROPOSAL(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL, GtkSourceCompletionWordsProposal))
+#define GTK_SOURCE_COMPLETION_WORDS_PROPOSAL_CONST(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL, GtkSourceCompletionWordsProposal const))
+#define GTK_SOURCE_COMPLETION_WORDS_PROPOSAL_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL, GtkSourceCompletionWordsProposalClass))
+#define GTK_IS_SOURCE_COMPLETION_WORDS_PROPOSAL(obj)			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL))
+#define GTK_IS_SOURCE_COMPLETION_WORDS_PROPOSAL_CLASS(klass)		(G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL))
+#define GTK_SOURCE_COMPLETION_WORDS_PROPOSAL_GET_CLASS(obj)		(G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_COMPLETION_WORDS_PROPOSAL, GtkSourceCompletionWordsProposalClass))
+
+typedef struct _GtkSourceCompletionWordsProposal		GtkSourceCompletionWordsProposal;
+typedef struct _GtkSourceCompletionWordsProposalClass		GtkSourceCompletionWordsProposalClass;
+typedef struct _GtkSourceCompletionWordsProposalPrivate		GtkSourceCompletionWordsProposalPrivate;
+
+struct _GtkSourceCompletionWordsProposal {
+	GObject parent;
+	
+	GtkSourceCompletionWordsProposalPrivate *priv;
+};
+
+struct _GtkSourceCompletionWordsProposalClass {
+	GObjectClass parent_class;
+};
+
+GType gtk_source_completion_words_proposal_get_type (void) G_GNUC_CONST;
+
+GtkSourceCompletionWordsProposal *
+		 gtk_source_completion_words_proposal_new 	(gchar const                      *word);
+
+gchar const 	*gtk_source_completion_words_proposal_get_word 	(GtkSourceCompletionWordsProposal *proposal);
+
+void		 gtk_source_completion_words_proposal_use 	(GtkSourceCompletionWordsProposal *proposal);
+void		 gtk_source_completion_words_proposal_unuse 	(GtkSourceCompletionWordsProposal *proposal);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_COMPLETION_WORDS_PROPOSAL_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b688e53..63910bb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,9 +22,7 @@ test_widget_LDADD = 			\
 	$(TESTS_LIBS)
 
 test_completion_SOURCES =		\
-        completion-simple.c		\
-        gsc-provider-words.c		\
-        gsc-provider-words.h
+        completion-simple.c
 
 test_completion_LDADD = 			\
 	$(top_builddir)/gtksourceview/libgtksourceview-2.0.la \
diff --git a/tests/completion-simple.c b/tests/completion-simple.c
index 2bc9826..46ab656 100644
--- a/tests/completion-simple.c
+++ b/tests/completion-simple.c
@@ -29,8 +29,7 @@
 #include <gtksourceview/gtksourcecompletion.h>
 #include <gtksourceview/gtksourcecompletioninfo.h>
 #include <gtksourceview/gtksourcelanguagemanager.h>
-
-#include "gsc-provider-words.h"
+#include <gtksourceview/completion-providers/words/gtksourcecompletionwords.h>
 
 #ifdef HAVE_DEVHELP
 #include <devhelp/dh-base.h>
@@ -84,26 +83,6 @@ show_headers_toggled_cb (GtkToggleButton *button,
 		      NULL);
 }
 
-static gboolean
-key_press (GtkWidget   *widget,
-	   GdkEventKey *event,
-	   gpointer     user_data)
-{
-	if (event->keyval == GDK_F8)
-	{
-		GtkSourceCompletionInfo *gsc_info;
-		
-		gsc_info = gtk_source_completion_get_info_window (comp);
-		
-		if (GTK_WIDGET_VISIBLE (GTK_WIDGET (gsc_info)))
-			gtk_widget_hide (GTK_WIDGET (gsc_info));
-		else
-			gtk_widget_show (GTK_WIDGET (gsc_info));
-	}
-	
-	return FALSE;
-}
-
 static void
 toggle_active_property (gpointer     source,
                         gpointer     dest,
@@ -155,9 +134,6 @@ create_window (void)
 	
 	gtk_container_add (GTK_CONTAINER (window), vbox);
 	
-	g_signal_connect (view, "key-release-event",
-			  G_CALLBACK (key_press),
-			  NULL);
 	g_signal_connect (window, "destroy",
 			  G_CALLBACK (destroy_cb),
 			   NULL);
@@ -177,10 +153,13 @@ create_window (void)
 static void
 create_completion(void)
 {
-	GscProviderWords *prov_words;
+	GtkSourceCompletionWords *prov_words;
 	
 	comp = gtk_source_view_get_completion (GTK_SOURCE_VIEW (view));
-	prov_words = gsc_provider_words_new (GTK_SOURCE_VIEW (view));
+	prov_words = gtk_source_completion_words_new (NULL, NULL);
+	
+	gtk_source_completion_words_register (prov_words,
+	                                      gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
 	
 	gtk_source_completion_add_provider (comp, 
 	                                    GTK_SOURCE_COMPLETION_PROVIDER (prov_words), 



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