1 /*
2  *  gretl -- Gnu Regression, Econometrics and Time-series Library
3  *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include "gretl.h"
21 #include "genparse.h"
22 #include "completions.h"
23 
24 #if GTKSOURCEVIEW_VERSION > 2
25 # define GTK_TYPE_SOURCE_COMPLETION_PROVIDER GTK_SOURCE_TYPE_COMPLETION_PROVIDER
26 #endif
27 
28 #define AC_DEBUG 0
29 
30 #include <gtksourceview/gtksourcecompletionprovider.h>
31 #include <gtksourceview/gtksourcecompletionitem.h>
32 #include <gtksourceview/completion-providers/words/gtksourcecompletionwords.h>
33 
34 /* global, referenced in settings.c and elsewhere */
35 int hansl_completion;
36 int console_completion;
37 
38 enum {
39     PROV_WORDS,
40     PROV_FUNCS,
41     PROV_CMDS,
42     PROV_SNIPPETS,
43     PROV_SERIES,
44     N_PROV
45 };
46 
47 static const char *prov_names[] = {
48     "words", "functions", "commands", "snippets", "series"
49 };
50 
51 typedef struct prov_info_ prov_info;
52 
53 struct prov_info_ {
54     void *ptr;          /* pointer to the completer struct */
55     GtkTextBuffer *buf; /* text buffer used for PROV_FUNCS, PROV_CMDS */
56 };
57 
prov_info_new(void)58 static prov_info *prov_info_new (void)
59 {
60     prov_info *pi = malloc(N_PROV * sizeof *pi);
61     int i;
62 
63     for (i=0; i<N_PROV; i++) {
64 	pi[i].ptr = NULL;
65 	pi[i].buf = NULL;
66     }
67 
68     return pi;
69 }
70 
prov_info_destroy(prov_info * pi)71 static void prov_info_destroy (prov_info *pi)
72 {
73     int i;
74 
75     for (i=0; i<N_PROV; i++) {
76 	if (pi[i].buf != NULL) {
77 	    g_object_unref(pi[i].buf);
78 	}
79     }
80     free(pi);
81 }
82 
destroy_words_providers(GtkWidget * w,gpointer p)83 static void destroy_words_providers (GtkWidget *w, gpointer p)
84 {
85     prov_info *pi = g_object_get_data(G_OBJECT(w), "prov_info");
86 
87     if (pi != NULL) {
88 	prov_info_destroy(pi);
89 	g_object_set_data(G_OBJECT(w), "prov_info", NULL);
90     }
91 }
92 
93 /* Create a GtkTextBuffer holding the names of built-in
94    gretl functions to serve as a completion provider.
95 */
96 
function_names_buffer(void)97 static GtkTextBuffer *function_names_buffer (void)
98 {
99     GtkTextBuffer *tbuf;
100     GString *str;
101     gchar *fnames;
102     const char *s;
103     int i, nf;
104 
105     nf = gen_func_count();
106     tbuf = gtk_text_buffer_new(NULL);
107     str = g_string_sized_new(nf * 8);
108 
109     for (i=0; i<nf; i++) {
110 	s = gen_func_name(i);
111 	if (*s != '_') {
112 	    g_string_append(str, s);
113 	    g_string_append_c(str, ' ');
114 	}
115     }
116 
117     fnames = g_string_free(str, FALSE);
118     gtk_text_buffer_set_text(tbuf, fnames, -1);
119     g_free(fnames);
120 
121     return tbuf;
122 }
123 
124 /* Create a GtkTextBuffer holding the names of gretl
125    commands to serve as a completion provider.
126 */
127 
command_names_buffer(void)128 static GtkTextBuffer *command_names_buffer (void)
129 {
130     GtkTextBuffer *tbuf;
131     GString *str;
132     gchar *cnames;
133     int i;
134 
135     tbuf = gtk_text_buffer_new(NULL);
136     str = g_string_sized_new(NC * 8);
137 
138     for (i=1; i<=NC; i++) {
139 	g_string_append(str, gretl_command_word(i));
140 	g_string_append_c(str, ' ');
141     }
142 
143     cnames = g_string_free(str, FALSE);
144     gtk_text_buffer_set_text(tbuf, cnames, -1);
145     g_free(cnames);
146 
147     return tbuf;
148 }
149 
150 /* Apparatus for providing "snippets" which may consist of
151    several "words", or other gretl-specific material (at
152    present, just names of dataset series).
153 */
154 
155 typedef struct _GretlProvider GretlProvider;
156 typedef struct _GretlProviderClass GretlProviderClass;
157 
158 struct _GretlProvider {
159     GObject parent;
160     GList *proposals;
161     gint priority;
162     const gchar *name;
163     GdkPixbuf *icon;
164     GtkSourceCompletionActivation activation;
165     gint id;
166 };
167 
168 struct _GretlProviderClass {
169     GObjectClass parent_class;
170 };
171 
172 static void gretl_provider_iface_init (GtkSourceCompletionProviderIface *iface);
173 GType gretl_provider_get_type (void);
174 
G_DEFINE_TYPE_WITH_CODE(GretlProvider,gretl_provider,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (GTK_TYPE_SOURCE_COMPLETION_PROVIDER,gretl_provider_iface_init))175 G_DEFINE_TYPE_WITH_CODE (GretlProvider,
176 			 gretl_provider,
177 			 G_TYPE_OBJECT,
178 			 G_IMPLEMENT_INTERFACE(GTK_TYPE_SOURCE_COMPLETION_PROVIDER,
179 					       gretl_provider_iface_init))
180 
181 static gchar *
182 gretl_provider_get_name (GtkSourceCompletionProvider *provider)
183 {
184     return g_strdup(((GretlProvider *) provider)->name);
185 }
186 
187 static gint
gretl_provider_get_priority(GtkSourceCompletionProvider * provider)188 gretl_provider_get_priority (GtkSourceCompletionProvider *provider)
189 {
190     return ((GretlProvider *) provider)->priority;
191 }
192 
193 static GtkSourceCompletionActivation
gretl_provider_get_activation(GtkSourceCompletionProvider * provider)194 gretl_provider_get_activation (GtkSourceCompletionProvider *provider)
195 {
196     return ((GretlProvider *) provider)->activation;
197 }
198 
199 #define valid(c) (c == '_' || isalnum(c))
200 
backward_word_start(GtkTextIter * iter)201 static gboolean backward_word_start (GtkTextIter *iter)
202 {
203     GtkTextIter prev = *iter;
204 
205     while (TRUE) {
206 	/* starting a line is OK */
207 	if (gtk_text_iter_starts_line(&prev)) {
208 	    break;
209 	}
210 	gtk_text_iter_backward_char(&prev);
211 	/* check if the previous character is a valid word character */
212 	if (!valid(gtk_text_iter_get_char(&prev))) {
213 	    break;
214 	}
215 	*iter = prev;
216     }
217 
218     if (!valid(gtk_text_iter_get_char(iter))) {
219 	return FALSE;
220     }
221 
222     return isalpha(gtk_text_iter_get_char(iter));
223 }
224 
forward_word_end(GtkTextIter * iter)225 static gboolean forward_word_end (GtkTextIter *iter)
226 {
227     while (TRUE) {
228 	/* ending a line is OK */
229 	if (gtk_text_iter_ends_line(iter)) {
230 	    break;
231 	}
232 	/* check if the next character is a valid word character */
233 	if (!valid(gtk_text_iter_get_char(iter))) {
234 	    break;
235 	}
236 	gtk_text_iter_forward_char(iter);
237     }
238 
239     return TRUE;
240 }
241 
get_word_at_iter(GtkTextIter * iter)242 static gchar *get_word_at_iter (GtkTextIter *iter)
243 {
244     GtkTextIter end = *iter;
245 
246     if (!forward_word_end(iter) || !gtk_text_iter_equal(iter, &end)) {
247 	return NULL;
248     } else if (!backward_word_start(iter)) {
249 	return NULL;
250     } else if (gtk_text_iter_equal(iter, &end)) {
251 	return NULL;
252     } else {
253 	return gtk_text_iter_get_text(iter, &end);
254     }
255 }
256 
proposal_get_cursor_offset(const gchar * s)257 static int proposal_get_cursor_offset (const gchar *s)
258 {
259     if (!strncmp(s, "if", 2)) {
260 	return 3;
261     } else if (!strncmp(s, "loop", 4)) {
262 	return 5;
263     } else if (!strncmp(s, "function", 8)) {
264 	return 9;
265     } else if (!strncmp(s, "outfile", 7)) {
266 	return 8;
267     } else if (!strncmp(s, "plot", 4)) {
268 	return 5;
269     } else if (!strncmp(s, "mpi", 3)) {
270 	return 5;
271     } else {
272 	return 0;
273     }
274 }
275 
276 /* Back up over the trigger for completion; insert the replacement
277    text; then back the cursor up to the point where the user will
278    first have to add something to the boilerplate.
279 */
280 
281 static gboolean
snippet_activate_proposal(GtkSourceCompletionProvider * provider,GtkSourceCompletionProposal * proposal,GtkTextIter * iter)282 snippet_activate_proposal (GtkSourceCompletionProvider *provider,
283 			   GtkSourceCompletionProposal *proposal,
284 			   GtkTextIter *iter)
285 {
286     gchar *s = gtk_source_completion_proposal_get_text(proposal);
287     int n = proposal_get_cursor_offset(s);
288 
289     if (n > 0) {
290 	GtkTextBuffer *buf = gtk_text_iter_get_buffer(iter);
291 	GtkTextIter start = *iter;
292 
293 	backward_word_start(&start);
294 	gtk_text_buffer_delete(buf, &start, iter);
295 	gtk_text_buffer_insert(buf, iter, s, -1);
296 	gtk_text_iter_backward_chars(iter, strlen(s) - n);
297 	gtk_text_buffer_place_cursor(buf, iter);
298 	return TRUE;
299     } else {
300 	return FALSE;
301     }
302 }
303 
304 static gboolean
series_activate_proposal(GtkSourceCompletionProvider * provider,GtkSourceCompletionProposal * proposal,GtkTextIter * iter)305 series_activate_proposal (GtkSourceCompletionProvider *provider,
306 			  GtkSourceCompletionProposal *proposal,
307 			  GtkTextIter *iter)
308 {
309     /* just accept the gtksourceview default */
310     return FALSE;
311 }
312 
313 static gboolean
gretl_activate_proposal(GtkSourceCompletionProvider * provider,GtkSourceCompletionProposal * proposal,GtkTextIter * iter)314 gretl_activate_proposal (GtkSourceCompletionProvider *provider,
315 			 GtkSourceCompletionProposal *proposal,
316 			 GtkTextIter *iter)
317 {
318     gint id = ((GretlProvider *) provider)->id;
319     gboolean ret = FALSE;
320 
321 #if AC_DEBUG
322     fprintf(stderr, "HERE gretl_activate_proposal (%s)\n",
323 	    prov_names[id]);
324 #endif
325 
326     if (id == PROV_SNIPPETS) {
327 	ret = snippet_activate_proposal(provider, proposal, iter);
328     } else if (id == PROV_SERIES) {
329 	ret = series_activate_proposal(provider, proposal, iter);
330     }
331 
332     return ret;
333 }
334 
335 static gboolean
gretl_provider_match(GtkSourceCompletionProvider * provider,GtkSourceCompletionContext * context)336 gretl_provider_match (GtkSourceCompletionProvider *provider,
337 		      GtkSourceCompletionContext *context)
338 {
339     return TRUE;
340 }
341 
342 static void
snippet_provider_populate(GtkSourceCompletionProvider * provider,GtkSourceCompletionContext * context)343 snippet_provider_populate (GtkSourceCompletionProvider *provider,
344 			   GtkSourceCompletionContext *context)
345 {
346     GList *L = ((GretlProvider *) provider)->proposals;
347     GList *ret = NULL;
348     GtkTextIter iter;
349     gchar *word;
350     int n;
351 
352     gtk_source_completion_context_get_iter(context, &iter);
353     word = get_word_at_iter(&iter);
354 
355     if (word != NULL && (n = strlen(word)) >= 2) {
356 	GtkSourceCompletionItem *item;
357 	gchar *label;
358 
359 	while (L != NULL) {
360 	    item = L->data;
361 	    g_object_get(item, "label", &label, NULL);
362 	    if (!strncmp(label, word, n)) {
363 		ret = g_list_prepend(ret, item);
364 	    }
365 	    g_free(label);
366 	    L = L->next;
367 	}
368 	g_free(word);
369     }
370 
371     if (ret != NULL) {
372 	ret = g_list_reverse(ret);
373     }
374 
375     gtk_source_completion_context_add_proposals(context, provider, ret, TRUE);
376     g_list_free(ret);
377 }
378 
comp_item_new(const gchar * label,const gchar * text)379 static GtkSourceCompletionItem *comp_item_new (const gchar *label,
380 					       const gchar *text)
381 {
382     GtkSourceCompletionItem *item;
383 
384 #if GTKSOURCEVIEW_VERSION == 4
385     item = gtk_source_completion_item_new();
386     gtk_source_completion_item_set_label(item, label);
387     gtk_source_completion_item_set_text(item, text);
388 #else
389     item = gtk_source_completion_item_new(label, text, NULL, NULL);
390 #endif
391 
392     return item;
393 }
394 
395 static void
series_provider_populate(GtkSourceCompletionProvider * provider,GtkSourceCompletionContext * context)396 series_provider_populate (GtkSourceCompletionProvider *provider,
397 			  GtkSourceCompletionContext *context)
398 {
399     GList *ret = NULL;
400     GtkTextIter iter;
401     gchar *word;
402     int n;
403 
404     gtk_source_completion_context_get_iter(context, &iter);
405     word = get_word_at_iter(&iter);
406 
407     if (word != NULL && (n = strlen(word)) > 1) {
408 	GtkSourceCompletionItem *item;
409 	const char *vname;
410 	int i;
411 
412 	for (i=0; i<dataset->v; i++) {
413 	    vname = dataset->varname[i];
414 	    if (!strncmp(vname, word, n)) {
415 		item = comp_item_new(vname, vname);
416 		ret = g_list_prepend(ret, item);
417 	    }
418 	}
419     }
420 
421     if (ret != NULL) {
422 	ret = g_list_reverse(ret);
423     }
424 
425     gtk_source_completion_context_add_proposals(context, provider, ret, TRUE);
426     g_list_free(ret);
427 }
428 
429 static void
gretl_provider_populate(GtkSourceCompletionProvider * provider,GtkSourceCompletionContext * context)430 gretl_provider_populate (GtkSourceCompletionProvider *provider,
431 			 GtkSourceCompletionContext *context)
432 {
433     gint id = ((GretlProvider *) provider)->id;
434 
435     if (id == PROV_SNIPPETS) {
436 	snippet_provider_populate(provider, context);
437     } else if (id == PROV_SERIES) {
438 	series_provider_populate(provider, context);
439     }
440 }
441 
442 static void
gretl_provider_iface_init(GtkSourceCompletionProviderIface * iface)443 gretl_provider_iface_init (GtkSourceCompletionProviderIface *iface)
444 {
445     iface->get_name = gretl_provider_get_name;
446     iface->populate = gretl_provider_populate;
447     iface->match = gretl_provider_match;
448     iface->get_priority = gretl_provider_get_priority;
449     iface->get_activation = gretl_provider_get_activation;
450     iface->activate_proposal = gretl_activate_proposal;
451 }
452 
453 static void
gretl_provider_set_activation(GretlProvider * gp,GtkSourceCompletionActivation A)454 gretl_provider_set_activation (GretlProvider *gp,
455 			       GtkSourceCompletionActivation A)
456 {
457     gp->activation = A;
458 }
459 
460 static void
words_provider_set_activation(GObject * obj,GtkSourceCompletionActivation A)461 words_provider_set_activation (GObject *obj,
462 			       GtkSourceCompletionActivation A)
463 {
464     g_object_set(obj, "activation", A, NULL);
465 }
466 
providers_set_activation(prov_info * pi,int userval)467 static void providers_set_activation (prov_info *pi,
468 				      int userval)
469 {
470     GtkSourceCompletionActivation A =
471 	GTK_SOURCE_COMPLETION_ACTIVATION_NONE;
472     int i;
473 
474     if (userval == COMPLETE_USER) {
475 	A = GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED;
476     } else if (userval == COMPLETE_AUTO) {
477 	A = GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE;
478     }
479 
480     for (i=0; i<N_PROV; i++) {
481 	if (pi[i].ptr != NULL) {
482 #if AC_DEBUG
483 	    fprintf(stderr, "set activation %d on %s\n", A, prov_names[i]);
484 #endif
485 	    if (i >= PROV_SNIPPETS) {
486 		gretl_provider_set_activation(pi[i].ptr, A);
487 	    } else {
488 		words_provider_set_activation(pi[i].ptr, A);
489 	    }
490 	}
491     }
492 }
493 
gretl_provider_class_init(GretlProviderClass * klass)494 static void gretl_provider_class_init (GretlProviderClass *klass)
495 {
496     return;
497 }
498 
499 struct snippet {
500     const char *label;
501     const char *text;
502 };
503 
504 /* a few simple example snippets for now */
505 
506 struct snippet snippets[] = {
507     { "loop..endloop", "loop \n\t\nendloop\n" },
508     { "if..endif", "if \n\t\nendif\n" },
509     { "function...", "function type name ()\n\t\nend function\n\n" },
510     { "outfile...", "outfile \n\t\nend outfile\n" },
511     { "plot...", "plot \n\t\nend plot\n" },
512     { "mpi...", "mpi\n\t\nend mpi\n" }
513 };
514 
snippet_provider_init(GretlProvider * self)515 static void snippet_provider_init (GretlProvider *self)
516 {
517     GtkSourceCompletionItem *item;
518     GList *proposals = NULL;
519     int i, n = G_N_ELEMENTS(snippets);
520 
521     for (i=0; i<n; i++) {
522 	item = comp_item_new(snippets[i].label, snippets[i].text);
523 	proposals = g_list_prepend(proposals, item);
524     }
525     self->proposals = proposals;
526 }
527 
528 static int gretl_prov_id;
529 
gretl_provider_init(GretlProvider * self)530 static void gretl_provider_init (GretlProvider *self)
531 {
532     if (gretl_prov_id == PROV_SNIPPETS) {
533 	snippet_provider_init(self);
534     } else {
535 	self->proposals = NULL;
536     }
537 }
538 
539 #if AC_DEBUG
540 
notify_activated(GtkSourceCompletion * comp,gpointer p)541 static void notify_activated (GtkSourceCompletion *comp,
542 			      gpointer p)
543 {
544     fprintf(stderr, "+++ got activate-proposal signal +++\n");
545 }
546 
notify_hidden(GtkSourceCompletion * comp,gpointer p)547 static void notify_hidden (GtkSourceCompletion *comp,
548 			   gpointer p)
549 {
550     fprintf(stderr, "+++ got hide signal +++\n");
551 }
552 
notify_populate(GtkSourceCompletion * comp,GtkSourceCompletionContext * context,gpointer p)553 static void notify_populate (GtkSourceCompletion *comp,
554 			     GtkSourceCompletionContext *context,
555 			     gpointer p)
556 {
557     fprintf(stderr, "+++ got populate-context signal +++\n");
558 }
559 
560 #endif
561 
add_gretl_provider(GtkSourceCompletion * comp,gint id,gint priority,windata_t * vwin,prov_info * pi)562 static void add_gretl_provider (GtkSourceCompletion *comp,
563 				gint id, gint priority,
564 				windata_t *vwin,
565 				prov_info *pi)
566 {
567     GretlProvider *gp;
568 
569     gretl_prov_id = id; /* hack! */
570     gp = g_object_new(gretl_provider_get_type(), NULL);
571     pi[id].ptr = gp;
572     gp->id = id;
573     gp->priority = priority;
574     gp->name = prov_names[id];
575     gtk_source_completion_add_provider(comp,
576 				       GTK_SOURCE_COMPLETION_PROVIDER(gp),
577 				       NULL);
578     g_object_unref(gp);
579 }
580 
581 /* end snippets apparatus */
582 
add_words_provider(GtkSourceCompletion * comp,gint8 id,gint priority,windata_t * vwin,prov_info * pi)583 static void add_words_provider (GtkSourceCompletion *comp,
584 				gint8 id, gint priority,
585 				windata_t *vwin,
586 				prov_info *pi)
587 {
588     const char *name = prov_names[id];
589     GtkSourceCompletionWords *cw;
590     GtkTextBuffer *buf;
591 
592     cw = gtk_source_completion_words_new(name, NULL);
593     pi[id].ptr = cw;
594     g_object_set(cw, "priority", priority, NULL);
595 
596     if (id == PROV_CMDS) {
597 	buf = pi[id].buf = command_names_buffer();
598     } else if (id == PROV_FUNCS) {
599 	buf = pi[id].buf = function_names_buffer();
600     } else {
601 	/* plain PROV_WORDS */
602 	buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
603     }
604     gtk_source_completion_words_register(cw, buf);
605     gtk_source_completion_add_provider(comp,
606 				       GTK_SOURCE_COMPLETION_PROVIDER(cw),
607 				       NULL);
608     g_object_set_data(G_OBJECT(vwin->text), name, cw);
609     g_object_unref(cw);
610 }
611 
set_sv_completion(windata_t * vwin)612 void set_sv_completion (windata_t *vwin)
613 {
614     GtkSourceCompletion *comp;
615     prov_info *pi = NULL;
616     int compval;
617 
618     comp = gtk_source_view_get_completion(GTK_SOURCE_VIEW(vwin->text));
619     pi = g_object_get_data(G_OBJECT(vwin->text), "prov_info");
620     compval = (vwin->role == CONSOLE)? console_completion :
621 	hansl_completion;
622 
623 #if AC_DEBUG
624     fprintf(stderr, "set_sv_completion: comp %s, prov_info %s, completion %d\n",
625 	    comp==NULL ? "null" : "present", pi==NULL? "null" : "present",
626 	    compval);
627 #endif
628 
629     if (compval && pi == NULL) {
630 	/* set up and activate */
631 	g_object_set(G_OBJECT(comp), "accelerators", 10,
632 		     "remember-info-visibility", TRUE, NULL);
633 	pi = prov_info_new();
634 	g_object_set_data(G_OBJECT(vwin->text), "prov_info", pi);
635 	if (vwin->role == CONSOLE) {
636 	    add_words_provider(comp, PROV_CMDS,   4, vwin, pi);
637 	    add_gretl_provider(comp, PROV_SERIES, 3, vwin, pi);
638 	    add_words_provider(comp, PROV_FUNCS,  2, vwin, pi);
639 	    add_words_provider(comp, PROV_WORDS,  1, vwin, pi);
640 	} else {
641 	    /* context is script editor */
642 	    add_gretl_provider(comp, PROV_SNIPPETS, 5, vwin, pi);
643 	    add_words_provider(comp, PROV_CMDS,     4, vwin, pi);
644 	    add_words_provider(comp, PROV_FUNCS,    3, vwin, pi);
645 	    add_gretl_provider(comp, PROV_SERIES,   2, vwin, pi);
646 	    add_words_provider(comp, PROV_WORDS,    1, vwin, pi);
647 	}
648 	g_signal_connect(G_OBJECT(vwin->text), "destroy",
649 			 G_CALLBACK(destroy_words_providers), NULL);
650 #if AC_DEBUG
651 	g_signal_connect(G_OBJECT(comp), "activate-proposal",
652 			 G_CALLBACK(notify_activated), NULL);
653 	g_signal_connect(G_OBJECT(comp), "hide",
654 			 G_CALLBACK(notify_hidden), NULL);
655 	g_signal_connect(G_OBJECT(comp), "populate-context",
656 			 G_CALLBACK(notify_populate), NULL);
657 	fprintf(stderr, "providers set up\n");
658 #endif
659     }
660 
661     if (pi != NULL) {
662 	providers_set_activation(pi, compval);
663     }
664 }
665 
call_user_completion(GtkWidget * w)666 void call_user_completion (GtkWidget *w)
667 {
668 #if AC_DEBUG
669     fprintf(stderr, "call_user_completion...\n");
670 #endif
671     g_signal_emit_by_name(GTK_SOURCE_VIEW(w), "show-completion", NULL);
672 }
673