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