dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 51.9% 202 / 0 / 389
Functions: 69.8% 37 / 0 / 53
Branches: 36.6% 52 / 26 / 168

src/show.c
Line Branch Exec Source
1 #include <stdlib.h>
2 #include <string.h>
3 #include "show.h"
4 #include "bind.h"
5 #include "buffer.h"
6 #include "change.h"
7 #include "cmdline.h"
8 #include "command/alias.h"
9 #include "command/error.h"
10 #include "command/macro.h"
11 #include "command/serialize.h"
12 #include "commands.h"
13 #include "compiler.h"
14 #include "completion.h"
15 #include "config.h"
16 #include "edit.h"
17 #include "editor.h"
18 #include "file-option.h"
19 #include "filetype.h"
20 #include "frame.h"
21 #include "mode.h"
22 #include "msg.h"
23 #include "options.h"
24 #include "syntax/color.h"
25 #include "tag.h"
26 #include "terminal/cursor.h"
27 #include "terminal/key.h"
28 #include "terminal/style.h"
29 #include "util/array.h"
30 #include "util/bsearch.h"
31 #include "util/debug.h"
32 #include "util/environ.h"
33 #include "util/intern.h"
34 #include "util/log.h"
35 #include "util/unicode.h"
36 #include "util/xmalloc.h"
37 #include "util/xsnprintf.h"
38 #include "util/xstring.h"
39 #include "vars.h"
40 #include "view.h"
41 #include "window.h"
42
43 typedef enum {
44 DTERC = 1 << 0, // Use "dte" filetype (and syntax highlighter)
45 LASTLINE = 1 << 1, // Move cursor to last line (e.g. most recent history entry)
46 MSGLINE = 1 << 2, // Move cursor to line corresponding to `e->messages[0].pos`
47 } ShowHandlerFlags;
48
49 typedef bool (*ShowHandlerFunc)(EditorState *e, const char *name, bool cmdline);
50 typedef void (*CompleteArgFunc)(EditorState *e, PointerArray *a, StringView prefix);
51
52 typedef struct {
53 const char name[10];
54 uint8_t flags; // ShowHandlerFlags
55 DumpFunc dump;
56 ShowHandlerFunc show;
57 CompleteArgFunc complete_arg;
58 } ShowHandler;
59
60 // NOLINTNEXTLINE(readability-function-size)
61 1 static void open_temporary_buffer (
62 EditorState *e,
63 StringView text,
64 const char *cmd,
65 const char *cmd_arg,
66 const char *filetype,
67 ShowHandlerFlags flags
68 ) {
69
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1 time.
1 const char *sp = cmd_arg[0] ? " " : "";
70 1 View *view = window_open_new_file(e->window);
71 1 Buffer *buffer = view->buffer;
72 1 buffer_set_display_filename(buffer, xasprintf("(%s%s%s)", cmd, sp, cmd_arg));
73 1 buffer->temporary = true;
74
75
2/4
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 9 not taken.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 12 not taken.
1 filetype = filetype ? filetype : ((flags & DTERC) ? "dte" : NULL);
76 if (filetype) {
77 1 buffer->options.filetype = str_intern(filetype);
78 1 set_file_options(e, buffer);
79 1 buffer_update_syntax(e, buffer);
80 }
81
82
1/2
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 24 not taken.
1 if (text.length == 0) {
83 return;
84 }
85
86 // We don't use buffer_insert_bytes() here, because the call to
87 // record_insert() would make the initial text contents undoable
88
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 17 taken 1 time.
1 if (unlikely(!strview_has_suffix(text, "\n"))) {
89 LOG_ERROR("no final newline in text");
90 do_insert(view, strview("\n"));
91 }
92 1 do_insert(view, text);
93
94
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 21 taken 1 time.
1 if (flags & LASTLINE) {
95 block_iter_eof(&view->cursor);
96 block_iter_prev_line(&view->cursor);
97
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 1 time.
1 } else if (flags & MSGLINE) {
98 const MessageList *msgs = &e->messages[0];
99 if (msgs->array.count > 0) {
100 block_iter_goto_line(&view->cursor, msgs->pos);
101 }
102 }
103 }
104
105 static bool show_normal_alias(EditorState *e, const char *alias_name, bool cflag)
106 {
107 const char *cmd_str = find_alias(&e->aliases, alias_name);
108 if (!cmd_str) {
109 if (find_normal_command(alias_name)) {
110 return info_msg(&e->err, "%s is a built-in command, not an alias", alias_name);
111 }
112 return info_msg(&e->err, "%s is not a known alias", alias_name);
113 }
114
115 if (!cflag) {
116 return info_msg(&e->err, "%s is aliased to: %s", alias_name, cmd_str);
117 }
118
119 push_input_mode(e, e->command_mode);
120 cmdline_set_text(&e->cmdline, cmd_str);
121 return true;
122 }
123
124 1 static bool show_binding(EditorState *e, const char *keystr, bool cflag)
125 {
126 1 KeyCode key = keycode_from_str(keystr);
127
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1 if (key == KEY_NONE) {
128 1 return error_msg(&e->err, "invalid key string: %s", keystr);
129 }
130
131 // Use canonical key string in printed messages
132 char buf[KEYCODE_STR_BUFSIZE];
133 size_t len = keycode_to_str(key, buf);
134 BUG_ON(len == 0);
135 keystr = buf;
136
137 if (u_is_unicode(key)) {
138 return error_msg(&e->err, "%s is not a bindable key", keystr);
139 }
140
141 const CachedCommand *b = lookup_binding(&e->normal_mode->key_bindings, key);
142 if (!b) {
143 return info_msg(&e->err, "%s is not bound to a command", keystr);
144 }
145
146 if (!cflag) {
147 return info_msg(&e->err, "%s is bound to: %s", keystr, b->cmd_str);
148 }
149
150 push_input_mode(e, e->command_mode);
151 cmdline_set_text(&e->cmdline, b->cmd_str);
152 return true;
153 }
154
155 static bool show_color(EditorState *e, const char *name, bool cflag)
156 {
157 const TermStyle *hl = find_style(&e->styles, name);
158 if (!hl) {
159 return info_msg(&e->err, "no color entry with name '%s'", name);
160 }
161
162 if (!cflag) {
163 char buf[TERM_STYLE_BUFSIZE];
164 const char *style_str = term_style_to_string(buf, hl);
165 return info_msg(&e->err, "color '%s' is set to: %s", name, style_str);
166 }
167
168 CommandLine *c = &e->cmdline;
169 push_input_mode(e, e->command_mode);
170 cmdline_clear(c);
171 string_append_hl_style(&c->buf, name, hl);
172 c->pos = c->buf.len;
173 return true;
174 }
175
176 1 static bool show_cursor(EditorState *e, const char *mode_str, bool cflag)
177 {
178 1 CursorInputMode mode = cursor_mode_from_str(mode_str);
179
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1 if (mode >= NR_CURSOR_MODES) {
180 1 return error_msg(&e->err, "no cursor entry for '%s'", mode_str);
181 }
182
183 TermCursorStyle style = e->cursor_styles[mode];
184 char colorbuf[COLOR_STR_BUFSIZE];
185 const char *type = cursor_type_to_str(style.type);
186 const char *color = cursor_color_to_str(colorbuf, style.color);
187
188 if (!cflag) {
189 return info_msg(&e->err, "cursor '%s' is set to: %s %s", mode_str, type, color);
190 }
191
192 char buf[64];
193 xsnprintf(buf, sizeof buf, "cursor %s %s %s", mode_str, type, color);
194 push_input_mode(e, e->command_mode);
195 cmdline_set_text(&e->cmdline, buf);
196 return true;
197 }
198
199 static bool show_env(EditorState *e, const char *name, bool cflag)
200 {
201 const char *value = getenv(name);
202 if (!value) {
203 return info_msg(&e->err, "no environment variable with name '%s'", name);
204 }
205
206 if (!cflag) {
207 return info_msg(&e->err, "$%s is set to: %s", name, value);
208 }
209
210 push_input_mode(e, e->command_mode);
211 cmdline_set_text(&e->cmdline, value);
212 return true;
213 }
214
215 1 static String dump_env(EditorState* UNUSED_ARG(e))
216 {
217 1 String buf = string_new(4096);
218
2/2
✓ Branch 7 → 4 taken 186 times.
✓ Branch 7 → 8 taken 1 time.
188 for (size_t i = 0; environ[i]; i++) {
219 186 string_append_cstring(&buf, environ[i]);
220 186 string_append_byte(&buf, '\n');
221 }
222 1 return buf;
223 }
224
225 1 static String dump_setenv(EditorState* UNUSED_ARG(e))
226 {
227 1 String buf = string_new(4096);
228
2/2
✓ Branch 16 → 4 taken 186 times.
✓ Branch 16 → 17 taken 1 time.
188 for (size_t i = 0; environ[i]; i++) {
229 186 const char *str = environ[i];
230 186 const char *delim = strchr(str, '=');
231
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 186 times.
186 if (unlikely(!delim || delim == str)) {
232 continue;
233 }
234 186 string_append_literal(&buf, "setenv ");
235
3/4
✓ Branch 7 → 8 taken 186 times.
✗ Branch 7 → 9 not taken.
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 10 taken 183 times.
186 if (unlikely(str[0] == '-' || delim[1] == '-')) {
236 3 string_append_literal(&buf, "-- ");
237 }
238 186 const StringView name = string_view(str, delim - str);
239 186 string_append_escaped_arg_sv(&buf, name, true);
240 186 string_append_byte(&buf, ' ');
241 186 string_append_escaped_arg(&buf, delim + 1, true);
242 186 string_append_byte(&buf, '\n');
243 }
244 1 return buf;
245 }
246
247 static const char *flag_for_builtin_mode(const EditorState *e, const ModeHandler *m)
248 {
249 if (m == e->normal_mode) {
250 return "";
251 } else if (m == e->command_mode) {
252 return "-c ";
253 } else if (m == e->search_mode) {
254 return "-s ";
255 }
256 return NULL;
257 }
258
259 static bool show_mode(EditorState *e, const char *name, bool cflag)
260 {
261 const ModeHandler *mode = get_mode_handler(&e->modes, name);
262 if (!mode) {
263 return info_msg(&e->err, "no mode with name '%s'", name);
264 }
265
266 String str = string_new(4096);
267 const char *flag = flag_for_builtin_mode(e, mode);
268 if (!flag) {
269 string_append_def_mode(&str, mode);
270 string_append_byte(&str, '\n');
271 }
272
273 dump_bindings(&mode->key_bindings, flag ? flag : name, &str);
274 StringView text = strview_from_string(&str);
275
276 if (cflag) {
277 buffer_insert_bytes(e->view, text);
278 } else {
279 open_temporary_buffer(e, text, "def-mode", name, NULL, DTERC);
280 }
281
282 string_free(&str);
283 return true;
284 }
285
286 1 static bool show_builtin(EditorState *e, const char *name, bool cflag)
287 {
288 1 const BuiltinConfig *cfg = get_builtin_config(name);
289
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 4 not taken.
1 if (!cfg) {
290 1 return error_msg(&e->err, "no built-in config with name '%s'", name);
291 }
292
293 if (cflag) {
294 buffer_insert_bytes(e->view, cfg->text);
295 } else {
296 bool script = str_has_prefix(name, "script/");
297 const char *ft = script ? filetype_str_from_extension(name) : "dte";
298 open_temporary_buffer(e, cfg->text, "builtin", name, ft, 0);
299 }
300
301 return true;
302 }
303
304 static bool show_compiler(EditorState *e, const char *name, bool cflag)
305 {
306 const Compiler *compiler = find_compiler(&e->compilers, name);
307 if (!compiler) {
308 return info_msg(&e->err, "no errorfmt entry found for '%s'", name);
309 }
310
311 String str = string_new(512);
312 dump_compiler(compiler, name, &str);
313 StringView text = strview_from_string(&str);
314
315 if (cflag) {
316 buffer_insert_bytes(e->view, text);
317 } else {
318 open_temporary_buffer(e, text, "errorfmt", name, NULL, DTERC);
319 }
320
321 string_free(&str);
322 return true;
323 }
324
325 static bool show_msg(EditorState *e, const char *name, bool cflag)
326 {
327 size_t idx = name[0] - 'A';
328 if (idx >= ARRAYLEN(e->messages) || name[1] != '\0') {
329 return info_msg(&e->err, "no message list '%s'", name);
330 }
331
332 const MessageList *msgs = &e->messages[idx];
333 String str = dump_messages(msgs);
334 StringView text = strview_from_string(&str);
335
336 if (cflag) {
337 buffer_insert_bytes(e->view, text);
338 } else {
339 open_temporary_buffer(e, text, "msg", name, NULL, 0);
340 if (msgs->array.count > 0) {
341 block_iter_goto_line(&e->view->cursor, msgs->pos);
342 }
343 }
344
345 string_free(&str);
346 return true;
347 }
348
349 1 static bool show_option(EditorState *e, const char *name, bool cflag)
350 {
351 1 const char *value = get_option_value_string(e, name);
352
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1 if (!value) {
353 1 return error_msg(&e->err, "invalid option name: %s", name);
354 }
355
356 if (!cflag) {
357 return info_msg(&e->err, "%s is set to: %s", name, value);
358 }
359
360 push_input_mode(e, e->command_mode);
361 cmdline_set_text(&e->cmdline, value);
362 return true;
363 }
364
365 1 static void collect_all_options(EditorState* UNUSED_ARG(e), PointerArray *a, StringView prefix)
366 {
367 1 collect_options(a, prefix, false, false);
368 1 }
369
370 static void do_collect_cursor_modes(EditorState* UNUSED_ARG(e), PointerArray *a, StringView prefix)
371 {
372 collect_cursor_modes(a, prefix);
373 }
374
375 1 static void do_collect_builtin_configs(EditorState* UNUSED_ARG(e), PointerArray *a, StringView prefix)
376 {
377 1 collect_builtin_configs(a, prefix);
378 1 }
379
380 static void do_collect_builtin_includes(EditorState* UNUSED_ARG(e), PointerArray *a, StringView prefix)
381 {
382 collect_builtin_includes(a, prefix);
383 }
384
385 static void do_collect_modes(EditorState *e, PointerArray *a, StringView prefix)
386 {
387 collect_modes(&e->modes, a, prefix);
388 }
389
390 static void do_collect_env(EditorState* UNUSED_ARG(e), PointerArray *a, StringView prefix)
391 {
392 collect_env(environ, a, prefix, "");
393 }
394
395 static void do_collect_normal_vars(EditorState* UNUSED_ARG(e), PointerArray *a, StringView prefix)
396 {
397 collect_normal_vars(a, prefix, "");
398 }
399
400 static void collect_show_msg_args(EditorState* UNUSED_ARG(e), PointerArray *a, StringView prefix)
401 {
402 static const char args[][2] = {"A", "B", "C"};
403 COLLECT_STRINGS(args, a, prefix);
404 }
405
406 static bool show_normal_var(EditorState *e, const char *name, bool cflag)
407 {
408 char *val = expand_normal_var(e, name);
409 if (!val) {
410 return info_msg(&e->err, "variable $%s not defined for current buffer", name);
411 }
412
413 if (cflag) {
414 push_input_mode(e, e->command_mode);
415 cmdline_set_text(&e->cmdline, val);
416 } else {
417 info_msg(&e->err, "$%s expands to: %s", name, val);
418 }
419
420 free(val);
421 return true;
422 }
423
424 1 static bool show_wsplit(EditorState *e, const char *name, bool cflag)
425 {
426
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 4 not taken.
1 if (!streq(name, "this")) {
427 1 return error_msg(&e->err, "invalid window: %s", name);
428 }
429
430 const Window *w = e->window;
431 char buf[(4 * DECIMAL_STR_MAX(w->x)) + 4];
432 xsnprintf(buf, sizeof buf, "%d,%d %dx%d", w->x, w->y, w->w, w->h);
433
434 if (!cflag) {
435 return info_msg(&e->err, "current window dimensions: %s", buf);
436 }
437
438 push_input_mode(e, e->command_mode);
439 cmdline_set_text(&e->cmdline, buf);
440 return true;
441 }
442
443 typedef struct {
444 const char *name;
445 const char *value;
446 } CommandAlias;
447
448 86 static int alias_cmp(const void *ap, const void *bp)
449 {
450 86 const CommandAlias *a = ap;
451 86 const CommandAlias *b = bp;
452 86 return strcmp(a->name, b->name);
453 }
454
455 1 static String dump_normal_aliases(EditorState *e)
456 {
457 1 const size_t count = e->aliases.count;
458
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1 time.
1 if (unlikely(count == 0)) {
459 return string_new(0);
460 }
461
462 // Clone the contents of the HashMap as an array of name/value pairs
463 1 CommandAlias *array = xmallocarray(count, sizeof(*array));
464 1 size_t n = 0;
465
2/2
✓ Branch 8 → 6 taken 25 times.
✓ Branch 8 → 9 taken 1 time.
26 for (HashMapIter it = hashmap_iter(&e->aliases); hashmap_next(&it); ) {
466 25 array[n++] = (CommandAlias) {
467 25 .name = it.entry->key,
468 25 .value = it.entry->value,
469 };
470 }
471
472 // Sort the array
473 1 BUG_ON(n != count);
474 1 qsort(array, count, sizeof(array[0]), alias_cmp);
475
476 // Serialize the aliases in sorted order
477 1 String buf = string_new(4096);
478
2/2
✓ Branch 22 → 14 taken 25 times.
✓ Branch 22 → 23 taken 1 time.
27 for (size_t i = 0; i < count; i++) {
479 25 const char *name = array[i].name;
480 25 string_append_literal(&buf, "alias ");
481
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 25 times.
25 if (unlikely(name[0] == '-')) {
482 string_append_literal(&buf, "-- ");
483 }
484 25 string_append_escaped_arg(&buf, name, true);
485 25 string_append_byte(&buf, ' ');
486 25 string_append_escaped_arg(&buf, array[i].value, true);
487 25 string_append_byte(&buf, '\n');
488 }
489
490 1 free(array);
491 1 return buf;
492 }
493
494 typedef struct {
495 const char *name;
496 const ModeHandler *handler;
497 } ModeHandlerEntry;
498
499 static int mhe_cmp(const void *ap, const void *bp)
500 {
501 const ModeHandlerEntry *a = ap;
502 const ModeHandlerEntry *b = bp;
503 return strcmp(a->name, b->name);
504 }
505
506 1 static String dump_all_bindings(EditorState *e)
507 {
508 1 String buf = string_new(4096);
509
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
1 if (dump_bindings(&e->normal_mode->key_bindings, "", &buf)) {
510 1 string_append_byte(&buf, '\n');
511 }
512
513 1 size_t count = e->modes.count;
514 1 BUG_ON(count < 3);
515 1 count -= 3;
516
517
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 27 taken 1 time.
1 if (count) {
518 // Clone custom modes in HashMap as an array
519 ModeHandlerEntry *array = xmallocarray(count, sizeof(*array));
520 size_t n = 0;
521 for (HashMapIter it = hashmap_iter(&e->modes); hashmap_next(&it); ) {
522 const char *name = it.entry->key;
523 const ModeHandler *handler = it.entry->value;
524 bool is_builtin_mode = !!flag_for_builtin_mode(e, handler);
525 if (is_builtin_mode) {
526 continue;
527 }
528 array[n++] = (ModeHandlerEntry) {
529 .name = name,
530 .handler = handler,
531 };
532 }
533
534 // Sort the array
535 BUG_ON(n != count);
536 qsort(array, count, sizeof(array[0]), mhe_cmp);
537
538 // Serialize bindings for each mode, sorted by mode name
539 for (size_t i = 0; i < count; i++) {
540 const char *name = array[i].name;
541 const ModeHandler *handler = array[i].handler;
542 const IntMap *bindings = &handler->key_bindings;
543 if (dump_bindings(bindings, name, &buf)) {
544 string_append_byte(&buf, '\n');
545 }
546 }
547
548 free(array);
549 }
550
551 1 dump_bindings(&e->command_mode->key_bindings, "-c ", &buf);
552 1 string_append_byte(&buf, '\n');
553 1 dump_bindings(&e->search_mode->key_bindings, "-s ", &buf);
554 1 return buf;
555 }
556
557 1 static String dump_modes(EditorState *e)
558 {
559 // TODO: Serialize in alphabetical order, instead of table order?
560 1 String buf = string_new(256);
561
2/2
✓ Branch 7 → 4 taken 3 times.
✓ Branch 7 → 8 taken 1 time.
4 for (HashMapIter it = hashmap_iter(&e->modes); hashmap_next(&it); ) {
562 3 const ModeHandler *mode = it.entry->value;
563 3 string_append_def_mode(&buf, mode);
564 3 string_append_byte(&buf, '\n');
565 }
566 1 return buf;
567 }
568
569 1 static String dump_frames(EditorState *e)
570 {
571 1 String str = string_new(4096);
572 1 dump_frame(e->root_frame, 0, &str);
573 1 return str;
574 }
575
576 1 static String dump_compilers(EditorState *e)
577 {
578 1 String buf = string_new(4096);
579
2/2
✓ Branch 7 → 4 taken 3 times.
✓ Branch 7 → 8 taken 1 time.
4 for (HashMapIter it = hashmap_iter(&e->compilers); hashmap_next(&it); ) {
580 3 const char *name = it.entry->key;
581 3 const Compiler *c = it.entry->value;
582 3 dump_compiler(c, name, &buf);
583 3 string_append_byte(&buf, '\n');
584 }
585 1 return buf;
586 }
587
588 1 static String dump_cursors(EditorState *e)
589 {
590 1 String buf = string_new(128);
591 1 char colorbuf[COLOR_STR_BUFSIZE];
592
2/2
✓ Branch 15 → 4 taken 4 times.
✓ Branch 15 → 16 taken 1 time.
6 for (CursorInputMode m = 0; m < ARRAYLEN(e->cursor_styles); m++) {
593 4 const TermCursorStyle *style = &e->cursor_styles[m];
594 4 string_append_literal(&buf, "cursor ");
595 4 string_append_cstring(&buf, cursor_mode_to_str(m));
596 4 string_append_byte(&buf, ' ');
597 4 string_append_cstring(&buf, cursor_type_to_str(style->type));
598 4 string_append_byte(&buf, ' ');
599 4 string_append_cstring(&buf, cursor_color_to_str(colorbuf, style->color));
600 4 string_append_byte(&buf, '\n');
601 }
602 1 return buf;
603 }
604
605 // Dump option values and FileOption entries
606 1 static String dump_options_and_fileopts(EditorState *e)
607 {
608 1 String str = dump_options(&e->options, &e->buffer->options);
609 1 string_append_literal(&str, "\n\n");
610 1 dump_file_options(&e->file_options, &str);
611 1 return str;
612 }
613
614 1 static String dump_paste(EditorState *e)
615 {
616 1 const Clipboard *clip = &e->clipboard;
617 1 return string_new_from_buf(clip->buf, clip->len);
618 }
619
620 1 static String do_dump_options(EditorState *e) {return dump_options(&e->options, &e->buffer->options);}
621 2 static String do_dump_builtin_configs(EditorState* UNUSED_ARG(e)) {return dump_builtin_configs();}
622 2 static String do_dump_hl_styles(EditorState *e) {return dump_hl_styles(&e->styles);}
623 1 static String do_dump_filetypes(EditorState *e) {return dump_filetypes(&e->filetypes);}
624 1 static String do_dump_messages_a(EditorState *e) {return dump_messages(&e->messages[0]);}
625 2 static String do_dump_macro(EditorState *e) {return dump_macro(&e->macro);}
626 1 static String do_dump_buffer(EditorState *e) {return dump_buffer(e->view);}
627 static String do_dump_tags(EditorState *e) {return dump_tags(&e->tagfile, &e->err);}
628 1 static String dump_command_history(EditorState *e) {return history_dump(&e->command_history);}
629 1 static String dump_search_history(EditorState *e) {return history_dump(&e->search_history);}
630 1 static String dump_file_history(EditorState *e) {return file_history_dump(&e->file_history);}
631 static String dump_show_subcmds(EditorState *e); // Forward declaration
632
633 static const ShowHandler show_handlers[] = {
634 {"alias", DTERC, dump_normal_aliases, show_normal_alias, collect_normal_aliases},
635 {"bind", DTERC, dump_all_bindings, show_binding, collect_bound_normal_keys},
636 {"buffer", 0, do_dump_buffer, NULL, NULL},
637 {"builtin", 0, do_dump_builtin_configs, show_builtin, do_collect_builtin_configs},
638 {"color", DTERC, do_dump_hl_styles, show_color, collect_hl_styles},
639 {"command", DTERC | LASTLINE, dump_command_history, NULL, NULL},
640 {"cursor", DTERC, dump_cursors, show_cursor, do_collect_cursor_modes},
641 {"def-mode", DTERC, dump_modes, show_mode, do_collect_modes},
642 {"env", 0, dump_env, show_env, do_collect_env},
643 {"errorfmt", DTERC, dump_compilers, show_compiler, collect_compilers},
644 {"ft", DTERC, do_dump_filetypes, NULL, NULL},
645 {"hi", DTERC, do_dump_hl_styles, show_color, collect_hl_styles},
646 {"include", 0, do_dump_builtin_configs, show_builtin, do_collect_builtin_includes},
647 {"macro", DTERC, do_dump_macro, NULL, NULL},
648 {"msg", MSGLINE, do_dump_messages_a, show_msg, collect_show_msg_args},
649 {"open", LASTLINE, dump_file_history, NULL, NULL},
650 {"option", DTERC, dump_options_and_fileopts, show_option, collect_all_options},
651 {"paste", 0, dump_paste, NULL, NULL},
652 {"search", LASTLINE, dump_search_history, NULL, NULL},
653 {"set", DTERC, do_dump_options, show_option, collect_all_options},
654 {"setenv", DTERC, dump_setenv, show_env, do_collect_env},
655 {"show", DTERC, dump_show_subcmds, NULL, NULL},
656 {"tag", 0, do_dump_tags, NULL, NULL},
657 {"var", DTERC, dump_normal_vars, show_normal_var, do_collect_normal_vars},
658 {"wsplit", 0, dump_frames, show_wsplit, NULL},
659 };
660
661 25 static const char *arg_hint_for_subcmd(const ShowHandler *handler)
662 {
663 25 const ShowHandlerFunc func = handler->show;
664
4/4
✓ Branch 2 → 3 taken 24 times.
✓ Branch 2 → 5 taken 1 time.
✓ Branch 3 → 4 taken 9 times.
✓ Branch 3 → 5 taken 15 times.
25 return (func == show_binding) ? " [key]" : (func ? " [name]" : "");
665 }
666
667 1 static String dump_show_subcmds(EditorState* UNUSED_ARG(e))
668 {
669 1 String str = string_new(256);
670 1 string_append_literal(&str, "# Available `show` sub-commands:\n");
671
2/2
✓ Branch 10 → 5 taken 25 times.
✓ Branch 10 → 11 taken 1 time.
27 for (size_t i = 0; i < ARRAYLEN(show_handlers); i++) {
672 25 const ShowHandler *handler = &show_handlers[i];
673 25 string_append_literal(&str, "show ");
674 25 string_append_cstring(&str, handler->name);
675 25 string_append_cstring(&str, arg_hint_for_subcmd(handler));
676 25 string_append_byte(&str, '\n');
677 }
678 1 return str;
679 }
680
681 251 static const ShowHandler *lookup_show_handler(const char *name)
682 {
683 251 const ShowHandler *handler = BSEARCH(name, show_handlers, vstrcmp);
684 251 return handler;
685 }
686
687 71 DumpFunc get_dump_function(const char *name)
688 {
689 71 const ShowHandler *handler = lookup_show_handler(name);
690
2/2
✓ Branch 3 → 4 taken 47 times.
✓ Branch 3 → 5 taken 24 times.
71 return handler ? handler->dump : NULL;
691 }
692
693 24 UNITTEST {
694 // NOLINTBEGIN(bugprone-assert-side-effect)
695 24 CHECK_BSEARCH_ARRAY(show_handlers, name);
696 24 BUG_ON(!lookup_show_handler("alias"));
697 24 BUG_ON(!lookup_show_handler("set"));
698 24 BUG_ON(!lookup_show_handler("show"));
699 24 BUG_ON(!lookup_show_handler("wsplit"));
700 24 BUG_ON(lookup_show_handler("alia"));
701 24 BUG_ON(lookup_show_handler("sete"));
702 24 BUG_ON(lookup_show_handler(""));
703 24 BUG_ON(!get_dump_function("msg"));
704 24 BUG_ON(get_dump_function("_"));
705 // NOLINTEND(bugprone-assert-side-effect)
706 24 }
707
708 8 bool show(EditorState *e, const char *type, const char *key, bool cflag)
709 {
710
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 8 times.
8 const ShowHandler *handler = lookup_show_handler(type ? type : "show");
711
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 7 times.
8 if (!handler) {
712 1 return error_msg(&e->err, "invalid argument: '%s'", type);
713 }
714
715
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 7 times.
7 if (!type) {
716 type = "";
717 }
718
719
2/2
✓ Branch 9 → 10 taken 6 times.
✓ Branch 9 → 13 taken 1 time.
7 if (key) {
720
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 5 times.
6 if (!handler->show) {
721 1 return error_msg(&e->err, "'show %s' doesn't take extra arguments", type);
722 }
723 5 return handler->show(e, key, cflag);
724 }
725
726 1 String str = handler->dump(e);
727 1 StringView text = strview_from_string(&str);
728 1 open_temporary_buffer(e, text, "show", type, NULL, handler->flags);
729 1 string_free(&str);
730 1 return true;
731 }
732
733 2 void collect_show_subcommands(PointerArray *a, StringView prefix)
734 {
735 2 COLLECT_STRING_FIELDS(show_handlers, name, a, prefix);
736 2 }
737
738 4 void collect_show_subcommand_args(EditorState *e, PointerArray *a, const char *name, StringView arg_prefix)
739 {
740 4 const ShowHandler *handler = lookup_show_handler(name);
741
2/4
✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 6 not taken.
✓ Branch 4 → 5 taken 4 times.
✗ Branch 4 → 6 not taken.
4 if (handler && handler->complete_arg) {
742 4 handler->complete_arg(e, a, arg_prefix);
743 }
744 4 }
745