1
/* ide-source-snippet-parser.c
3
* Copyright (C) 2013 Christian Hergert <christian@hergert.me>
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.
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.
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/>.
20
#include <glib/gi18n.h>
23
#include "ide-source-snippet.h"
24
#include "ide-source-snippet-chunk.h"
25
#include "ide-source-snippet-parser.h"
26
#include "ide-source-snippet-private.h"
28
struct _IdeSourceSnippetParser
30
GObject parent_instance;
43
G_DEFINE_TYPE (IdeSourceSnippetParser, ide_source_snippet_parser, G_TYPE_OBJECT)
46
IdeSourceSnippetParser *
47
ide_source_snippet_parser_new (void)
49
return g_object_new (IDE_TYPE_SOURCE_SNIPPET_PARSER, NULL);
53
ide_source_snippet_parser_flush_chunk (IdeSourceSnippetParser *parser)
55
IdeSourceSnippetChunk *chunk;
57
if (parser->cur_text->len)
59
chunk = ide_source_snippet_chunk_new ();
60
ide_source_snippet_chunk_set_spec (chunk, parser->cur_text->str);
61
parser->chunks = g_list_append (parser->chunks, chunk);
62
g_string_truncate (parser->cur_text, 0);
67
ide_source_snippet_parser_store (IdeSourceSnippetParser *parser)
69
IdeSourceSnippet *snippet;
73
ide_source_snippet_parser_flush_chunk (parser);
74
for (scope_iter = parser->scope; scope_iter; scope_iter = scope_iter->next)
76
snippet = ide_source_snippet_new (parser->cur_name, g_strdup(scope_iter->data));
77
ide_source_snippet_set_description(snippet, parser->cur_desc);
79
for (chunck_iter = parser->chunks; chunck_iter; chunck_iter = chunck_iter->next)
82
g_printerr ("%s: Tab: %02d Link: %02d Text: %s\n",
84
ide_source_snippet_chunk_get_tab_stop (chunck_iter->data),
85
ide_source_snippet_chunk_get_linked_chunk (chunck_iter->data),
86
ide_source_snippet_chunk_get_text (chunck_iter->data));
88
ide_source_snippet_add_chunk (snippet, chunck_iter->data);
91
parser->snippets = g_list_append (parser->snippets, snippet);
96
ide_source_snippet_parser_finish (IdeSourceSnippetParser *parser)
100
ide_source_snippet_parser_store(parser);
103
g_clear_pointer (&parser->cur_name, g_free);
105
g_string_truncate (parser->cur_text, 0);
107
g_list_foreach (parser->chunks, (GFunc) g_object_unref, NULL);
108
g_list_free (parser->chunks);
109
parser->chunks = NULL;
111
g_list_free_full(parser->scope, g_free);
112
parser->scope = NULL;
114
g_free(parser->cur_desc);
115
parser->cur_desc = NULL;
119
ide_source_snippet_parser_do_part_simple (IdeSourceSnippetParser *parser,
122
g_string_append (parser->cur_text, line);
126
ide_source_snippet_parser_do_part_n (IdeSourceSnippetParser *parser,
130
IdeSourceSnippetChunk *chunk;
132
g_return_if_fail (IDE_IS_SOURCE_SNIPPET_PARSER (parser));
133
g_return_if_fail (n >= -1);
134
g_return_if_fail (inner);
136
chunk = ide_source_snippet_chunk_new ();
137
ide_source_snippet_chunk_set_spec (chunk, n ? inner : "");
138
ide_source_snippet_chunk_set_tab_stop (chunk, n);
139
parser->chunks = g_list_append (parser->chunks, chunk);
143
ide_source_snippet_parser_do_part_linked (IdeSourceSnippetParser *parser,
146
IdeSourceSnippetChunk *chunk;
149
chunk = ide_source_snippet_chunk_new ();
152
g_snprintf (text, sizeof text, "$%d", n);
153
text[sizeof text - 1] = '\0';
154
ide_source_snippet_chunk_set_spec (chunk, text);
158
ide_source_snippet_chunk_set_spec (chunk, "");
159
ide_source_snippet_chunk_set_tab_stop (chunk, 0);
161
parser->chunks = g_list_append (parser->chunks, chunk);
165
ide_source_snippet_parser_do_part_named (IdeSourceSnippetParser *parser,
168
IdeSourceSnippetChunk *chunk;
171
chunk = ide_source_snippet_chunk_new ();
172
spec = g_strdup_printf ("$%s", name);
173
ide_source_snippet_chunk_set_spec (chunk, spec);
174
ide_source_snippet_chunk_set_tab_stop (chunk, -1);
175
parser->chunks = g_list_append (parser->chunks, chunk);
180
parse_variable (const gchar *line,
183
const gchar **endptr,
186
gboolean has_inner = FALSE;
196
g_assert (*line == '$');
214
if (g_ascii_isdigit (*line))
217
*n = strtol (line, &end, 10);
218
if (((*n == LONG_MIN) || (*n == LONG_MAX)) && errno == ERANGE)
224
else if (g_ascii_isalpha (*line))
228
for (cur = line; *cur; cur++)
230
if (g_ascii_isalnum (*cur))
235
*name = g_strndup (line, cur - line);
247
for (i = 0; line[i]; i++)
265
*inner = g_strndup (line, i);
266
*endptr = &line[i + 1];
280
ide_source_snippet_parser_do_part (IdeSourceSnippetParser *parser,
290
g_assert (*line == '\t');
298
if (!(dollar = strchr (line, '$')))
300
ide_source_snippet_parser_do_part_simple (parser, line);
305
* Parse up to the next $ as a simple.
306
* If it is $N or ${N} then it is a linked chunk w/o tabstop.
307
* If it is ${N:""} then it is a chunk w/ tabstop.
308
* If it is ${blah|upper} then it is a non-tab stop chunk performing
309
* some sort of of expansion.
312
g_assert (dollar >= line);
316
str = g_strndup (line, (dollar - line));
317
ide_source_snippet_parser_do_part_simple (parser, str);
325
if (!parse_variable (line, &n, &inner, &line, &name))
327
ide_source_snippet_parser_do_part_simple (parser, line);
332
g_printerr ("Parse Variable: N=%d inner=\"%s\"\n", n, inner);
333
g_printerr (" Left over: \"%s\"\n", line);
336
ide_source_snippet_parser_flush_chunk (parser);
340
ide_source_snippet_parser_do_part_n (parser, n, inner);
344
else if (n == -2 && name)
345
ide_source_snippet_parser_do_part_named (parser, name);
347
ide_source_snippet_parser_do_part_linked (parser, n);
363
ide_source_snippet_parser_do_snippet (IdeSourceSnippetParser *parser,
366
parser->cur_name = g_strstrip (g_strdup (&line[8]));
370
ide_source_snippet_parser_do_snippet_scope (IdeSourceSnippetParser *parser,
378
scope_list = g_strsplit (&line[8], ",", -1);
380
for (i = 0; scope_list[i]; i++)
383
for (iter = parser->scope; iter; iter = iter->next)
385
if (g_strcmp0(iter->data, scope_list[i]) == 0)
391
parser->scope = g_list_append(parser->scope,
392
g_strstrip (g_strdup (scope_list[i])));
395
g_strfreev(scope_list);
399
ide_source_snippet_parser_do_snippet_description (IdeSourceSnippetParser *parser,
402
if (parser->cur_desc)
404
g_free(parser->cur_desc);
405
parser->cur_desc = NULL;
408
parser->cur_desc = g_strstrip (g_strdup (&line[7]));
412
ide_source_snippet_parser_feed_line (IdeSourceSnippetParser *parser,
425
if (parser->cur_name)
426
g_string_append_c (parser->cur_text, '\n');
433
if (parser->cur_name)
436
gboolean add_default_scope = TRUE;
437
for (iter = parser->scope; iter; iter = iter->next)
439
if (g_strcmp0(iter->data, basename) == 0)
441
add_default_scope = FALSE;
446
if (add_default_scope)
447
parser->scope = g_list_append(parser->scope,
448
g_strstrip (g_strdup (basename)));
450
if (parser->cur_text->len || parser->chunks)
451
g_string_append_c (parser->cur_text, '\n');
452
ide_source_snippet_parser_do_part (parser, line);
457
if (g_str_has_prefix (line, "snippet"))
459
ide_source_snippet_parser_finish (parser);
460
ide_source_snippet_parser_do_snippet (parser, line);
465
if (parser->cur_text->len || parser->chunks)
467
ide_source_snippet_parser_store(parser);
469
g_string_truncate (parser->cur_text, 0);
471
g_list_foreach (parser->chunks, (GFunc) g_object_unref, NULL);
472
g_list_free (parser->chunks);
473
parser->chunks = NULL;
475
g_list_free_full(parser->scope, g_free);
476
parser->scope = NULL;
479
if (g_str_has_prefix(line, "- scope"))
481
ide_source_snippet_parser_do_snippet_scope (parser, line);
485
if (g_str_has_prefix(line, "- desc"))
487
ide_source_snippet_parser_do_snippet_description (parser, line);
493
g_warning (_("Invalid snippet at line %d: %s"), parser->lineno, line);
499
ide_source_snippet_parser_load_from_file (IdeSourceSnippetParser *parser,
503
GFileInputStream *file_stream;
504
GDataInputStream *data_stream;
505
GError *local_error = NULL;
507
gchar *basename = NULL;
509
g_return_val_if_fail (IDE_IS_SOURCE_SNIPPET_PARSER (parser), FALSE);
510
g_return_val_if_fail (G_IS_FILE (file), FALSE);
512
basename = g_file_get_basename (file);
516
if (strstr (basename, "."))
517
*strstr (basename, ".") = '\0';
520
file_stream = g_file_read (file, NULL, error);
524
data_stream = g_data_input_stream_new (G_INPUT_STREAM (file_stream));
525
g_object_unref (file_stream);
528
line = g_data_input_stream_read_line_utf8 (data_stream, NULL, NULL, &local_error);
529
if (line && local_error)
531
g_propagate_error (error, local_error);
536
ide_source_snippet_parser_feed_line (parser, basename, line);
541
ide_source_snippet_parser_finish (parser);
548
ide_source_snippet_parser_get_snippets (IdeSourceSnippetParser *parser)
550
g_return_val_if_fail (IDE_IS_SOURCE_SNIPPET_PARSER (parser), NULL);
551
return parser->snippets;
555
ide_source_snippet_parser_finalize (GObject *object)
557
IdeSourceSnippetParser *self = IDE_SOURCE_SNIPPET_PARSER (object);
559
g_list_foreach (self->snippets, (GFunc) g_object_unref, NULL);
560
g_list_free (self->snippets);
561
self->snippets = NULL;
563
g_list_foreach (self->chunks, (GFunc) g_object_unref, NULL);
564
g_list_free (self->chunks);
567
g_list_free_full(self->scope, g_free);
571
g_string_free (self->cur_text, TRUE);
573
g_free (self->cur_name);
574
self->cur_name = NULL;
578
g_free (self->cur_desc);
579
self->cur_desc = NULL;
582
G_OBJECT_CLASS (ide_source_snippet_parser_parent_class)->finalize (object);
586
ide_source_snippet_parser_class_init (IdeSourceSnippetParserClass *klass)
588
GObjectClass *object_class;
590
object_class = G_OBJECT_CLASS (klass);
591
object_class->finalize = ide_source_snippet_parser_finalize;
595
ide_source_snippet_parser_init (IdeSourceSnippetParser *parser)
598
parser->cur_text = g_string_new (NULL);
599
parser->scope = NULL;
600
parser->cur_desc = NULL;