dte test coverage


Directory: ./
File: src/show.c
Date: 2025-07-04 20:13:35
Exec Total Coverage
Lines: 202 371 54.4%
Functions: 37 50 74.0%
Branches: 52 138 37.7%

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