dte test coverage


Directory: ./
File: src/show.c
Date: 2025-07-10 06:26:10
Exec Total Coverage
Lines: 202 374 54.0%
Functions: 37 51 72.5%
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 167 times.
✓ Branch 1 (7→8) taken 1 times.
168 for (size_t i = 0; environ[i]; i++) {
217 167 string_append_cstring(&buf, environ[i]);
218 167 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 167 times.
✓ Branch 1 (16→17) taken 1 times.
168 for (size_t i = 0; environ[i]; i++) {
227 167 const char *str = environ[i];
228 167 const char *delim = strchr(str, '=');
229
1/2
✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 167 times.
167 if (unlikely(!delim || delim == str)) {
230 continue;
231 }
232 167 string_append_literal(&buf, "setenv ");
233
3/4
✓ Branch 0 (7→8) taken 167 times.
✗ Branch 1 (7→9) not taken.
✓ Branch 2 (8→9) taken 3 times.
✓ Branch 3 (8→10) taken 164 times.
167 if (unlikely(str[0] == '-' || delim[1] == '-')) {
234 3 string_append_literal(&buf, "-- ");
235 }
236 167 const StringView name = string_view(str, delim - str);
237 167 string_append_escaped_arg_sv(&buf, name, true);
238 167 string_append_byte(&buf, ' ');
239 167 string_append_escaped_arg(&buf, delim + 1, true);
240 167 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 do_collect_env(EditorState* UNUSED_ARG(e), PointerArray *a, const char *prefix)
386 {
387 collect_env(a, strview_from_cstring(prefix), "");
388 }
389
390 static void collect_show_msg_args(EditorState* UNUSED_ARG(e), PointerArray *a, const char *prefix)
391 {
392 static const char args[][2] = {"A", "B", "C"};
393 COLLECT_STRINGS(args, a, prefix);
394 }
395
396 1 static bool show_wsplit(EditorState *e, const char *name, bool cflag)
397 {
398
1/2
✓ Branch 0 (2→3) taken 1 times.
✗ Branch 1 (2→4) not taken.
1 if (!streq(name, "this")) {
399 1 return error_msg(&e->err, "invalid window: %s", name);
400 }
401
402 const Window *w = e->window;
403 char buf[(4 * DECIMAL_STR_MAX(w->x)) + 4];
404 xsnprintf(buf, sizeof buf, "%d,%d %dx%d", w->x, w->y, w->w, w->h);
405
406 if (!cflag) {
407 return info_msg(&e->err, "current window dimensions: %s", buf);
408 }
409
410 push_input_mode(e, e->command_mode);
411 cmdline_set_text(&e->cmdline, buf);
412 return true;
413 }
414
415 typedef struct {
416 const char *name;
417 const char *value;
418 } CommandAlias;
419
420 77 static int alias_cmp(const void *ap, const void *bp)
421 {
422 77 const CommandAlias *a = ap;
423 77 const CommandAlias *b = bp;
424 77 return strcmp(a->name, b->name);
425 }
426
427 1 static String dump_normal_aliases(EditorState *e)
428 {
429 1 const size_t count = e->aliases.count;
430
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 1 times.
1 if (unlikely(count == 0)) {
431 return string_new(0);
432 }
433
434 // Clone the contents of the HashMap as an array of name/value pairs
435 1 CommandAlias *array = xmallocarray(count, sizeof(*array));
436 1 size_t n = 0;
437
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); ) {
438 24 array[n++] = (CommandAlias) {
439 24 .name = it.entry->key,
440 24 .value = it.entry->value,
441 };
442 }
443
444 // Sort the array
445 1 BUG_ON(n != count);
446 1 qsort(array, count, sizeof(array[0]), alias_cmp);
447
448 // Serialize the aliases in sorted order
449 1 String buf = string_new(4096);
450
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++) {
451 24 const char *name = array[i].name;
452 24 string_append_literal(&buf, "alias ");
453
1/2
✗ Branch 0 (15→16) not taken.
✓ Branch 1 (15→17) taken 24 times.
24 if (unlikely(name[0] == '-')) {
454 string_append_literal(&buf, "-- ");
455 }
456 24 string_append_escaped_arg(&buf, name, true);
457 24 string_append_byte(&buf, ' ');
458 24 string_append_escaped_arg(&buf, array[i].value, true);
459 24 string_append_byte(&buf, '\n');
460 }
461
462 1 free(array);
463 1 return buf;
464 }
465
466 typedef struct {
467 const char *name;
468 const ModeHandler *handler;
469 } ModeHandlerEntry;
470
471 static int mhe_cmp(const void *ap, const void *bp)
472 {
473 const ModeHandlerEntry *a = ap;
474 const ModeHandlerEntry *b = bp;
475 return strcmp(a->name, b->name);
476 }
477
478 1 static String dump_all_bindings(EditorState *e)
479 {
480 1 String buf = string_new(4096);
481
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)) {
482 1 string_append_byte(&buf, '\n');
483 }
484
485 1 size_t count = e->modes.count;
486 1 BUG_ON(count < 3);
487 1 count -= 3;
488
489
1/2
✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→27) taken 1 times.
1 if (count) {
490 // Clone custom modes in HashMap as an array
491 ModeHandlerEntry *array = xmallocarray(count, sizeof(*array));
492 size_t n = 0;
493 for (HashMapIter it = hashmap_iter(&e->modes); hashmap_next(&it); ) {
494 const char *name = it.entry->key;
495 const ModeHandler *handler = it.entry->value;
496 bool is_builtin_mode = !!flag_for_builtin_mode(e, handler);
497 if (is_builtin_mode) {
498 continue;
499 }
500 array[n++] = (ModeHandlerEntry) {
501 .name = name,
502 .handler = handler,
503 };
504 }
505
506 // Sort the array
507 BUG_ON(n != count);
508 qsort(array, count, sizeof(array[0]), mhe_cmp);
509
510 // Serialize bindings for each mode, sorted by mode name
511 for (size_t i = 0; i < count; i++) {
512 const char *name = array[i].name;
513 const ModeHandler *handler = array[i].handler;
514 const IntMap *bindings = &handler->key_bindings;
515 if (dump_bindings(bindings, name, &buf)) {
516 string_append_byte(&buf, '\n');
517 }
518 }
519
520 free(array);
521 }
522
523 1 dump_bindings(&e->command_mode->key_bindings, "-c ", &buf);
524 1 string_append_byte(&buf, '\n');
525 1 dump_bindings(&e->search_mode->key_bindings, "-s ", &buf);
526 1 return buf;
527 }
528
529 1 static String dump_modes(EditorState *e)
530 {
531 // TODO: Serialize in alphabetical order, instead of table order?
532 1 String buf = string_new(256);
533
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); ) {
534 3 const ModeHandler *mode = it.entry->value;
535 3 string_append_def_mode(&buf, mode);
536 3 string_append_byte(&buf, '\n');
537 }
538 1 return buf;
539 }
540
541 1 static String dump_frames(EditorState *e)
542 {
543 1 String str = string_new(4096);
544 1 dump_frame(e->root_frame, 0, &str);
545 1 return str;
546 }
547
548 1 static String dump_compilers(EditorState *e)
549 {
550 1 String buf = string_new(4096);
551
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); ) {
552 3 const char *name = it.entry->key;
553 3 const Compiler *c = it.entry->value;
554 3 dump_compiler(c, name, &buf);
555 3 string_append_byte(&buf, '\n');
556 }
557 1 return buf;
558 }
559
560 1 static String dump_cursors(EditorState *e)
561 {
562 1 String buf = string_new(128);
563 1 char colorbuf[COLOR_STR_BUFSIZE];
564
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++) {
565 4 const TermCursorStyle *style = &e->cursor_styles[m];
566 4 string_append_literal(&buf, "cursor ");
567 4 string_append_cstring(&buf, cursor_mode_to_str(m));
568 4 string_append_byte(&buf, ' ');
569 4 string_append_cstring(&buf, cursor_type_to_str(style->type));
570 4 string_append_byte(&buf, ' ');
571 4 string_append_cstring(&buf, cursor_color_to_str(colorbuf, style->color));
572 4 string_append_byte(&buf, '\n');
573 }
574 1 return buf;
575 }
576
577 // Dump option values and FileOption entries
578 1 static String dump_options_and_fileopts(EditorState *e)
579 {
580 1 String str = dump_options(&e->options, &e->buffer->options);
581 1 string_append_literal(&str, "\n\n");
582 1 dump_file_options(&e->file_options, &str);
583 1 return str;
584 }
585
586 1 static String dump_paste(EditorState *e)
587 {
588 1 const Clipboard *clip = &e->clipboard;
589 1 return string_new_from_buf(clip->buf, clip->len);
590 }
591
592 1 static String do_dump_options(EditorState *e) {return dump_options(&e->options, &e->buffer->options);}
593 2 static String do_dump_builtin_configs(EditorState* UNUSED_ARG(e)) {return dump_builtin_configs();}
594 2 static String do_dump_hl_styles(EditorState *e) {return dump_hl_styles(&e->styles);}
595 1 static String do_dump_filetypes(EditorState *e) {return dump_filetypes(&e->filetypes);}
596 1 static String do_dump_messages_a(EditorState *e) {return dump_messages(&e->messages[0]);}
597 2 static String do_dump_macro(EditorState *e) {return dump_macro(&e->macro);}
598 1 static String do_dump_buffer(EditorState *e) {return dump_buffer(e->view);}
599 static String do_dump_tags(EditorState *e) {return dump_tags(&e->tagfile, &e->err);}
600 1 static String dump_command_history(EditorState *e) {return history_dump(&e->command_history);}
601 1 static String dump_search_history(EditorState *e) {return history_dump(&e->search_history);}
602 1 static String dump_file_history(EditorState *e) {return file_history_dump(&e->file_history);}
603 static String dump_show_subcmds(EditorState *e); // Forward declaration
604
605 static const ShowHandler show_handlers[] = {
606 {"alias", DTERC, dump_normal_aliases, show_normal_alias, collect_normal_aliases},
607 {"bind", DTERC, dump_all_bindings, show_binding, collect_bound_normal_keys},
608 {"buffer", 0, do_dump_buffer, NULL, NULL},
609 {"builtin", 0, do_dump_builtin_configs, show_builtin, do_collect_builtin_configs},
610 {"color", DTERC, do_dump_hl_styles, show_color, collect_hl_styles},
611 {"command", DTERC | LASTLINE, dump_command_history, NULL, NULL},
612 {"cursor", DTERC, dump_cursors, show_cursor, do_collect_cursor_modes},
613 {"def-mode", DTERC, dump_modes, show_mode, do_collect_modes},
614 {"env", 0, dump_env, show_env, do_collect_env},
615 {"errorfmt", DTERC, dump_compilers, show_compiler, collect_compilers},
616 {"ft", DTERC, do_dump_filetypes, NULL, NULL},
617 {"hi", DTERC, do_dump_hl_styles, show_color, collect_hl_styles},
618 {"include", 0, do_dump_builtin_configs, show_builtin, do_collect_builtin_includes},
619 {"macro", DTERC, do_dump_macro, NULL, NULL},
620 {"msg", MSGLINE, do_dump_messages_a, show_msg, collect_show_msg_args},
621 {"open", LASTLINE, dump_file_history, NULL, NULL},
622 {"option", DTERC, dump_options_and_fileopts, show_option, collect_all_options},
623 {"paste", 0, dump_paste, NULL, NULL},
624 {"search", LASTLINE, dump_search_history, NULL, NULL},
625 {"set", DTERC, do_dump_options, show_option, collect_all_options},
626 {"setenv", DTERC, dump_setenv, show_env, do_collect_env},
627 {"show", DTERC, dump_show_subcmds, NULL, NULL},
628 {"tag", 0, do_dump_tags, NULL, NULL},
629 {"wsplit", 0, dump_frames, show_wsplit, NULL},
630 };
631
632 24 static const char *arg_hint_for_subcmd(const ShowHandler *handler)
633 {
634 24 const void *show = handler->show;
635
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]" : "");
636 }
637
638 1 static String dump_show_subcmds(EditorState* UNUSED_ARG(e))
639 {
640 1 String str = string_new(256);
641 1 string_append_literal(&str, "# Available `show` sub-commands:\n");
642
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++) {
643 24 const ShowHandler *handler = &show_handlers[i];
644 24 string_append_cstring(&str, "show ");
645 24 string_append_cstring(&str, handler->name);
646 24 string_append_cstring(&str, arg_hint_for_subcmd(handler));
647 24 string_append_byte(&str, '\n');
648 }
649 1 return str;
650 }
651
652 251 static const ShowHandler *lookup_show_handler(const char *name)
653 {
654 251 const ShowHandler *handler = BSEARCH(name, show_handlers, vstrcmp);
655 251 return handler;
656 }
657
658 71 DumpFunc get_dump_function(const char *name)
659 {
660 71 const ShowHandler *handler = lookup_show_handler(name);
661
2/2
✓ Branch 0 (3→4) taken 47 times.
✓ Branch 1 (3→5) taken 24 times.
71 return handler ? handler->dump : NULL;
662 }
663
664 24 UNITTEST {
665 // NOLINTBEGIN(bugprone-assert-side-effect)
666 24 CHECK_BSEARCH_ARRAY(show_handlers, name);
667 24 BUG_ON(!lookup_show_handler("alias"));
668 24 BUG_ON(!lookup_show_handler("set"));
669 24 BUG_ON(!lookup_show_handler("show"));
670 24 BUG_ON(!lookup_show_handler("wsplit"));
671 24 BUG_ON(lookup_show_handler("alia"));
672 24 BUG_ON(lookup_show_handler("sete"));
673 24 BUG_ON(lookup_show_handler(""));
674 24 BUG_ON(!get_dump_function("msg"));
675 24 BUG_ON(get_dump_function("_"));
676 // NOLINTEND(bugprone-assert-side-effect)
677 24 }
678
679 8 bool show(EditorState *e, const char *type, const char *key, bool cflag)
680 {
681
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");
682
2/2
✓ Branch 0 (5→6) taken 1 times.
✓ Branch 1 (5→7) taken 7 times.
8 if (!handler) {
683 1 return error_msg(&e->err, "invalid argument: '%s'", type);
684 }
685
686
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→9) taken 7 times.
7 if (!type) {
687 type = "";
688 }
689
690
2/2
✓ Branch 0 (9→10) taken 6 times.
✓ Branch 1 (9→13) taken 1 times.
7 if (key) {
691
2/2
✓ Branch 0 (10→11) taken 1 times.
✓ Branch 1 (10→12) taken 5 times.
6 if (!handler->show) {
692 1 return error_msg(&e->err, "'show %s' doesn't take extra arguments", type);
693 }
694 5 return handler->show(e, key, cflag);
695 }
696
697 1 String str = handler->dump(e);
698 1 open_temporary_buffer(e, str.buffer, str.len, "show", type, NULL, handler->flags);
699 1 string_free(&str);
700 1 return true;
701 }
702
703 2 void collect_show_subcommands(PointerArray *a, const char *prefix)
704 {
705 2 COLLECT_STRING_FIELDS(show_handlers, name, a, prefix);
706 2 }
707
708 4 void collect_show_subcommand_args(EditorState *e, PointerArray *a, const char *name, const char *arg_prefix)
709 {
710 4 const ShowHandler *handler = lookup_show_handler(name);
711
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) {
712 4 handler->complete_arg(e, a, arg_prefix);
713 }
714 4 }
715