dte test coverage


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