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 |