Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <errno.h> | ||
2 | #include <stdbool.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include "state.h" | ||
6 | #include "command/args.h" | ||
7 | #include "command/error.h" | ||
8 | #include "command/run.h" | ||
9 | #include "config.h" | ||
10 | #include "editor.h" | ||
11 | #include "filetype.h" | ||
12 | #include "syntax/merge.h" | ||
13 | #include "util/bsearch.h" | ||
14 | #include "util/debug.h" | ||
15 | #include "util/hashset.h" | ||
16 | #include "util/intern.h" | ||
17 | #include "util/log.h" | ||
18 | #include "util/macros.h" | ||
19 | #include "util/path.h" | ||
20 | #include "util/strtonum.h" | ||
21 | #include "util/xmalloc.h" | ||
22 | #include "util/xsnprintf.h" | ||
23 | #include "util/xstring.h" | ||
24 | |||
25 | 3786 | static bool in_syntax(EditorState *e) | |
26 | { | ||
27 |
1/4✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→6) taken 3786 times.
✗ Branch 2 (4→5) not taken.
✗ Branch 3 (4→6) not taken.
|
3786 | return likely(e->syn.current_syntax) || error_msg(&e->err, "No syntax started"); |
28 | } | ||
29 | |||
30 | 2734 | static bool in_state(EditorState *e) | |
31 | { | ||
32 |
1/2✓ Branch 0 (3→4) taken 2734 times.
✗ Branch 1 (3→9) not taken.
|
2734 | if (unlikely(!in_syntax(e))) { |
33 | return false; | ||
34 | } | ||
35 |
1/4✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→8) taken 2734 times.
✗ Branch 2 (6→7) not taken.
✗ Branch 3 (6→8) not taken.
|
2734 | return likely(e->syn.current_state) || error_msg(&e->err, "No state started"); |
36 | } | ||
37 | |||
38 | 1185 | static void close_state(EditorState *e) | |
39 | { | ||
40 | 1185 | const State *state = e->syn.current_state; | |
41 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→6) taken 1185 times.
|
1185 | if (!state) { |
42 | return; | ||
43 | } | ||
44 | |||
45 | ✗ | if (unlikely(state->type == STATE_INVALID)) { | |
46 | // This error applies to the state itself rather than the last | ||
47 | // command, so it doesn't make sense to include the command name | ||
48 | // in the error message | ||
49 | ✗ | error_msg_for_cmd(&e->err, NULL, "No default action in state '%s'", state->name); | |
50 | } | ||
51 | |||
52 | ✗ | e->syn.current_state = NULL; | |
53 | } | ||
54 | |||
55 | 2645 | static State *find_or_add_state(EditorState *e, const char *name) | |
56 | { | ||
57 | 2645 | State *state = find_state(e->syn.current_syntax, name); | |
58 |
2/2✓ Branch 0 (3→4) taken 890 times.
✓ Branch 1 (3→9) taken 1755 times.
|
2645 | if (state) { |
59 | return state; | ||
60 | } | ||
61 | |||
62 | 890 | state = xnew0(State, 1); | |
63 | 890 | state->name = xstrdup(name); | |
64 | 890 | state->defined = false; | |
65 | 890 | state->type = STATE_INVALID; | |
66 | |||
67 |
2/2✓ Branch 0 (6→7) taken 133 times.
✓ Branch 1 (6→8) taken 757 times.
|
890 | if (e->syn.current_syntax->states.count == 0) { |
68 | 133 | e->syn.current_syntax->start_state = state; | |
69 | } | ||
70 | |||
71 | 890 | return hashmap_insert(&e->syn.current_syntax->states, state->name, state); | |
72 | } | ||
73 | |||
74 | 2378 | static State *reference_state(EditorState *e, const char *name) | |
75 | { | ||
76 |
2/2✓ Branch 0 (2→3) taken 623 times.
✓ Branch 1 (2→4) taken 1755 times.
|
2378 | if (streq(name, "this")) { |
77 | 623 | return e->syn.current_state; | |
78 | } | ||
79 | |||
80 | 1755 | State *state = find_or_add_state(e, name); | |
81 |
1/4✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→8) taken 1755 times.
✗ Branch 2 (6→7) not taken.
✗ Branch 3 (6→8) not taken.
|
1755 | if (unlikely((e->syn.flags & SYN_LINT) && state == e->syn.current_state)) { |
82 | ✗ | error_msg ( | |
83 | &e->err, | ||
84 | "destination '%s' can be optimized to 'this' in '%s' syntax", | ||
85 | name, | ||
86 | ✗ | e->syn.current_syntax->name | |
87 | ); | ||
88 | } | ||
89 | |||
90 | return state; | ||
91 | } | ||
92 | |||
93 | 267 | static bool in_subsyntax(EditorState *e) | |
94 | { | ||
95 | 267 | bool ss = likely(is_subsyntax(e->syn.current_syntax)); | |
96 |
1/4✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→6) taken 267 times.
✗ Branch 2 (4→5) not taken.
✗ Branch 3 (4→6) not taken.
|
267 | return ss || error_msg(&e->err, "Destination state 'END' only allowed in a subsyntax"); |
97 | } | ||
98 | |||
99 | 182 | static Syntax *must_find_subsyntax(EditorState *e, const char *name) | |
100 | { | ||
101 | 182 | Syntax *syntax = find_any_syntax(&e->syntaxes, name); | |
102 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→6) taken 182 times.
|
182 | if (unlikely(!syntax)) { |
103 | ✗ | error_msg(&e->err, "No such syntax '%s'", name); | |
104 | ✗ | return NULL; | |
105 | } | ||
106 |
1/2✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→9) taken 182 times.
|
182 | if (unlikely(!is_subsyntax(syntax))) { |
107 | ✗ | error_msg(&e->err, "Syntax '%s' is not a subsyntax", name); | |
108 | ✗ | return NULL; | |
109 | } | ||
110 | return syntax; | ||
111 | } | ||
112 | |||
113 | 169 | static bool subsyntax_call(EditorState *e, const char *name, const char *ret, State **dest) | |
114 | { | ||
115 | 169 | Syntax *subsyn = must_find_subsyntax(e, name); | |
116 | |||
117 | 169 | SyntaxMerge m = { | |
118 | .subsyn = subsyn, | ||
119 | .return_state = NULL, | ||
120 | .delim = NULL, | ||
121 | .delim_len = 0, | ||
122 | }; | ||
123 | |||
124 |
2/2✓ Branch 0 (3→4) taken 17 times.
✓ Branch 1 (3→6) taken 152 times.
|
169 | if (streq(ret, "END")) { |
125 |
1/2✓ Branch 0 (5→9) taken 17 times.
✗ Branch 1 (5→12) not taken.
|
17 | if (!in_subsyntax(e)) { |
126 | return false; | ||
127 | } | ||
128 |
1/2✓ Branch 0 (6→7) taken 152 times.
✗ Branch 1 (6→12) not taken.
|
152 | } else if (subsyn) { |
129 | 152 | m.return_state = reference_state(e, ret); | |
130 | } | ||
131 | |||
132 |
1/2✓ Branch 0 (9→10) taken 169 times.
✗ Branch 1 (9→12) not taken.
|
169 | if (subsyn) { |
133 | 169 | *dest = merge_syntax(e->syn.current_syntax, &m, &e->styles); | |
134 | 169 | return true; | |
135 | } | ||
136 | |||
137 | return false; | ||
138 | } | ||
139 | |||
140 | 2645 | static bool destination_state(EditorState *e, const char *name, State **dest) | |
141 | { | ||
142 | 2645 | const char *sep = strchr(name, ':'); | |
143 |
2/2✓ Branch 0 (2→3) taken 169 times.
✓ Branch 1 (2→6) taken 2476 times.
|
2645 | if (sep) { |
144 | // subsyntax:returnstate | ||
145 | 169 | char *sub = xstrcut(name, sep - name); | |
146 | 169 | bool success = subsyntax_call(e, sub, sep + 1, dest); | |
147 | 169 | free(sub); | |
148 | 169 | return success; | |
149 | } | ||
150 | |||
151 |
2/2✓ Branch 0 (6→7) taken 250 times.
✓ Branch 1 (6→10) taken 2226 times.
|
2476 | if (streq(name, "END")) { |
152 |
1/2✓ Branch 0 (8→9) taken 250 times.
✗ Branch 1 (8→12) not taken.
|
250 | if (!in_subsyntax(e)) { |
153 | return false; | ||
154 | } | ||
155 | 250 | *dest = NULL; | |
156 | 250 | return true; | |
157 | } | ||
158 | |||
159 | 2226 | *dest = reference_state(e, name); | |
160 | 2226 | return true; | |
161 | } | ||
162 | |||
163 | 2088 | static void lint_emit_name(EditorState *e, const char *ename, const State *dest) | |
164 | { | ||
165 | 2088 | if ( | |
166 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→7) taken 2088 times.
|
2088 | (e->syn.flags & SYN_LINT) |
167 | ✗ | && ename | |
168 | ✗ | && dest | |
169 | ✗ | && dest->emit_name | |
170 | ✗ | && interned_strings_equal(ename, dest->emit_name) | |
171 | ) { | ||
172 | ✗ | error_msg ( | |
173 | &e->err, | ||
174 | "emit-name '%s' not needed (destination state uses same emit-name)", | ||
175 | ename | ||
176 | ); | ||
177 | } | ||
178 | 2088 | } | |
179 | |||
180 | 1844 | static Condition *add_condition ( | |
181 | EditorState *e, | ||
182 | ConditionType type, | ||
183 | const char *dest, | ||
184 | const char *emit | ||
185 | ) { | ||
186 | 1844 | BUG_ON(!dest && cond_type_has_destination(type)); | |
187 |
1/2✓ Branch 0 (6→7) taken 1844 times.
✗ Branch 1 (6→18) not taken.
|
1844 | if (!in_state(e)) { |
188 | return NULL; | ||
189 | } | ||
190 | |||
191 | 1844 | State *d = NULL; | |
192 |
3/4✓ Branch 0 (7→8) taken 1755 times.
✓ Branch 1 (7→10) taken 89 times.
✓ Branch 2 (9→10) taken 1755 times.
✗ Branch 3 (9→18) not taken.
|
1844 | if (dest && !destination_state(e, dest, &d)) { |
193 | return NULL; | ||
194 | } | ||
195 | |||
196 |
2/2✓ Branch 0 (10→11) taken 531 times.
✓ Branch 1 (10→12) taken 1313 times.
|
1844 | emit = emit ? str_intern(emit) : NULL; |
197 | |||
198 | 1844 | if ( | |
199 | 1844 | type != COND_HEREDOCEND | |
200 |
2/2✓ Branch 0 (12→13) taken 1735 times.
✓ Branch 1 (12→15) taken 109 times.
|
1844 | && type != COND_INLIST |
201 |
1/2✓ Branch 0 (13→14) taken 1735 times.
✗ Branch 1 (13→15) not taken.
|
1735 | && type != COND_INLIST_BUFFER |
202 | ) { | ||
203 | 1735 | lint_emit_name(e, emit, d); | |
204 | } | ||
205 | |||
206 | 1844 | Condition *c = xnew0(Condition, 1); | |
207 | 1844 | c->a.destination = d; | |
208 | 1844 | c->a.emit_name = emit; | |
209 | 1844 | c->type = type; | |
210 | 1844 | ptr_array_append(&e->syn.current_state->conds, c); | |
211 | 1844 | return c; | |
212 | } | ||
213 | |||
214 | 18 | static bool cmd_bufis(EditorState *e, const CommandArgs *a) | |
215 | { | ||
216 | 18 | const char *str = a->args[0]; | |
217 | 18 | const size_t len = strlen(str); | |
218 | 18 | Condition *c; | |
219 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 18 times.
|
18 | if (unlikely(len > ARRAYLEN(c->u.str.buf))) { |
220 | ✗ | return error_msg ( | |
221 | &e->err, | ||
222 | "Maximum length of string is %zu bytes", | ||
223 | ARRAYLEN(c->u.str.buf) | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | 18 | ConditionType type = a->flags[0] == 'i' ? COND_BUFIS_ICASE : COND_BUFIS; | |
228 | 18 | c = add_condition(e, type, a->args[1], a->args[2]); | |
229 |
1/2✓ Branch 0 (5→6) taken 18 times.
✗ Branch 1 (5→7) not taken.
|
18 | if (!c) { |
230 | return false; | ||
231 | } | ||
232 | |||
233 | 18 | memcpy(c->u.str.buf, str, len); | |
234 | 18 | c->u.str.len = len; | |
235 | 18 | return true; | |
236 | } | ||
237 | |||
238 | 1513 | static bool cmd_char(EditorState *e, const CommandArgs *a) | |
239 | { | ||
240 | 1513 | const char *chars = a->args[0]; | |
241 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 1513 times.
|
1513 | if (unlikely(chars[0] == '\0')) { |
242 | ✗ | return error_msg(&e->err, "char argument can't be empty"); | |
243 | } | ||
244 | |||
245 | 1513 | bool add_to_buffer = cmdargs_has_flag(a, 'b'); | |
246 | 1513 | bool invert = cmdargs_has_flag(a, 'n'); | |
247 | 1513 | ConditionType type; | |
248 |
2/2✓ Branch 0 (6→7) taken 1288 times.
✓ Branch 1 (6→10) taken 225 times.
|
1513 | if (add_to_buffer) { |
249 | type = COND_CHAR_BUFFER; | ||
250 |
4/4✓ Branch 0 (7→8) taken 1259 times.
✓ Branch 1 (7→9) taken 29 times.
✓ Branch 2 (8→9) taken 529 times.
✓ Branch 3 (8→10) taken 730 times.
|
1288 | } else if (!invert && chars[1] == '\0') { |
251 | type = COND_CHAR1; | ||
252 | } else { | ||
253 | 558 | type = COND_CHAR; | |
254 | } | ||
255 | |||
256 | 1513 | Condition *c = add_condition(e, type, a->args[1], a->args[2]); | |
257 |
1/2✓ Branch 0 (11→12) taken 1513 times.
✗ Branch 1 (11→17) not taken.
|
1513 | if (!c) { |
258 | return false; | ||
259 | } | ||
260 | |||
261 |
2/2✓ Branch 0 (12→13) taken 730 times.
✓ Branch 1 (12→14) taken 783 times.
|
1513 | if (type == COND_CHAR1) { |
262 | 730 | c->u.ch = (unsigned char)chars[0]; | |
263 | } else { | ||
264 | 783 | bitset_add_char_range(c->u.bitset, chars); | |
265 |
2/2✓ Branch 0 (15→16) taken 42 times.
✓ Branch 1 (15→17) taken 741 times.
|
783 | if (invert) { |
266 | 42 | BITSET_INVERT(c->u.bitset); | |
267 | } | ||
268 | } | ||
269 | |||
270 | return true; | ||
271 | } | ||
272 | |||
273 | 65 | static bool cmd_default(EditorState *e, const CommandArgs *a) | |
274 | { | ||
275 | 65 | close_state(e); | |
276 |
1/2✓ Branch 0 (4→5) taken 65 times.
✗ Branch 1 (4→13) not taken.
|
65 | if (!in_syntax(e)) { |
277 | return false; | ||
278 | } | ||
279 | |||
280 | 65 | const char *value = str_intern(a->args[0]); | |
281 | 65 | HashMap *map = &e->syn.current_syntax->default_styles; | |
282 |
2/2✓ Branch 0 (12→7) taken 77 times.
✓ Branch 1 (12→13) taken 65 times.
|
142 | for (size_t i = 1, n = a->nr_args; i < n; i++) { |
283 | 77 | const char *name = a->args[i]; | |
284 | 77 | const void *oldval = hashmap_insert_or_replace(map, xstrdup(name), (char*)value); | |
285 |
1/2✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→11) taken 77 times.
|
77 | if (unlikely(oldval)) { |
286 | ✗ | error_msg(&e->err, "'%s' argument specified multiple times", name); | |
287 | } | ||
288 | } | ||
289 | |||
290 | return true; | ||
291 | } | ||
292 | |||
293 | 353 | static bool cmd_eat(EditorState *e, const CommandArgs *a) | |
294 | { | ||
295 |
1/2✓ Branch 0 (3→4) taken 353 times.
✗ Branch 1 (3→10) not taken.
|
353 | if (!in_state(e)) { |
296 | return false; | ||
297 | } | ||
298 | |||
299 | 353 | const char *dest = a->args[0]; | |
300 |
1/2✓ Branch 0 (5→6) taken 353 times.
✗ Branch 1 (5→10) not taken.
|
353 | if (!destination_state(e, dest, &e->syn.current_state->default_action.destination)) { |
301 | return false; | ||
302 | } | ||
303 | |||
304 |
2/2✓ Branch 0 (6→7) taken 86 times.
✓ Branch 1 (6→8) taken 267 times.
|
353 | const char *emit = a->args[1] ? str_intern(a->args[1]) : NULL; |
305 | 353 | State *curstate = e->syn.current_state; | |
306 | 353 | lint_emit_name(e, emit, curstate->default_action.destination); | |
307 | 353 | curstate->default_action.emit_name = emit; | |
308 | 353 | curstate->type = STATE_EAT; | |
309 | 353 | e->syn.current_state = NULL; | |
310 | 353 | return true; | |
311 | } | ||
312 | |||
313 | 13 | static bool cmd_heredocbegin(EditorState *e, const CommandArgs *a) | |
314 | { | ||
315 |
1/2✓ Branch 0 (3→4) taken 13 times.
✗ Branch 1 (3→9) not taken.
|
13 | if (!in_state(e)) { |
316 | return false; | ||
317 | } | ||
318 | |||
319 | 13 | Syntax *subsyn = must_find_subsyntax(e, a->args[0]); | |
320 |
1/2✓ Branch 0 (5→6) taken 13 times.
✗ Branch 1 (5→9) not taken.
|
13 | if (!subsyn) { |
321 | return false; | ||
322 | } | ||
323 | |||
324 | // default_action.destination is used as the return state | ||
325 | 13 | const char *ret = a->args[1]; | |
326 |
1/2✓ Branch 0 (7→8) taken 13 times.
✗ Branch 1 (7→9) not taken.
|
13 | if (!destination_state(e, ret, &e->syn.current_state->default_action.destination)) { |
327 | return false; | ||
328 | } | ||
329 | |||
330 | 13 | e->syn.current_state->default_action.emit_name = NULL; | |
331 | 13 | e->syn.current_state->type = STATE_HEREDOCBEGIN; | |
332 | 13 | e->syn.current_state->heredoc.subsyntax = subsyn; | |
333 | 13 | e->syn.current_state = NULL; | |
334 | |||
335 | // Normally merge() marks subsyntax used but in case of heredocs merge() | ||
336 | // is not called when syntax file is loaded | ||
337 | 13 | subsyn->used = true; | |
338 | 13 | return true; | |
339 | } | ||
340 | |||
341 | 9 | static bool cmd_heredocend(EditorState *e, const CommandArgs *a) | |
342 | { | ||
343 | 9 | Condition *c = add_condition(e, COND_HEREDOCEND, a->args[0], a->args[1]); | |
344 |
1/2✓ Branch 0 (3→4) taken 9 times.
✗ Branch 1 (3→7) not taken.
|
9 | if (unlikely(!c)) { |
345 | return false; | ||
346 | } | ||
347 | 9 | BUG_ON(!e->syn.current_syntax); | |
348 | 9 | e->syn.current_syntax->heredoc = true; | |
349 | 9 | return true; | |
350 | } | ||
351 | |||
352 | // Forward declaration, used in cmd_include() and cmd_require() | ||
353 | static int read_syntax(EditorState *e, const char *filename, SyntaxLoadFlags flags); | ||
354 | |||
355 | ✗ | static bool cmd_include(EditorState *e, const CommandArgs *a) | |
356 | { | ||
357 | ✗ | SyntaxLoadFlags flags = SYN_MUST_EXIST; | |
358 | ✗ | if (a->flags[0] == 'b') { | |
359 | ✗ | flags |= SYN_BUILTIN; | |
360 | } | ||
361 | ✗ | int r = read_syntax(e, a->args[0], flags); | |
362 | ✗ | return r == 0; | |
363 | } | ||
364 | |||
365 | 97 | static bool cmd_list(EditorState *e, const CommandArgs *a) | |
366 | { | ||
367 | 97 | close_state(e); | |
368 |
1/2✓ Branch 0 (4→5) taken 97 times.
✗ Branch 1 (4→17) not taken.
|
97 | if (!in_syntax(e)) { |
369 | return false; | ||
370 | } | ||
371 | |||
372 | 97 | char **args = a->args; | |
373 | 97 | const char *name = args[0]; | |
374 | 97 | StringList *list = find_string_list(e->syn.current_syntax, name); | |
375 |
2/2✓ Branch 0 (6→7) taken 7 times.
✓ Branch 1 (6→10) taken 90 times.
|
97 | if (!list) { |
376 | 7 | list = xnew0(StringList, 1); | |
377 | 7 | hashmap_insert(&e->syn.current_syntax->string_lists, xstrdup(name), list); | |
378 |
1/2✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→12) taken 90 times.
|
90 | } else if (unlikely(list->defined)) { |
379 | ✗ | return error_msg(&e->err, "List '%s' already exists", name); | |
380 | } | ||
381 | 97 | list->defined = true; | |
382 | |||
383 | 97 | bool icase = a->flags[0] == 'i'; | |
384 | 97 | HashSet *set = &list->strings; | |
385 | 97 | hashset_init(set, a->nr_args - 1, icase); | |
386 |
2/2✓ Branch 0 (16→14) taken 3509 times.
✓ Branch 1 (16→17) taken 97 times.
|
3606 | for (size_t i = 1, n = a->nr_args; i < n; i++) { |
387 | 3509 | const char *str = args[i]; | |
388 | 3509 | hashset_insert(set, str, strlen(str)); | |
389 | } | ||
390 | return true; | ||
391 | } | ||
392 | |||
393 | 100 | static bool cmd_inlist(EditorState *e, const CommandArgs *a) | |
394 | { | ||
395 | 100 | char **args = a->args; | |
396 | 100 | const char *name = args[0]; | |
397 |
1/2✓ Branch 0 (3→4) taken 100 times.
✗ Branch 1 (3→5) not taken.
|
100 | ConditionType type = cmdargs_has_flag(a, 'b') ? COND_INLIST_BUFFER : COND_INLIST; |
398 |
2/2✓ Branch 0 (5→6) taken 27 times.
✓ Branch 1 (5→7) taken 73 times.
|
127 | Condition *c = add_condition(e, type, args[1], args[2] ? args[2] : name); |
399 | |||
400 |
1/2✓ Branch 0 (8→9) taken 100 times.
✗ Branch 1 (8→15) not taken.
|
100 | if (!c) { |
401 | return false; | ||
402 | } | ||
403 | |||
404 | 100 | StringList *list = find_string_list(e->syn.current_syntax, name); | |
405 |
2/2✓ Branch 0 (10→11) taken 90 times.
✓ Branch 1 (10→14) taken 10 times.
|
100 | if (unlikely(!list)) { |
406 | // Add undefined list | ||
407 | 90 | list = xnew0(StringList, 1); | |
408 | 90 | hashmap_insert(&e->syn.current_syntax->string_lists, xstrdup(name), list); | |
409 | } | ||
410 | |||
411 | 100 | list->used = true; | |
412 | 100 | c->u.str_list = list; | |
413 | 100 | return true; | |
414 | } | ||
415 | |||
416 | 524 | static bool cmd_noeat(EditorState *e, const CommandArgs *a) | |
417 | { | ||
418 | 524 | State *dest; | |
419 |
2/4✓ Branch 0 (3→4) taken 524 times.
✗ Branch 1 (3→6) not taken.
✗ Branch 2 (5→6) not taken.
✓ Branch 3 (5→7) taken 524 times.
|
524 | if (unlikely(!in_state(e) || !destination_state(e, a->args[0], &dest))) { |
420 | ✗ | return false; | |
421 | } | ||
422 | |||
423 |
1/2✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→9) taken 524 times.
|
524 | if (unlikely(dest == e->syn.current_state)) { |
424 | ✗ | return error_msg(&e->err, "using noeat to jump to same state causes infinite loop"); | |
425 | } | ||
426 | |||
427 | 524 | e->syn.current_state->default_action.destination = dest; | |
428 | 524 | e->syn.current_state->default_action.emit_name = NULL; | |
429 |
2/2✓ Branch 0 (9→10) taken 497 times.
✓ Branch 1 (9→11) taken 27 times.
|
524 | e->syn.current_state->type = a->flags[0] == 'b' ? STATE_NOEAT_BUFFER : STATE_NOEAT; |
430 | 524 | e->syn.current_state = NULL; | |
431 | 524 | return true; | |
432 | } | ||
433 | |||
434 | 89 | static bool cmd_recolor(EditorState *e, const CommandArgs *a) | |
435 | { | ||
436 | // If length is not specified then buffered bytes will be recolored | ||
437 | 89 | ConditionType type = COND_RECOLOR_BUFFER; | |
438 | 89 | size_t len = 0; | |
439 | |||
440 | 89 | const char *len_str = a->args[1]; | |
441 |
2/2✓ Branch 0 (2→3) taken 57 times.
✓ Branch 1 (2→8) taken 32 times.
|
89 | if (len_str) { |
442 | 57 | type = COND_RECOLOR; | |
443 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 57 times.
|
57 | if (unlikely(!str_to_size(len_str, &len))) { |
444 | ✗ | return error_msg(&e->err, "invalid number: '%s'", len_str); | |
445 | } | ||
446 |
1/2✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→8) taken 57 times.
|
57 | if (unlikely(len < 1 || len > 2500)) { |
447 | ✗ | return error_msg(&e->err, "number of bytes must be between 1-2500 (got %zu)", len); | |
448 | } | ||
449 | } | ||
450 | |||
451 | 89 | Condition *c = add_condition(e, type, NULL, a->args[0]); | |
452 |
1/2✓ Branch 0 (9→10) taken 89 times.
✗ Branch 1 (9→12) not taken.
|
89 | if (!c) { |
453 | return false; | ||
454 | } | ||
455 | |||
456 |
2/2✓ Branch 0 (10→11) taken 57 times.
✓ Branch 1 (10→12) taken 32 times.
|
89 | if (type == COND_RECOLOR) { |
457 | 57 | c->u.recolor_len = len; | |
458 | } | ||
459 | |||
460 | return true; | ||
461 | } | ||
462 | |||
463 | 13 | static bool cmd_require(EditorState *e, const CommandArgs *a) | |
464 | { | ||
465 | 13 | char buf[8192]; | |
466 | 13 | char *path; | |
467 | 13 | size_t path_len; | |
468 | 13 | HashSet *set; | |
469 | 13 | SyntaxLoadFlags flags = SYN_MUST_EXIST; | |
470 | |||
471 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 13 times.
|
13 | if (a->flags[0] == 'f') { |
472 | ✗ | set = &e->required_syntax_files; | |
473 | ✗ | path = a->args[0]; | |
474 | ✗ | path_len = strlen(path); | |
475 | } else { | ||
476 | 13 | set = &e->required_syntax_builtins; | |
477 | 13 | path_len = xsnprintf(buf, sizeof(buf), "syntax/inc/%s", a->args[0]); | |
478 | 13 | path = buf; | |
479 | 13 | flags |= SYN_BUILTIN; | |
480 | } | ||
481 | |||
482 |
2/2✓ Branch 0 (6→7) taken 4 times.
✓ Branch 1 (6→11) taken 9 times.
|
13 | if (hashset_get(set, path, path_len)) { |
483 | return true; | ||
484 | } | ||
485 | |||
486 | 4 | const SyntaxLoadFlags save = e->syn.flags; | |
487 | 4 | e->syn.flags &= ~SYN_WARN_ON_UNUSED_SUBSYN; | |
488 | 4 | int r = read_syntax(e, path, flags); | |
489 | 4 | e->syn.flags = save; | |
490 |
1/2✓ Branch 0 (8→9) taken 4 times.
✗ Branch 1 (8→11) not taken.
|
4 | if (r != 0) { |
491 | return false; | ||
492 | } | ||
493 | |||
494 | 4 | hashset_insert(set, path, path_len); | |
495 | 4 | return true; | |
496 | } | ||
497 | |||
498 | 890 | static bool cmd_state(EditorState *e, const CommandArgs *a) | |
499 | { | ||
500 | 890 | close_state(e); | |
501 |
1/2✓ Branch 0 (4→5) taken 890 times.
✗ Branch 1 (4→15) not taken.
|
890 | if (!in_syntax(e)) { |
502 | return false; | ||
503 | } | ||
504 | |||
505 | 890 | const char *name = a->args[0]; | |
506 |
2/4✓ Branch 0 (5→6) taken 890 times.
✗ Branch 1 (5→7) not taken.
✗ Branch 2 (6→7) not taken.
✓ Branch 3 (6→8) taken 890 times.
|
890 | if (unlikely(streq(name, "END") || streq(name, "this"))) { |
507 | ✗ | return error_msg(&e->err, "'%s' is reserved state name", name); | |
508 | } | ||
509 | |||
510 | 890 | State *state = find_or_add_state(e, name); | |
511 |
1/2✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→11) taken 890 times.
|
890 | if (unlikely(state->defined)) { |
512 | ✗ | return error_msg(&e->err, "State '%s' already exists", name); | |
513 | } | ||
514 | |||
515 | 890 | state->defined = true; | |
516 |
2/2✓ Branch 0 (11→12) taken 613 times.
✓ Branch 1 (11→13) taken 277 times.
|
1503 | state->emit_name = str_intern(a->args[1] ? a->args[1] : name); |
517 | 890 | e->syn.current_state = state; | |
518 | 890 | return true; | |
519 | } | ||
520 | |||
521 | 115 | static bool cmd_str(EditorState *e, const CommandArgs *a) | |
522 | { | ||
523 | 115 | const char *str = a->args[0]; | |
524 | 115 | size_t len = strlen(str); | |
525 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 115 times.
|
115 | if (unlikely(len < 2)) { |
526 | ✗ | return error_msg(&e->err, "string should be at least 2 bytes; use 'char' for single bytes"); | |
527 | } | ||
528 | |||
529 | 115 | Condition *c; | |
530 | 115 | size_t maxlen = ARRAYLEN(c->u.str.buf); | |
531 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 115 times.
|
115 | if (unlikely(len > maxlen)) { |
532 | ✗ | return error_msg(&e->err, "maximum length of string is %zu bytes", maxlen); | |
533 | } | ||
534 | |||
535 | 115 | ConditionType type; | |
536 |
2/2✓ Branch 0 (7→8) taken 108 times.
✓ Branch 1 (7→10) taken 7 times.
|
115 | if (cmdargs_has_flag(a, 'i')) { |
537 | type = COND_STR_ICASE; | ||
538 | } else { | ||
539 |
2/2✓ Branch 0 (8→9) taken 36 times.
✓ Branch 1 (8→10) taken 72 times.
|
108 | type = (len == 2) ? COND_STR2 : COND_STR; |
540 | } | ||
541 | |||
542 | 115 | c = add_condition(e, type, a->args[1], a->args[2]); | |
543 |
1/2✓ Branch 0 (11→12) taken 115 times.
✗ Branch 1 (11→13) not taken.
|
115 | if (!c) { |
544 | return false; | ||
545 | } | ||
546 | |||
547 | 115 | memcpy(c->u.str.buf, str, len); | |
548 | 115 | c->u.str.len = len; | |
549 | 115 | return true; | |
550 | } | ||
551 | |||
552 | 133 | static void finish_syntax(EditorState *e) | |
553 | { | ||
554 | 133 | BUG_ON(!e->syn.current_syntax); | |
555 | 133 | close_state(e); | |
556 | 133 | finalize_syntax(&e->syntaxes, e->syn.current_syntax, &e->err, e->syn.saved_nr_errors); | |
557 | 133 | e->syn.current_syntax = NULL; | |
558 | 133 | } | |
559 | |||
560 | 133 | static bool cmd_syntax(EditorState *e, const CommandArgs *a) | |
561 | { | ||
562 |
2/2✓ Branch 0 (2→3) taken 79 times.
✓ Branch 1 (2→4) taken 54 times.
|
133 | if (e->syn.current_syntax) { |
563 | 79 | finish_syntax(e); | |
564 | } | ||
565 | |||
566 | 133 | Syntax *syntax = xnew0(Syntax, 1); | |
567 | 133 | syntax->name = xstrdup(a->args[0]); | |
568 |
4/4✓ Branch 0 (6→7) taken 79 times.
✓ Branch 1 (6→9) taken 54 times.
✓ Branch 2 (7→8) taken 6 times.
✓ Branch 3 (7→9) taken 73 times.
|
133 | if (is_subsyntax(syntax) && !(e->syn.flags & SYN_WARN_ON_UNUSED_SUBSYN)) { |
569 | 6 | syntax->warned_unused_subsyntax = true; | |
570 | } | ||
571 | |||
572 | 133 | e->syn.current_syntax = syntax; | |
573 | 133 | e->syn.current_state = NULL; | |
574 | 133 | e->syn.saved_nr_errors = e->err.nr_errors; | |
575 | 133 | return true; | |
576 | } | ||
577 | |||
578 | #define CMD(name, flags, min, max, func) \ | ||
579 | {name, flags, CMDOPT_ALLOW_IN_RC, min, max, func} | ||
580 | |||
581 | static const Command cmds[] = { | ||
582 | CMD("bufis", "i", 2, 3, cmd_bufis), | ||
583 | CMD("char", "bn", 2, 3, cmd_char), | ||
584 | CMD("default", "", 2, -1, cmd_default), | ||
585 | CMD("eat", "", 1, 2, cmd_eat), | ||
586 | CMD("heredocbegin", "", 2, 2, cmd_heredocbegin), | ||
587 | CMD("heredocend", "", 1, 2, cmd_heredocend), | ||
588 | CMD("include", "b", 1, 1, cmd_include), | ||
589 | CMD("inlist", "b", 2, 3, cmd_inlist), | ||
590 | CMD("list", "i", 2, -1, cmd_list), | ||
591 | CMD("noeat", "b", 1, 1, cmd_noeat), | ||
592 | CMD("recolor", "", 1, 2, cmd_recolor), | ||
593 | CMD("require", "f", 1, 1, cmd_require), | ||
594 | CMD("state", "", 1, 2, cmd_state), | ||
595 | CMD("str", "i", 2, 3, cmd_str), | ||
596 | CMD("syntax", "", 1, 1, cmd_syntax), | ||
597 | }; | ||
598 | |||
599 | 18 | UNITTEST { | |
600 | 18 | CHECK_BSEARCH_ARRAY(cmds, name, strcmp); | |
601 | 18 | } | |
602 | |||
603 | 3932 | static const Command *find_syntax_command(const char *name) | |
604 | { | ||
605 | 3932 | return BSEARCH(name, cmds, command_cmp); | |
606 | } | ||
607 | |||
608 | ✗ | static char *expand_syntax_var(const EditorState *e, const char *name) | |
609 | { | ||
610 | ✗ | if (streq(name, "DTE_HOME")) { | |
611 | ✗ | return xstrdup(e->user_config_dir); | |
612 | } | ||
613 | return NULL; | ||
614 | } | ||
615 | |||
616 | static const CommandSet syntax_commands = { | ||
617 | .lookup = find_syntax_command, | ||
618 | }; | ||
619 | |||
620 | 163 | static CommandRunner cmdrunner_for_syntaxes(EditorState *e) | |
621 | { | ||
622 | 163 | CommandRunner runner = cmdrunner(e, &syntax_commands); | |
623 | 163 | runner.expand_variable = expand_syntax_var; | |
624 | 163 | return runner; | |
625 | } | ||
626 | |||
627 | 163 | static ConfigFlags syn_flags_to_cfg_flags(SyntaxLoadFlags flags) | |
628 | { | ||
629 | 163 | static_assert(SYN_MUST_EXIST == (SyntaxLoadFlags)CFG_MUST_EXIST); | |
630 | 163 | static_assert(SYN_BUILTIN == (SyntaxLoadFlags)CFG_BUILTIN); | |
631 | 163 | SyntaxLoadFlags mask = SYN_MUST_EXIST | SYN_BUILTIN; | |
632 | 163 | return (ConfigFlags)(flags & mask); | |
633 | } | ||
634 | |||
635 | 4 | static int read_syntax(EditorState *e, const char *filename, SyntaxLoadFlags flags) | |
636 | { | ||
637 | 4 | CommandRunner runner = cmdrunner_for_syntaxes(e); | |
638 | 4 | return read_config(&runner, filename, syn_flags_to_cfg_flags(flags)); | |
639 | } | ||
640 | |||
641 | 159 | Syntax *load_syntax_file(EditorState *e, const char *filename, SyntaxLoadFlags flags, int *err) | |
642 | { | ||
643 | 159 | e->syn = (SyntaxLoadState) { | |
644 | .current_syntax = NULL, | ||
645 | .current_state = NULL, | ||
646 | 159 | .flags = flags | SYN_WARN_ON_UNUSED_SUBSYN, | |
647 | .saved_nr_errors = 0, | ||
648 | }; | ||
649 | |||
650 | 159 | const char *saved_file = e->err.config_filename; | |
651 | 159 | const unsigned int saved_line = e->err.config_line; | |
652 | 159 | CommandRunner runner = cmdrunner_for_syntaxes(e); | |
653 | 159 | *err = do_read_config(&runner, filename, syn_flags_to_cfg_flags(flags)); | |
654 | |||
655 |
4/4✓ Branch 0 (3→4) taken 107 times.
✓ Branch 1 (3→7) taken 52 times.
✓ Branch 2 (4→5) taken 54 times.
✓ Branch 3 (4→7) taken 53 times.
|
159 | if (!*err && e->syn.current_syntax) { |
656 | 54 | finish_syntax(e); | |
657 | 54 | find_unused_subsyntaxes(&e->syntaxes, &e->err); | |
658 | } | ||
659 | |||
660 | 159 | e->err.config_filename = saved_file; | |
661 | 159 | e->err.config_line = saved_line; | |
662 | |||
663 |
2/2✓ Branch 0 (7→8) taken 107 times.
✓ Branch 1 (7→13) taken 52 times.
|
159 | if (*err) { |
664 | return NULL; | ||
665 | } | ||
666 | |||
667 | 107 | Syntax *syn = find_syntax(&e->syntaxes, path_basename(filename)); | |
668 |
2/2✓ Branch 0 (9→10) taken 53 times.
✓ Branch 1 (9→11) taken 54 times.
|
107 | if (!syn) { |
669 | 53 | *err = EINVAL; | |
670 | 53 | return NULL; | |
671 | } | ||
672 | |||
673 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 54 times.
|
54 | if (e->status != EDITOR_INITIALIZING) { |
674 | ✗ | update_syntax_styles(syn, &e->styles); | |
675 | } | ||
676 | |||
677 | return syn; | ||
678 | } | ||
679 | |||
680 | 52 | Syntax *load_syntax_by_filetype(EditorState *e, const char *filetype) | |
681 | { | ||
682 |
1/2✓ Branch 0 (2→3) taken 52 times.
✗ Branch 1 (2→9) not taken.
|
52 | if (!is_valid_filetype_name(filetype)) { |
683 | return NULL; | ||
684 | } | ||
685 | |||
686 | 52 | const char *cfgdir = e->user_config_dir; | |
687 | 52 | char filename[8192]; | |
688 | 52 | int err; | |
689 | |||
690 | 52 | xsnprintf(filename, sizeof filename, "%s/syntax/%s", cfgdir, filetype); | |
691 | 52 | Syntax *syn = load_syntax_file(e, filename, 0, &err); | |
692 |
2/4✓ Branch 0 (5→6) taken 52 times.
✗ Branch 1 (5→9) not taken.
✓ Branch 2 (6→7) taken 52 times.
✗ Branch 3 (6→9) not taken.
|
52 | if (syn || err != ENOENT) { |
693 | return syn; | ||
694 | } | ||
695 | |||
696 | 52 | xsnprintf(filename, sizeof filename, "syntax/%s", filetype); | |
697 | 52 | return load_syntax_file(e, filename, SYN_BUILTIN, &err); | |
698 | } | ||
699 |