1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
2  * gtksourcecompletionproviderwords.c
3  * This file is part of gtksourceview
4  *
5  * Copyright (C) 2009 - Jesse van den Kieboom
6  *
7  * gtksourceview is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * gtksourceview is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with gtksourceview; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA  02110-1301  USA
21  */
22 
23 #include "gtksourcecompletionwords.h"
24 #include "gtksourcecompletionwordslibrary.h"
25 #include "gtksourcecompletionwordsbuffer.h"
26 #include "gtksourcecompletionwordsutils.h"
27 
28 #include <gtksourceview/gtksourcecompletion.h>
29 #include <gtksourceview/gtksourceview-i18n.h>
30 #include <string.h>
31 
32 #define GTK_SOURCE_COMPLETION_WORDS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GTK_TYPE_SOURCE_COMPLETION_WORDS, GtkSourceCompletionWordsPrivate))
33 
34 #define BUFFER_KEY "GtkSourceCompletionWordsBufferKey"
35 
36 #define GET_WORDS_BUFFER(buf) (((BufferBinding *)g_object_get_data(G_OBJECT(buf), BUFFER_KEY))->buffer)
37 
38 enum
39 {
40 	PROP_0,
41 
42 	PROP_NAME,
43 	PROP_ICON,
44 	PROP_PROPOSALS_BATCH_SIZE,
45 	PROP_SCAN_BATCH_SIZE,
46 	PROP_MINIMUM_WORD_SIZE,
47 	PROP_INTERACTIVE_DELAY,
48 	PROP_PRIORITY
49 };
50 
51 struct _GtkSourceCompletionWordsPrivate
52 {
53 	gchar *name;
54 	GdkPixbuf *icon;
55 
56 	gchar *word;
57 	gint word_len;
58 	guint idle_id;
59 
60 	GtkSourceCompletionContext *context;
61 	GSequenceIter *populate_iter;
62 
63 	guint cancel_id;
64 
65 	guint proposals_batch_size;
66 	guint scan_batch_size;
67 	guint minimum_word_size;
68 
69 	GtkSourceCompletionWordsLibrary *library;
70 	GList *buffers;
71 
72 	gint interactive_delay;
73 	gint priority;
74 };
75 
76 typedef struct
77 {
78 	GObjectClass parent_class;
79 } GscProposalWordsClass;
80 
81 typedef struct
82 {
83 	GtkSourceCompletionWords *words;
84 	GtkSourceCompletionWordsBuffer *buffer;
85 } BufferBinding;
86 
87 static void gtk_source_completion_words_iface_init (GtkSourceCompletionProviderIface *iface);
88 
89 GType gsc_proposal_words_get_type (void);
90 
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))91 G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionWords,
92 			 gtk_source_completion_words,
93 			 G_TYPE_OBJECT,
94 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_SOURCE_COMPLETION_PROVIDER,
95 				 		gtk_source_completion_words_iface_init))
96 
97 static gchar *
98 gtk_source_completion_words_get_name (GtkSourceCompletionProvider *self)
99 {
100 	return g_strdup (GTK_SOURCE_COMPLETION_WORDS (self)->priv->name);
101 }
102 
103 static GdkPixbuf *
gtk_source_completion_words_get_icon(GtkSourceCompletionProvider * self)104 gtk_source_completion_words_get_icon (GtkSourceCompletionProvider *self)
105 {
106 	return GTK_SOURCE_COMPLETION_WORDS (self)->priv->icon;
107 }
108 
109 static void
population_finished(GtkSourceCompletionWords * words)110 population_finished (GtkSourceCompletionWords *words)
111 {
112 	if (words->priv->idle_id != 0)
113 	{
114 		g_source_remove (words->priv->idle_id);
115 		words->priv->idle_id = 0;
116 	}
117 
118 	g_free (words->priv->word);
119 	words->priv->word = NULL;
120 
121 	if (words->priv->context != NULL)
122 	{
123 		if (words->priv->cancel_id)
124 		{
125 			g_signal_handler_disconnect (words->priv->context,
126 			                             words->priv->cancel_id);
127 			words->priv->cancel_id = 0;
128 		}
129 
130 		g_object_unref (words->priv->context);
131 		words->priv->context = NULL;
132 	}
133 }
134 
135 static gboolean
add_in_idle(GtkSourceCompletionWords * words)136 add_in_idle (GtkSourceCompletionWords *words)
137 {
138 	guint idx = 0;
139 	GList *ret = NULL;
140 	gboolean finished;
141 
142 	if (words->priv->populate_iter == NULL)
143 	{
144 		words->priv->populate_iter =
145 			gtk_source_completion_words_library_find_first (words->priv->library,
146 			                                                words->priv->word,
147 			                                                words->priv->word_len);
148 	}
149 
150 	while (idx < words->priv->proposals_batch_size &&
151 	       words->priv->populate_iter)
152 	{
153 		GtkSourceCompletionWordsProposal *proposal =
154 				gtk_source_completion_words_library_get_proposal (words->priv->populate_iter);
155 
156 		/* Only add non-exact matches */
157 		if (strcmp (gtk_source_completion_words_proposal_get_word (proposal),
158 		            words->priv->word) != 0)
159 		{
160 			ret = g_list_prepend (ret, proposal);
161 		}
162 
163 		words->priv->populate_iter =
164 				gtk_source_completion_words_library_find_next (words->priv->populate_iter,
165 		                                                               words->priv->word,
166 		                                                               words->priv->word_len);
167 		++idx;
168 	}
169 
170 	ret = g_list_reverse (ret);
171 	finished = words->priv->populate_iter == NULL;
172 
173 	gtk_source_completion_context_add_proposals (words->priv->context,
174 	                                             GTK_SOURCE_COMPLETION_PROVIDER (words),
175 	                                             ret,
176 	                                             finished);
177 
178 	if (finished)
179 	{
180 		gtk_source_completion_words_library_unlock (words->priv->library);
181 		population_finished (words);
182 	}
183 
184 	return !finished;
185 }
186 
187 static gboolean
valid_word_char(gunichar ch,gpointer data)188 valid_word_char (gunichar ch,
189                  gpointer data)
190 {
191 	return g_unichar_isprint (ch) && (ch == '_' || g_unichar_isalnum (ch));
192 }
193 
194 static gboolean
valid_start_char(gunichar ch,gpointer data)195 valid_start_char (gunichar ch,
196                   gpointer data)
197 {
198 	return !g_unichar_isdigit (ch);
199 }
200 
201 static gchar *
get_word_at_iter(GtkTextIter * iter,CharacterCheck valid,CharacterCheck valid_start,gpointer data)202 get_word_at_iter (GtkTextIter    *iter,
203                   CharacterCheck  valid,
204                   CharacterCheck  valid_start,
205                   gpointer        data)
206 {
207 	GtkTextIter end = *iter;
208 
209 	if (!gtk_source_completion_words_utils_forward_word_end (iter, valid, data) ||
210 	    !gtk_text_iter_equal (iter, &end))
211 	{
212 		return NULL;
213 	}
214 
215 	if (!gtk_source_completion_words_utils_backward_word_start (iter,
216 	                                                            valid,
217 	                                                            valid_start,
218 	                                                            data))
219 	{
220 		return NULL;
221 	}
222 
223 	if (gtk_text_iter_equal (iter, &end))
224 	{
225 		return NULL;
226 	}
227 	else
228 	{
229 		return gtk_text_iter_get_text (iter, &end);
230 	}
231 }
232 
233 static gboolean
gtk_source_completion_words_match(GtkSourceCompletionProvider * provider,GtkSourceCompletionContext * context)234 gtk_source_completion_words_match (GtkSourceCompletionProvider *provider,
235                                    GtkSourceCompletionContext  *context)
236 {
237 	return TRUE;
238 }
239 
240 static void
gtk_source_completion_words_populate(GtkSourceCompletionProvider * provider,GtkSourceCompletionContext * context)241 gtk_source_completion_words_populate (GtkSourceCompletionProvider *provider,
242                                       GtkSourceCompletionContext  *context)
243 {
244 	GtkSourceCompletionWords *words = GTK_SOURCE_COMPLETION_WORDS (provider);
245 	GtkTextIter iter;
246 	gchar *word;
247 	GtkTextBuffer *buffer;
248 	GtkSourceCompletionWordsBuffer *buf;
249 
250 	gtk_source_completion_context_get_iter (context, &iter);
251 	buffer = gtk_text_iter_get_buffer (&iter);
252 
253 	g_free (words->priv->word);
254 	words->priv->word = NULL;
255 
256 	word = get_word_at_iter (&iter,
257 	                         valid_word_char,
258 	                         valid_start_char,
259 	                         words);
260 
261 	if (word == NULL ||
262 	    g_utf8_strlen (word, -1) < words->priv->minimum_word_size)
263 	{
264 		g_free (word);
265 		gtk_source_completion_context_add_proposals (context,
266 		                                             provider,
267 		                                             NULL,
268 		                                             TRUE);
269 		return;
270 	}
271 
272 	words->priv->cancel_id =
273 		g_signal_connect_swapped (context,
274 			                  "cancelled",
275 			                   G_CALLBACK (population_finished),
276 			                   provider);
277 
278 	words->priv->context = g_object_ref (context);
279 
280 	words->priv->word = word;
281 	words->priv->word_len = strlen (word);
282 
283 	buf = GET_WORDS_BUFFER (buffer);
284 	gtk_text_buffer_move_mark (buffer,
285 	                           gtk_source_completion_words_buffer_get_mark (buf),
286 	                           &iter);
287 
288 	/* Do first right now */
289 	if (add_in_idle (words))
290 	{
291 		gtk_source_completion_words_library_lock (words->priv->library);
292 		words->priv->idle_id = g_idle_add ((GSourceFunc)add_in_idle,
293 		                                   words);
294 	}
295 }
296 
297 static void
remove_buffer(BufferBinding * binding)298 remove_buffer (BufferBinding *binding)
299 {
300 	g_object_set_data (G_OBJECT (gtk_source_completion_words_buffer_get_buffer (binding->buffer)),
301 	                   BUFFER_KEY,
302 	                   NULL);
303 }
304 
305 static void
gtk_source_completion_words_dispose(GObject * object)306 gtk_source_completion_words_dispose (GObject *object)
307 {
308 	GtkSourceCompletionWords *provider = GTK_SOURCE_COMPLETION_WORDS (object);
309 	GList *cp;
310 
311 	population_finished (provider);
312 
313 	cp = g_list_copy (provider->priv->buffers);
314 	g_list_foreach (cp, (GFunc)remove_buffer, NULL);
315 
316 	g_list_free (cp);
317 	g_list_free (provider->priv->buffers);
318 
319 	g_free (provider->priv->name);
320 	provider->priv->name = NULL;
321 
322 	if (provider->priv->icon)
323 	{
324 		g_object_unref (provider->priv->icon);
325 		provider->priv->icon = NULL;
326 	}
327 
328 	if (provider->priv->library)
329 	{
330 		g_object_unref (provider->priv->library);
331 		provider->priv->library = NULL;
332 	}
333 
334 	G_OBJECT_CLASS (gtk_source_completion_words_parent_class)->dispose (object);
335 }
336 
337 static void
update_buffers_batch_size(GtkSourceCompletionWords * words)338 update_buffers_batch_size (GtkSourceCompletionWords *words)
339 {
340 	GList *item;
341 
342 	for (item = words->priv->buffers; item; item = g_list_next (item))
343 	{
344 		BufferBinding *binding = (BufferBinding *)item->data;
345 		gtk_source_completion_words_buffer_set_scan_batch_size (binding->buffer,
346 		                                                        words->priv->scan_batch_size);
347 	}
348 }
349 
350 static void
update_buffers_minimum_word_size(GtkSourceCompletionWords * words)351 update_buffers_minimum_word_size (GtkSourceCompletionWords *words)
352 {
353 	GList *item;
354 
355 	for (item = words->priv->buffers; item; item = g_list_next (item))
356 	{
357 		BufferBinding *binding = (BufferBinding *)item->data;
358 		gtk_source_completion_words_buffer_set_minimum_word_size (binding->buffer,
359 		                                                          words->priv->minimum_word_size);
360 	}
361 }
362 
363 static void
gtk_source_completion_words_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)364 gtk_source_completion_words_set_property (GObject      *object,
365                                           guint         prop_id,
366                                           const GValue *value,
367                                           GParamSpec   *pspec)
368 {
369 	GtkSourceCompletionWords *self = GTK_SOURCE_COMPLETION_WORDS (object);
370 
371 	switch (prop_id)
372 	{
373 		case PROP_NAME:
374 			g_free (self->priv->name);
375 			self->priv->name = g_value_dup_string (value);
376 
377 			if (self->priv->name == NULL)
378 			{
379 				self->priv->name = g_strdup (_("Document Words"));
380 			}
381 		break;
382 		case PROP_ICON:
383 			if (self->priv->icon)
384 			{
385 				g_object_unref (self->priv->icon);
386 			}
387 
388 			self->priv->icon = g_value_dup_object (value);
389 		break;
390 		case PROP_PROPOSALS_BATCH_SIZE:
391 			self->priv->proposals_batch_size = g_value_get_uint (value);
392 		break;
393 		case PROP_SCAN_BATCH_SIZE:
394 			self->priv->scan_batch_size = g_value_get_uint (value);
395 			update_buffers_batch_size (self);
396 		break;
397 		case PROP_MINIMUM_WORD_SIZE:
398 			self->priv->minimum_word_size = g_value_get_uint (value);
399 			update_buffers_minimum_word_size (self);
400 		break;
401 		case PROP_INTERACTIVE_DELAY:
402 			self->priv->interactive_delay = g_value_get_int (value);
403 		break;
404 		case PROP_PRIORITY:
405 			self->priv->priority = g_value_get_int (value);
406 		break;
407 		default:
408 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
409 		break;
410 	}
411 }
412 
413 static void
gtk_source_completion_words_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)414 gtk_source_completion_words_get_property (GObject    *object,
415                                           guint       prop_id,
416                                           GValue     *value,
417                                           GParamSpec *pspec)
418 {
419 	GtkSourceCompletionWords *self = GTK_SOURCE_COMPLETION_WORDS (object);
420 
421 	switch (prop_id)
422 	{
423 		case PROP_NAME:
424 			g_value_set_string (value, self->priv->name);
425 		break;
426 		case PROP_ICON:
427 			g_value_set_object (value, self->priv->icon);
428 		break;
429 		case PROP_PROPOSALS_BATCH_SIZE:
430 			g_value_set_uint (value, self->priv->proposals_batch_size);
431 		break;
432 		case PROP_SCAN_BATCH_SIZE:
433 			g_value_set_uint (value, self->priv->scan_batch_size);
434 		break;
435 		case PROP_MINIMUM_WORD_SIZE:
436 			g_value_set_uint (value, self->priv->minimum_word_size);
437 		break;
438 		case PROP_INTERACTIVE_DELAY:
439 			g_value_set_int (value, self->priv->interactive_delay);
440 		break;
441 		case PROP_PRIORITY:
442 			g_value_set_int (value, self->priv->priority);
443 		break;
444 		default:
445 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
446 		break;
447 	}
448 }
449 
450 static void
gtk_source_completion_words_class_init(GtkSourceCompletionWordsClass * klass)451 gtk_source_completion_words_class_init (GtkSourceCompletionWordsClass *klass)
452 {
453 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
454 
455 	object_class->dispose = gtk_source_completion_words_dispose;
456 
457 	object_class->set_property = gtk_source_completion_words_set_property;
458 	object_class->get_property = gtk_source_completion_words_get_property;
459 
460 	g_object_class_install_property (object_class,
461 	                                 PROP_NAME,
462 	                                 g_param_spec_string ("name",
463 	                                                      _("Name"),
464 	                                                      _("The provider name"),
465 	                                                      NULL,
466 	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
467 
468 	g_object_class_install_property (object_class,
469 	                                 PROP_ICON,
470 	                                 g_param_spec_object ("icon",
471 	                                                      _("Icon"),
472 	                                                      _("The provider icon"),
473 	                                                      GDK_TYPE_PIXBUF,
474 	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
475 
476 	g_object_class_install_property (object_class,
477 	                                 PROP_PROPOSALS_BATCH_SIZE,
478 	                                 g_param_spec_uint ("proposals-batch-size",
479 	                                                    _("Proposals Batch Size"),
480 	                                                    _("Number of proposals added in one batch"),
481 	                                                    1,
482 	                                                    G_MAXUINT,
483 	                                                    300,
484 	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
485 
486 	g_object_class_install_property (object_class,
487 	                                 PROP_SCAN_BATCH_SIZE,
488 	                                 g_param_spec_uint ("scan-batch-size",
489 	                                                    _("Scan Batch Size"),
490 	                                                    _("Number of lines scanned in one batch"),
491 	                                                    1,
492 	                                                    G_MAXUINT,
493 	                                                    50,
494 	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
495 
496 	g_object_class_install_property (object_class,
497 	                                 PROP_MINIMUM_WORD_SIZE,
498 	                                 g_param_spec_uint ("minimum-word-size",
499 	                                                    _("Minimum Word Size"),
500 	                                                    _("The minimum word size to complete"),
501 	                                                    2,
502 	                                                    G_MAXUINT,
503 	                                                    2,
504 	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
505 
506 	g_object_class_install_property (object_class,
507 	                                 PROP_INTERACTIVE_DELAY,
508 	                                 g_param_spec_int ("interactive-delay",
509 	                                                   _("Interactive Delay"),
510 	                                                   _("The delay before initiating interactive completion"),
511 	                                                   -1,
512 	                                                   G_MAXINT,
513 	                                                   50,
514 	                                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
515 
516 	g_object_class_install_property (object_class,
517 	                                 PROP_PRIORITY,
518 	                                 g_param_spec_int ("priority",
519 	                                                   _("Priority"),
520 	                                                   _("Provider priority"),
521 	                                                   G_MININT,
522 	                                                   G_MAXINT,
523 	                                                   0,
524 	                                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
525 
526 	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionWordsPrivate));
527 }
528 
529 static gboolean
gtk_source_completion_words_get_start_iter(GtkSourceCompletionProvider * provider,GtkSourceCompletionContext * context,GtkSourceCompletionProposal * proposal,GtkTextIter * iter)530 gtk_source_completion_words_get_start_iter (GtkSourceCompletionProvider *provider,
531                                             GtkSourceCompletionContext  *context,
532                                             GtkSourceCompletionProposal *proposal,
533                                             GtkTextIter                 *iter)
534 {
535 	GtkTextBuffer *buffer;
536 	GtkSourceCompletionWordsBuffer *buf;
537 	GtkTextIter it;
538 
539 	gtk_source_completion_context_get_iter (context, &it);
540 
541 	buffer = gtk_text_iter_get_buffer (&it);
542 	buf = GET_WORDS_BUFFER (buffer);
543 
544 	gtk_text_buffer_get_iter_at_mark (buffer,
545 	                                  iter,
546 	                                  gtk_source_completion_words_buffer_get_mark (buf));
547 	return TRUE;
548 }
549 
550 static gint
gtk_source_completion_words_get_interactive_delay(GtkSourceCompletionProvider * provider)551 gtk_source_completion_words_get_interactive_delay (GtkSourceCompletionProvider *provider)
552 {
553 	return GTK_SOURCE_COMPLETION_WORDS (provider)->priv->interactive_delay;
554 }
555 
556 static gint
gtk_source_completion_words_get_priority(GtkSourceCompletionProvider * provider)557 gtk_source_completion_words_get_priority (GtkSourceCompletionProvider *provider)
558 {
559 	return GTK_SOURCE_COMPLETION_WORDS (provider)->priv->priority;
560 }
561 
562 static void
gtk_source_completion_words_iface_init(GtkSourceCompletionProviderIface * iface)563 gtk_source_completion_words_iface_init (GtkSourceCompletionProviderIface *iface)
564 {
565 	iface->get_name = gtk_source_completion_words_get_name;
566 	iface->get_icon = gtk_source_completion_words_get_icon;
567 
568 	iface->populate = gtk_source_completion_words_populate;
569 	iface->match = gtk_source_completion_words_match;
570 
571 	iface->get_start_iter = gtk_source_completion_words_get_start_iter;
572 	iface->get_interactive_delay = gtk_source_completion_words_get_interactive_delay;
573 	iface->get_priority = gtk_source_completion_words_get_priority;
574 }
575 
576 static void
gtk_source_completion_words_init(GtkSourceCompletionWords * self)577 gtk_source_completion_words_init (GtkSourceCompletionWords *self)
578 {
579 	self->priv = GTK_SOURCE_COMPLETION_WORDS_GET_PRIVATE (self);
580 
581 	self->priv->library = gtk_source_completion_words_library_new ();
582 }
583 
584 GtkSourceCompletionWords *
gtk_source_completion_words_new(const gchar * name,GdkPixbuf * icon)585 gtk_source_completion_words_new (const gchar *name,
586                                  GdkPixbuf   *icon)
587 {
588 	return g_object_new (GTK_TYPE_SOURCE_COMPLETION_WORDS,
589 	                     "name", name,
590 	                     "icon", icon,
591 	                     NULL);
592 }
593 
594 static void
buffer_destroyed(BufferBinding * binding)595 buffer_destroyed (BufferBinding *binding)
596 {
597 	binding->words->priv->buffers = g_list_remove (binding->words->priv->buffers,
598 	                                               binding);
599 	g_object_unref (binding->buffer);
600 	g_slice_free (BufferBinding, binding);
601 }
602 
603 void
gtk_source_completion_words_register(GtkSourceCompletionWords * words,GtkTextBuffer * buffer)604 gtk_source_completion_words_register (GtkSourceCompletionWords *words,
605                                       GtkTextBuffer            *buffer)
606 {
607 	GtkSourceCompletionWordsBuffer *buf;
608 	BufferBinding *binding;
609 
610 	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS (words));
611 	g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
612 
613 	binding = g_object_get_data (G_OBJECT (buffer), BUFFER_KEY);
614 
615 	if (binding != NULL)
616 	{
617 		return;
618 	}
619 
620 	buf = gtk_source_completion_words_buffer_new (words->priv->library,
621 	                                              buffer);
622 
623 	gtk_source_completion_words_buffer_set_scan_batch_size (buf,
624 	                                                        words->priv->scan_batch_size);
625 
626 	gtk_source_completion_words_buffer_set_minimum_word_size (buf,
627 	                                                          words->priv->minimum_word_size);
628 
629 	binding = g_slice_new (BufferBinding);
630 	binding->words = words;
631 	binding->buffer = buf;
632 
633 	g_object_set_data_full (G_OBJECT (buffer),
634 	                        BUFFER_KEY,
635 	                        binding,
636 	                        (GDestroyNotify)buffer_destroyed);
637 
638 	words->priv->buffers = g_list_prepend (words->priv->buffers,
639 	                                       binding);
640 }
641 
642 void
gtk_source_completion_words_unregister(GtkSourceCompletionWords * words,GtkTextBuffer * buffer)643 gtk_source_completion_words_unregister (GtkSourceCompletionWords *words,
644                                         GtkTextBuffer            *buffer)
645 {
646 	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_WORDS (words));
647 	g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
648 
649 	g_object_set_data (G_OBJECT (buffer), BUFFER_KEY, NULL);
650 }
651 
652