1 /**
2 * @file snippets.c
3 * @brief handle snppets
4 *
5 * Copyright (C) 2009 Gummi Developers
6 * All Rights reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use,
12 * copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following
15 * conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30 #include "snippets.h"
31
32 #include <assert.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37
38 #include <glib.h>
39
40 #include "constants.h"
41 #include "editor.h"
42 #include "environment.h"
43 #include "utils.h"
44
snippets_init()45 GuSnippets* snippets_init () {
46 GuSnippets* s = g_new0 (GuSnippets, 1);
47
48 gchar* filename = g_build_filename (C_GUMMI_CONFDIR, "snippets.cfg", NULL);
49 gchar* dirname = g_path_get_dirname (filename);
50
51 g_mkdir_with_parents (dirname, DIR_PERMS);
52 g_free (dirname);
53
54 slog (L_INFO, "Snippets : %s\n", filename);
55
56 s->filename = g_strdup (filename);
57 s->accel_group = gtk_accel_group_new ();
58 s->stackframe = NULL;
59
60 snippets_load (s);
61 return s;
62 }
63
snippets_set_default(GuSnippets * sc)64 void snippets_set_default (GuSnippets* sc) {
65 GError* err = NULL;
66 gchar* snip = g_build_filename (GUMMI_DATA, "snippets", "snippets.cfg", NULL);
67 GList* current = sc->closure_data;
68
69 /* Remove all accelerator */
70 while (current) {
71 gtk_accel_group_disconnect (sc->accel_group,
72 TUPLE2 (current->data)->second);
73 slog (L_DEBUG, "Accelerator for `%s' disconnected\n",
74 TUPLE2 (current->data)->first);
75 current = g_list_next (current);
76 }
77 g_list_free (sc->closure_data);
78 sc->closure_data = NULL;
79
80 if (!utils_copy_file (snip, sc->filename, &err)) {
81 slog (L_G_ERROR, "can't open snippets file for writing, snippets may "
82 "not work properly\n");
83 g_error_free (err);
84 } else {
85 snippets_load (sc);
86 }
87 g_free (snip);
88 }
89
snippets_load(GuSnippets * sc)90 void snippets_load (GuSnippets* sc) {
91 FILE* fh = 0;
92 gchar buf[BUFSIZ];
93 gchar* rot = NULL;
94 gchar* seg = NULL;
95 slist* current = NULL;
96 slist* prev = NULL;
97
98 if (sc->head)
99 snippets_clean_up (sc);
100
101 if (! (fh = fopen (sc->filename, "r"))) {
102 slog (L_ERROR, "can't find snippets file, reseting to default\n");
103 snippets_set_default (sc);
104 return;
105 }
106
107 while (fgets (buf, BUFSIZ, fh)) {
108 current = g_new0 (slist, 1);
109 if (!sc->head)
110 sc->head = prev = current;
111 prev->next = current;
112 buf[strlen (buf) -1] = 0; /* remove trailing '\n' */
113
114 if (buf[0] != '\t') {
115 if ('#' == buf[0] || !strlen(buf)) {
116 current->first = g_strdup (buf);
117 } else {
118 seg = strstr (buf, " ") + 1;
119 current->first = g_strdup ((seg == buf)? "Invalid": seg);
120 snippets_set_accelerator (sc, current->first);
121 }
122 } else {
123 if (!prev->second) {
124 prev->second = g_strdup (buf + 1);
125 continue;
126 }
127 rot = g_strdup (prev->second);
128 g_free (prev->second);
129 prev->second = g_strconcat (rot, "\n", buf + 1, NULL);
130 g_free (rot);
131 g_free (current);
132 current = prev;
133 }
134 prev = current;
135 }
136 if (prev) prev->next = NULL;
137 fclose (fh);
138 }
139
snippets_save(GuSnippets * sc)140 void snippets_save (GuSnippets* sc) {
141 FILE* fh = 0;
142 slist* current = sc->head;
143 gint i = 0, count = 0, len = 0;
144 gchar* buf = 0;
145
146 if (! (fh = fopen (sc->filename, "w")))
147 slog (L_FATAL, "can't open snippets file for writing... abort\n");
148
149 while (current) {
150 /* skip comments */
151 if (!current->second) {
152 fputs (current->first, fh);
153 fputs ("\n", fh);
154 current = current->next;
155 continue;
156 }
157 fputs ("snippet ", fh);
158 fputs (current->first, fh);
159 fputs ("\n\t", fh);
160
161 len = strlen (current->second) + 1;
162 buf = (gchar*)g_malloc (len * 2);
163 memset (buf, 0, len * 2);
164 /* replace '\n' with '\n\t' for options with multi-line content */
165 for (i = 0; i < len; ++i) {
166 if (count + 2 == len * 2) break;
167 buf[count++] = current->second[i];
168 if (i != len -2 && '\n' == current->second[i])
169 buf[count++] = '\t';
170 }
171 fputs (buf, fh);
172 fputs ("\n", fh);
173 current = current->next;
174 count = 0;
175 g_free (buf);
176 }
177 fclose (fh);
178 }
179
snippets_clean_up(GuSnippets * sc)180 void snippets_clean_up (GuSnippets* sc) {
181 slist* prev = sc->head;
182 slist* current;
183 while (prev) {
184 current = prev->next;
185 g_free (prev);
186 prev = current;
187 }
188 sc->head = NULL;
189 }
190
snippets_get_value(GuSnippets * sc,const gchar * term)191 gchar* snippets_get_value (GuSnippets* sc, const gchar* term) {
192 gchar* key = g_strdup_printf ("%s,", term);
193 slist* index = slist_find (sc->head, key, TRUE, FALSE);
194 g_free (key);
195 return (index)? index->second: NULL;
196 }
197
snippets_set_accelerator(GuSnippets * sc,gchar * config)198 void snippets_set_accelerator (GuSnippets* sc, gchar* config) {
199 /* config has the form: Key,Accel_key,Name */
200 GClosure* closure = NULL;
201 GdkModifierType mod;
202 guint keyval = 0;
203 gchar** configs = g_strsplit (config, ",", 0);
204 Tuple2* data = g_new0 (Tuple2, 1);
205 Tuple2* closure_data = g_new0 (Tuple2, 1);
206
207 /* Return if config does not contains accelerator */
208 if (strlen (configs[1]) == 0) {
209 g_strfreev (configs);
210 return;
211 }
212
213 data->first = (gpointer)sc;
214 data->second = (gpointer)g_strdup (configs[0]);
215
216
217 closure = g_cclosure_new (G_CALLBACK (snippets_accel_cb), data, NULL);
218 closure_data->first = (gpointer)data->second;
219 closure_data->second = (gpointer)closure;
220
221 sc->closure_data = g_list_append (sc->closure_data, closure_data);
222 gtk_accelerator_parse (configs[1], &keyval, &mod);
223
224 /* Return without connect if accel is not valid */
225 if (!gtk_accelerator_valid (keyval, mod)) return;
226
227 snippets_accel_connect (sc, keyval, mod, closure);
228 g_strfreev (configs);
229 }
230
snippets_activate(GuSnippets * sc,GuEditor * ec,gchar * key)231 void snippets_activate (GuSnippets* sc, GuEditor* ec, gchar* key) {
232 gchar* snippet = NULL;
233 GuSnippetInfo* new_info = NULL;
234 GtkTextIter start, end;
235
236 slog (L_DEBUG, "Snippet `%s' activated\n", key);
237
238 snippet = snippets_get_value (sc, key);
239 g_return_if_fail (snippet != NULL);
240
241 new_info = snippets_parse (snippet);
242
243 gtk_text_buffer_get_selection_bounds (ec_buffer, &start, &end);
244 new_info->start_offset = gtk_text_iter_get_offset (&start);
245 new_info->sel_text = gtk_text_iter_get_text (&start, &end);
246 GSList* marks = gtk_text_iter_get_marks (&start);
247 new_info->sel_start = *GTK_TEXT_MARK (marks->data);
248 g_slist_free (marks);
249
250 gtk_text_buffer_insert (ec_buffer, &start, new_info->expanded, -1);
251 snippet_info_create_marks (new_info, ec);
252 snippet_info_initial_expand (new_info, ec);
253 gtk_text_buffer_set_modified (ec_buffer, TRUE);
254
255 if (sc->info) {
256 snippet_info_sync_group (sc->info, ec);
257 sc->stackframe = g_list_append (sc->stackframe, sc->info);
258 }
259 sc->info = new_info;
260
261 if (!snippet_info_goto_next_placeholder (sc->info, ec))
262 snippets_deactivate (sc, ec);
263 }
264
snippets_deactivate(GuSnippets * sc,GuEditor * ec)265 void snippets_deactivate (GuSnippets* sc, GuEditor* ec) {
266 GList* last = NULL;
267 snippet_info_free (sc->info, ec);
268 last = g_list_last (sc->stackframe);
269 if (last) {
270 sc->info = last->data;
271 sc->stackframe = g_list_remove (sc->stackframe, last->data);
272 } else
273 sc->info = NULL;
274 slog (L_DEBUG, "Snippet deactivated\n");
275 }
276
snippets_key_press_cb(GuSnippets * sc,GuEditor * ec,GdkEventKey * ev)277 gboolean snippets_key_press_cb (GuSnippets* sc, GuEditor* ec, GdkEventKey* ev) {
278 GtkTextIter current, start;
279
280 if (ev->keyval == GDK_KEY_Tab) {
281 gchar* key = NULL;
282 editor_get_current_iter (ec, ¤t);
283 if (gtk_text_iter_ends_word (¤t)) {
284 start = current;
285 gtk_text_iter_backward_word_start (&start);
286 key = gtk_text_iter_get_text (&start, ¤t);
287
288 if (snippets_get_value (sc, key)) {
289 gtk_text_buffer_delete (ec_buffer, &start, ¤t);
290 snippets_activate (sc, ec, key);
291 g_free (key);
292 return TRUE;
293 }
294 g_free (key);
295 }
296 }
297
298 if (sc->info) {
299 if (ev->keyval == GDK_KEY_Tab) {
300 if (!snippet_info_goto_next_placeholder (sc->info, ec))
301 snippets_deactivate (sc, ec);
302 return TRUE;
303 } else if (ev->keyval == GDK_KEY_ISO_Left_Tab
304 && ev->state & GDK_SHIFT_MASK) {
305 if (!snippet_info_goto_prev_placeholder (sc->info, ec))
306 snippets_deactivate (sc, ec);
307 return TRUE;
308 }
309 /* Deactivate snippet if the current insert range is not within the
310 * snippet */
311 editor_get_current_iter (ec, ¤t);
312 gint offset = gtk_text_iter_get_offset (¤t);
313 GList* last = g_list_last (sc->info->einfo);
314 if (last) {
315 gtk_text_buffer_get_iter_at_mark (ec_buffer, ¤t,
316 GU_SNIPPET_EXPAND_INFO (last->data)->left_mark);
317 gint bound_end = gtk_text_iter_get_offset (¤t);
318 if (offset < sc->info->start_offset || offset > bound_end)
319 snippets_deactivate (sc, ec);
320 }
321 }
322
323 return FALSE;
324 }
325
snippets_key_release_cb(GuSnippets * sc,GuEditor * ec,GdkEventKey * ev)326 gboolean snippets_key_release_cb (GuSnippets* sc, GuEditor* ec, GdkEventKey* ev) {
327
328 if (ev->keyval != GDK_KEY_Tab && !sc->info)
329 return FALSE;
330
331 if (sc->info)
332 snippet_info_sync_group (sc->info, ec);
333
334 return FALSE;
335 }
336
snippets_parse(char * snippet)337 GuSnippetInfo* snippets_parse (char* snippet) {
338 gint i, start, end;
339 GError* err = NULL;
340 gchar** result = NULL;
341 GRegex* regex = NULL;
342 GMatchInfo* match_info = NULL;
343 const gchar* holders[] = { "\\$([0-9]+)", "\\${([0-9]*):?([^}]*)}",
344 "\\$(FILENAME)", "\\${(FILENAME)}",
345 "\\$(BASENAME)", "\\${(BASENAME)}",
346 "\\$(SELECTED_TEXT)", "\\${(SELECTED_TEXT)}"
347 /* Anyway to combine these? */
348 };
349
350 GuSnippetInfo* info = snippet_info_new (snippet);
351
352 for (i = 0; i < sizeof(holders) / sizeof(holders[0]); ++i) {
353 if (! (regex = g_regex_new (holders[i], G_REGEX_DOTALL, 0, &err))) {
354 slog (L_ERROR, "g_regex_new (): %s\n", err->message);
355 g_error_free (err);
356 return info;
357 }
358 g_regex_match (regex, snippet, 0, &match_info);
359 while (g_match_info_matches (match_info)) {
360 result = g_match_info_fetch_all (match_info);
361 g_match_info_fetch_pos (match_info, 0, &start, &end);
362
363 /* Convert start, end to UTF8 offset */
364 char* s_start = g_substr(snippet, 0, start);
365 char* s_end = g_substr(snippet, 0, end);
366 start = g_utf8_strlen(s_start, -1);
367 end = g_utf8_strlen(s_end, -1);
368 g_free(s_start);
369 g_free(s_end);
370
371 if (i < 2) {
372 snippet_info_append_holder (info, atoi (result[1]), start,
373 end -start, result[2]);
374 } else {
375 snippet_info_append_holder (info, -1, start, end -start,
376 result[1]);
377 }
378 slog (L_DEBUG, "Placeholder: (%s, %s, %s)\n", result[0], result[1],
379 result[2]);
380 g_match_info_next (match_info, NULL);
381 g_strfreev (result);
382 }
383 g_regex_unref (regex);
384 g_match_info_free (match_info);
385 }
386 info->einfo = g_list_sort (info->einfo, snippet_info_pos_cmp);
387 info->einfo_sorted = g_list_copy (info->einfo);
388 info->einfo_sorted = g_list_sort (info->einfo_sorted, snippet_info_num_cmp);
389
390 return info;
391 }
392
snippets_accel_cb(GtkAccelGroup * accel_group,GObject * obj,guint keyval,GdkModifierType mods,Tuple2 * udata)393 void snippets_accel_cb (GtkAccelGroup* accel_group, GObject* obj,
394 guint keyval, GdkModifierType mods, Tuple2* udata) {
395 GuSnippets* sc = GU_SNIPPETS (udata->first);
396 gchar* key = (gchar*)udata->second;
397 /* XXX: Don't know how to avoid using gummi_get_active_editor () here. Since
398 * gtk_accel_group must be connect when load, we can not specify the
399 * editor in user_data, because snippets should only have effect on
400 * active tab */
401 snippets_activate (sc, gummi_get_active_editor (), key);
402 }
403
snippets_accel_connect(GuSnippets * sc,guint keyval,GdkModifierType mod,GClosure * closure)404 void snippets_accel_connect (GuSnippets* sc, guint keyval, GdkModifierType mod,
405 GClosure* closure) {
406 gchar* acc = NULL;
407 gtk_accel_group_connect (sc->accel_group, keyval,
408 gtk_accelerator_get_default_mod_mask () & mod, GTK_ACCEL_VISIBLE,
409 closure);
410
411 acc = gtk_accelerator_get_label (keyval,
412 gtk_accelerator_get_default_mod_mask () & mod);
413 slog (L_DEBUG, "Accelerator `%s' connected\n", acc);
414 g_free (acc);
415 }
416
snippets_accel_disconnect(GuSnippets * sc,const gchar * key)417 void snippets_accel_disconnect (GuSnippets* sc, const gchar* key) {
418 Tuple2* closure_data = NULL;
419 GList* current = NULL;
420
421 g_return_if_fail (key != NULL);
422
423 current = sc->closure_data;
424 while (current) {
425 closure_data = TUPLE2 (current->data);
426 if (STR_EQU (closure_data->first, key))
427 break;
428 current = g_list_next (current);
429 }
430 if (current) {
431 gtk_accel_group_disconnect (sc->accel_group, closure_data->second);
432 sc->closure_data = g_list_remove (sc->closure_data, closure_data);
433 g_free (closure_data);
434 slog (L_DEBUG, "Accelerator for `%s' disconnected\n",
435 closure_data->first);
436 }
437 }
438
snippet_info_new(gchar * snippet)439 GuSnippetInfo* snippet_info_new (gchar* snippet) {
440 GuSnippetInfo* info = g_new0 (GuSnippetInfo, 1);
441 info->snippet = g_strdup (snippet);
442 info->expanded = g_strdup (snippet);
443 info->einfo = NULL;
444 info->einfo_sorted = NULL;
445 return info;
446 }
447
snippet_info_free(GuSnippetInfo * info,GuEditor * ec)448 void snippet_info_free (GuSnippetInfo* info, GuEditor* ec) {
449 snippet_info_remove_marks (info, ec);
450 GList* current = g_list_first (info->einfo);
451 while (current) {
452 g_free (GU_SNIPPET_EXPAND_INFO (current->data)->text);
453 current = g_list_next (current);
454 }
455 g_list_free (info->einfo);
456 g_list_free (info->einfo_unique);
457 g_list_free (info->einfo_sorted);
458 g_free (info);
459 }
460
snippet_info_goto_next_placeholder(GuSnippetInfo * info,GuEditor * ec)461 gboolean snippet_info_goto_next_placeholder (GuSnippetInfo* info, GuEditor* ec) {
462 GuSnippetExpandInfo* einfo = NULL;
463 GtkTextIter start, end;
464 gboolean success = TRUE;
465
466 /* Snippet just activated */
467 if (!info->current) {
468 /* Skip $0, $-1 and jump to next placeholder */
469 if (info->einfo_unique) {
470 info->current = info->einfo_unique;
471 while (info->current &&
472 GU_SNIPPET_EXPAND_INFO (info->current->data)->group_number <= 0)
473 info->current = g_list_next (info->current);
474 }
475 } else
476 info->current = g_list_next (info->current);
477
478 /* No placeholder left */
479 if (!info->current) {
480 info->current = g_list_first (info->einfo_sorted);
481 while (info->current &&
482 GU_SNIPPET_EXPAND_INFO (info->current->data)->group_number != 0)
483 info->current = g_list_next(info->current);
484
485 if (!info->current)
486 return FALSE;
487 else /* This is the last one($0) set to false to deactivate snippet */
488 success = FALSE;
489 }
490
491 einfo = GU_SNIPPET_EXPAND_INFO (info->current->data);
492 gtk_text_buffer_get_iter_at_mark (ec_buffer, &start, einfo->left_mark);
493 gtk_text_buffer_get_iter_at_mark (ec_buffer, &end, einfo->right_mark);
494 gtk_text_buffer_place_cursor (ec_buffer, &start);
495 gtk_text_buffer_select_range (ec_buffer, &start, &end);
496 return success;
497 }
498
snippet_info_goto_prev_placeholder(GuSnippetInfo * info,GuEditor * ec)499 gboolean snippet_info_goto_prev_placeholder (GuSnippetInfo* info, GuEditor* ec) {
500 GuSnippetExpandInfo* einfo = NULL;
501 GtkTextIter start, end;
502 info->current = g_list_previous (info->current);
503
504 /* Return false to deactivate snippet */
505 if (!info->current ||
506 GU_SNIPPET_EXPAND_INFO(info->current->data)->group_number < 0)
507 return FALSE;
508
509 einfo = GU_SNIPPET_EXPAND_INFO (info->current->data);
510 gtk_text_buffer_get_iter_at_mark (ec_buffer, &start, einfo->left_mark);
511 gtk_text_buffer_get_iter_at_mark (ec_buffer, &end, einfo->right_mark);
512 gtk_text_buffer_place_cursor (ec_buffer, &start);
513 gtk_text_buffer_select_range (ec_buffer, &start, &end);
514 return TRUE;
515 }
516
snippet_info_append_holder(GuSnippetInfo * info,gint group,gint start,gint len,gchar * text)517 void snippet_info_append_holder (GuSnippetInfo* info, gint group, gint start,
518 gint len, gchar* text) {
519 GuSnippetExpandInfo* einfo = g_new0 (GuSnippetExpandInfo, 1);
520 einfo->group_number = group;
521 einfo->start = start;
522 einfo->len = len;
523 einfo->text = g_strdup (text? text: "");
524 info->einfo = g_list_append (info->einfo, einfo);
525 }
526
snippet_info_create_marks(GuSnippetInfo * info,GuEditor * ec)527 void snippet_info_create_marks (GuSnippetInfo* info, GuEditor* ec) {
528 GList* current = g_list_first (info->einfo);
529 GtkTextIter start, end;
530
531 while (current) {
532 GuSnippetExpandInfo* einfo = GU_SNIPPET_EXPAND_INFO (current->data);
533 gtk_text_buffer_get_iter_at_offset (ec_buffer, &start,
534 info->start_offset + einfo->start);
535 gtk_text_buffer_get_iter_at_offset (ec_buffer, &end,
536 info->start_offset + einfo->start + einfo->len);
537 einfo->left_mark = gtk_text_mark_new (NULL, TRUE);
538 einfo->right_mark = gtk_text_mark_new (NULL, FALSE);
539 gtk_text_buffer_add_mark (ec_buffer, einfo->left_mark, &start);
540 gtk_text_buffer_add_mark (ec_buffer, einfo->right_mark, &end);
541 current = g_list_next (current);
542 }
543 slog (L_DEBUG, "Marks created\n");
544 }
545
snippet_info_remove_marks(GuSnippetInfo * info,GuEditor * ec)546 void snippet_info_remove_marks (GuSnippetInfo* info, GuEditor* ec) {
547 GList* current = g_list_first (info->einfo);
548
549 while (current) {
550 GuSnippetExpandInfo* einfo = GU_SNIPPET_EXPAND_INFO (current->data);
551 gtk_text_buffer_delete_mark (ec_buffer, einfo->left_mark);
552 gtk_text_buffer_delete_mark (ec_buffer, einfo->right_mark);
553 current = g_list_next (current);
554 }
555 slog (L_DEBUG, "Marks removed\n");
556 }
557
snippet_info_initial_expand(GuSnippetInfo * info,GuEditor * ec)558 void snippet_info_initial_expand (GuSnippetInfo* info, GuEditor* ec) {
559 GuSnippetExpandInfo* value = NULL;
560 GtkTextIter start, end;
561 GHashTable* map = NULL;
562 GList* current = NULL;
563 gchar* text = NULL;
564 gint key = 0;
565
566 map = g_hash_table_new (NULL, NULL);
567 current = g_list_first (info->einfo);
568
569 while (current) {
570 GuSnippetExpandInfo* einfo = GU_SNIPPET_EXPAND_INFO (current->data);
571 if (!g_hash_table_lookup_extended (map, ((gpointer)einfo->group_number),
572 (gpointer)&key, (gpointer)&value)) {
573 g_hash_table_insert (map, (gpointer)einfo->group_number, einfo);
574 info->einfo_unique = g_list_append (info->einfo_unique, einfo);
575 }
576 current = g_list_next (current);
577 }
578 info->einfo_unique = g_list_sort (info->einfo_unique, snippet_info_num_cmp);
579
580 current = g_list_first (info->einfo);
581 info->offset = 0;
582
583 while (current) {
584 GuSnippetExpandInfo* einfo = GU_SNIPPET_EXPAND_INFO (current->data);
585 g_hash_table_lookup_extended (map, (gpointer)einfo->group_number,
586 (gpointer)&key, (gpointer)&value);
587 gtk_text_buffer_get_iter_at_mark (ec_buffer, &start, einfo->left_mark);
588 gtk_text_buffer_get_iter_at_mark (ec_buffer, &end, einfo->right_mark);
589
590 /* Expand macros */
591 text = GU_SNIPPET_EXPAND_INFO(current->data)->text;
592 if (STR_EQU (text, "SELECTED_TEXT")) {
593 GtkTextIter ms, me;
594 gtk_text_buffer_delete (ec_buffer, &start, &end);
595 gtk_text_buffer_insert (ec_buffer, &start, info->sel_text, -1);
596 gtk_text_buffer_get_iter_at_mark (ec_buffer, &ms, &info->sel_start);
597 me = ms;
598 gtk_text_iter_forward_chars (&me, strlen (info->sel_text));
599 gtk_text_buffer_delete (ec_buffer, &ms, &me);
600 } else if (STR_EQU (text, "FILENAME")) {
601 gtk_text_buffer_delete (ec_buffer, &start, &end);
602 gtk_text_buffer_insert (ec_buffer, &start,
603 ec->filename? ec->filename: "", -1);
604 } else if (STR_EQU (text, "BASENAME")) {
605 gchar* basename = g_path_get_basename(ec->filename?ec->filename:"");
606 gtk_text_buffer_delete (ec_buffer, &start, &end);
607 gtk_text_buffer_insert (ec_buffer, &start, basename, -1);
608 g_free (basename);
609 } else {
610 /* Expand text of same group with with text of group leader */
611 gtk_text_buffer_delete (ec_buffer, &start, &end);
612 gtk_text_buffer_insert (ec_buffer, &start, value->text, -1);
613 }
614
615 current = g_list_next (current);
616 }
617 g_hash_table_destroy (map);
618 }
619
snippet_info_sync_group(GuSnippetInfo * info,GuEditor * ec)620 void snippet_info_sync_group (GuSnippetInfo* info, GuEditor* ec) {
621 if (!info->current ||
622 GU_SNIPPET_EXPAND_INFO(info->current->data)->group_number == -1)
623 return;
624
625 GuSnippetExpandInfo* active = GU_SNIPPET_EXPAND_INFO (info->current->data);
626 GList* current = g_list_first (info->einfo);
627 gchar* text = NULL;
628 GtkTextIter start, end;
629
630 gtk_text_buffer_get_iter_at_mark (ec_buffer, &start, active->left_mark);
631 gtk_text_buffer_get_iter_at_mark (ec_buffer, &end, active->right_mark);
632 text = gtk_text_buffer_get_text (ec_buffer, &start, &end, TRUE);
633
634 while (current) {
635 GuSnippetExpandInfo* einfo = GU_SNIPPET_EXPAND_INFO (current->data);
636 if (einfo != active && einfo->group_number == active->group_number) {
637 gtk_text_buffer_get_iter_at_mark (ec_buffer, &start,
638 einfo->left_mark);
639 gtk_text_buffer_get_iter_at_mark (ec_buffer, &end,
640 einfo->right_mark);
641 gtk_text_buffer_delete (ec_buffer, &start, &end);
642 gtk_text_buffer_insert (ec_buffer, &start, text, -1);
643 }
644 current = g_list_next (current);
645 }
646 g_free (text);
647 }
648
snippet_info_num_cmp(gconstpointer a,gconstpointer b)649 gint snippet_info_num_cmp (gconstpointer a, gconstpointer b) {
650 return ( (GU_SNIPPET_EXPAND_INFO (a)->group_number <
651 GU_SNIPPET_EXPAND_INFO (b)->group_number)? -1: 1);
652 }
653
snippet_info_pos_cmp(gconstpointer a,gconstpointer b)654 gint snippet_info_pos_cmp (gconstpointer a, gconstpointer b) {
655 return ( (GU_SNIPPET_EXPAND_INFO (a)->start <
656 GU_SNIPPET_EXPAND_INFO (b)->start)? -1: 1);
657 }
658