diff options
-rw-r--r-- | runtime/doc/api.txt | 8 | ||||
-rw-r--r-- | src/nvim/api/keysets.lua | 3 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 19 | ||||
-rw-r--r-- | src/nvim/charset.c | 16 | ||||
-rw-r--r-- | src/nvim/charset.h | 1 | ||||
-rw-r--r-- | src/nvim/options.lua | 2 | ||||
-rw-r--r-- | src/nvim/tui/terminfo.c | 75 | ||||
-rw-r--r-- | src/nvim/tui/terminfo.h | 2 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 72 | ||||
-rw-r--r-- | test/functional/ui/options_spec.lua | 1 |
10 files changed, 140 insertions, 59 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 8a33fc58a3..3cd4578750 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -717,7 +717,7 @@ nvim_del_var({name}) *nvim_del_var()* Parameters: ~ • {name} Variable name -nvim_echo({chunks}, {history}, {opts}) *nvim_echo()* +nvim_echo({chunks}, {history}, {*opts}) *nvim_echo()* Echo a message. Parameters: ~ @@ -725,7 +725,11 @@ nvim_echo({chunks}, {history}, {opts}) *nvim_echo()* chunk with specified highlight. `hl_group` element can be omitted for no highlight. • {history} if true, add to |message-history|. - • {opts} Optional parameters. Reserved for future use. + • {opts} Optional parameters. + • verbose: Message was printed as a result of 'verbose' + option if Nvim was invoked with -V3log_file, the message + will be redirected to the log_file and surpressed from + direct output. nvim_err_write({str}) *nvim_err_write()* Writes a message to the Vim error buffer. Does not append "\n", the diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 7e0d399573..8ded9cfa5d 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -219,5 +219,8 @@ return { cmd_opts = { "output"; }; + echo_opts = { + "verbose"; + }; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 70b07dabe8..83c9d54725 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -726,8 +726,11 @@ void nvim_set_vvar(String name, Object value, Error *err) /// text chunk with specified highlight. `hl_group` element /// can be omitted for no highlight. /// @param history if true, add to |message-history|. -/// @param opts Optional parameters. Reserved for future use. -void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err) +/// @param opts Optional parameters. +/// - verbose: Message was printed as a result of 'verbose' option +/// if Nvim was invoked with -V3log_file, the message will be +/// redirected to the log_file and surpressed from direct output. +void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err) FUNC_API_SINCE(7) { HlMessage hl_msg = parse_hl_msg(chunks, err); @@ -735,13 +738,19 @@ void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err) goto error; } - if (opts.size > 0) { - api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); - goto error; + bool verbose = api_object_to_bool(opts->verbose, "verbose", false, err); + + if (verbose) { + verbose_enter(); } msg_multiattr(hl_msg, history ? "echomsg" : "echo", history); + if (verbose) { + verbose_leave(); + verbose_stop(); // flush now + } + if (history) { // history takes ownership return; diff --git a/src/nvim/charset.c b/src/nvim/charset.c index a8abee42be..51eddd5850 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -418,6 +418,22 @@ char *transstr(const char *const s, bool untab) return buf; } +size_t kv_transstr(StringBuilder *str, const char *const s, bool untab) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (!s) { + return 0; + } + + // Compute the length of the result, taking account of unprintable + // multi-byte characters. + const size_t len = transstr_len(s, untab); + kv_ensure_space(*str, len + 1); + transstr_buf(s, str->items + str->size, len + 1, untab); + str->size += len; // do not include NUL byte + return len; +} + /// Convert the string "str[orglen]" to do ignore-case comparing. /// Use the current locale. /// diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 978a357aa7..e1ef06ef1d 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -7,6 +7,7 @@ #include "nvim/eval/typval.h" #include "nvim/option_defs.h" #include "nvim/pos.h" +#include "nvim/strings.h" #include "nvim/types.h" /// Return the folded-case equivalent of the given character diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 1cf8ab3253..3c2fb1797b 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2690,7 +2690,7 @@ return { full_name='verbose', abbreviation='vbs', short_desc=N_("give informative messages"), type='number', scope={'global'}, - varname='p_verbose', + varname='p_verbose', redraw={'ui_option'}, defaults={if_true=0} }, { diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 0f6ae03d35..507e9df21e 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -7,10 +7,13 @@ #include <string.h> #include <unibilium.h> +#include "nvim/api/private/helpers.h" +#include "nvim/charset.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/strings.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/terminfo_defs.h" @@ -147,82 +150,80 @@ unibi_term *terminfo_from_builtin(const char *term, char **termname) /// Serves a similar purpose as Vim `:set termcap` (removed in Nvim). /// /// @note adapted from unibilium unibi-dump.c -void terminfo_info_msg(const unibi_term *const ut) +/// @return allocated string +String terminfo_info_msg(const unibi_term *ut, const char *termname) { - if (exiting) { - return; - } - msg_puts_title("\n\n--- Terminal info --- {{{\n"); + StringBuilder data = KV_INITIAL_VALUE; - char *term; - get_tty_option("term", &term); - msg_printf_attr(0, "&term: %s\n", term); - msg_printf_attr(0, "Description: %s\n", unibi_get_name(ut)); + kv_printf(data, "&term: %s\n", termname); + kv_printf(data, "Description: %s\n", unibi_get_name(ut)); const char **a = unibi_get_aliases(ut); if (*a) { - msg_puts("Aliases: "); + kv_printf(data, "Aliases: "); do { - msg_printf_attr(0, "%s%s\n", *a, a[1] ? " | " : ""); + kv_printf(data, "%s%s\n", *a, a[1] ? " | " : ""); a++; } while (*a); } - msg_puts("Boolean capabilities:\n"); + kv_printf(data, "Boolean capabilities:\n"); for (enum unibi_boolean i = unibi_boolean_begin_ + 1; i < unibi_boolean_end_; i++) { - msg_printf_attr(0, " %-25s %-10s = %s\n", unibi_name_bool(i), - unibi_short_name_bool(i), - unibi_get_bool(ut, i) ? "true" : "false"); + kv_printf(data, " %-25s %-10s = %s\n", unibi_name_bool(i), + unibi_short_name_bool(i), + unibi_get_bool(ut, i) ? "true" : "false"); } - msg_puts("Numeric capabilities:\n"); + kv_printf(data, "Numeric capabilities:\n"); for (enum unibi_numeric i = unibi_numeric_begin_ + 1; i < unibi_numeric_end_; i++) { int n = unibi_get_num(ut, i); // -1 means "empty" - msg_printf_attr(0, " %-25s %-10s = %d\n", unibi_name_num(i), - unibi_short_name_num(i), n); + kv_printf(data, " %-25s %-10s = %d\n", unibi_name_num(i), + unibi_short_name_num(i), n); } - msg_puts("String capabilities:\n"); + kv_printf(data, "String capabilities:\n"); for (enum unibi_string i = unibi_string_begin_ + 1; i < unibi_string_end_; i++) { const char *s = unibi_get_str(ut, i); if (s) { - msg_printf_attr(0, " %-25s %-10s = ", unibi_name_str(i), - unibi_short_name_str(i)); + kv_printf(data, " %-25s %-10s = ", unibi_name_str(i), + unibi_short_name_str(i)); // Most of these strings will contain escape sequences. - msg_outtrans_special(s, false, 0); - msg_putchar('\n'); + kv_transstr(&data, s, false); + kv_push(data, '\n'); } } if (unibi_count_ext_bool(ut)) { - msg_puts("Extended boolean capabilities:\n"); + kv_printf(data, "Extended boolean capabilities:\n"); for (size_t i = 0; i < unibi_count_ext_bool(ut); i++) { - msg_printf_attr(0, " %-25s = %s\n", - unibi_get_ext_bool_name(ut, i), - unibi_get_ext_bool(ut, i) ? "true" : "false"); + kv_printf(data, " %-25s = %s\n", + unibi_get_ext_bool_name(ut, i), + unibi_get_ext_bool(ut, i) ? "true" : "false"); } } if (unibi_count_ext_num(ut)) { - msg_puts("Extended numeric capabilities:\n"); + kv_printf(data, "Extended numeric capabilities:\n"); for (size_t i = 0; i < unibi_count_ext_num(ut); i++) { - msg_printf_attr(0, " %-25s = %d\n", - unibi_get_ext_num_name(ut, i), - unibi_get_ext_num(ut, i)); + kv_printf(data, " %-25s = %d\n", + unibi_get_ext_num_name(ut, i), + unibi_get_ext_num(ut, i)); } } if (unibi_count_ext_str(ut)) { - msg_puts("Extended string capabilities:\n"); + kv_printf(data, "Extended string capabilities:\n"); for (size_t i = 0; i < unibi_count_ext_str(ut); i++) { - msg_printf_attr(0, " %-25s = ", unibi_get_ext_str_name(ut, i)); - msg_outtrans_special(unibi_get_ext_str(ut, i), false, 0); - msg_putchar('\n'); + kv_printf(data, " %-25s = ", unibi_get_ext_str_name(ut, i)); + // NOTE: unibi_get_ext_str(ut, i) might be NULL, as termcap + // might include junk data on mac os. kv_transstr will handle this. + kv_transstr(&data, unibi_get_ext_str(ut, i), false); + kv_push(data, '\n'); } } + kv_push(data, NUL); - msg_puts("}}}\n"); - xfree(term); + return cbuf_as_string(data.items, data.size - 1); } diff --git a/src/nvim/tui/terminfo.h b/src/nvim/tui/terminfo.h index 099df8967f..178d384457 100644 --- a/src/nvim/tui/terminfo.h +++ b/src/nvim/tui/terminfo.h @@ -3,6 +3,8 @@ #include <unibilium.h> +#include "nvim/api/private/defs.h" + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/terminfo.h.generated.h" #endif diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 14ad4ada36..3010a7b612 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -15,6 +15,8 @@ #include "auto/config.h" #include "klib/kvec.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/ascii.h" #include "nvim/event/defs.h" #include "nvim/event/loop.h" @@ -25,10 +27,12 @@ #include "nvim/grid_defs.h" #include "nvim/highlight_defs.h" #include "nvim/log.h" +#include "nvim/macros.h" #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/msgpack_rpc/channel.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" @@ -98,6 +102,7 @@ struct TUIData { TermInput input; uv_loop_t write_loop; unibi_term *ut; + char *term; // value of $TERM union { uv_tty_t tty; uv_pipe_t pipe; @@ -132,6 +137,7 @@ struct TUIData { bool default_attr; bool can_clear_attr; ModeShape showing_mode; + Integer verbose; struct { int enable_mouse, disable_mouse; int enable_mouse_move, disable_mouse_move; @@ -295,6 +301,7 @@ static void terminfo_start(UI *ui) os_env_var_unlock(); if (data->ut) { termname = xstrdup(term); + data->term = xstrdup(term); } } if (!data->ut) { @@ -509,9 +516,6 @@ static void tui_main(UIBridgeData *bridge, UI *ui) // Allow main thread to continue, we are ready to handle UI callbacks. CONTINUE(bridge); - loop_schedule_deferred(&main_loop, - event_create(show_termcap_event, 1, data->ut)); - // "Active" loop: first ~100 ms of startup. for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) { ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1); @@ -533,6 +537,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui) kv_destroy(data->invalid_regions); kv_destroy(data->attrs); xfree(data->space_buf); + xfree(data->term); xfree(data); } @@ -1246,6 +1251,11 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx) } #endif tui_set_mode(ui, (ModeShape)mode_idx); + if (data->is_starting) { + if (data->verbose >= 3) { + show_verbose_terminfo(data); + } + } data->is_starting = false; // mode entered, no longer starting data->showing_mode = (ModeShape)mode_idx; } @@ -1390,21 +1400,53 @@ static void tui_flush(UI *ui) } /// Dumps termcap info to the messages area, if 'verbose' >= 3. -static void show_termcap_event(void **argv) +static void show_verbose_terminfo(TUIData *data) { - if (p_verbose < 3) { - return; - } - const unibi_term *const ut = argv[0]; + const unibi_term *const ut = data->ut; if (!ut) { abort(); } - verbose_enter(); - // XXX: (future) if unibi_term is modified (e.g. after a terminal - // query-response) this is a race condition. - terminfo_info_msg(ut); - verbose_leave(); - verbose_stop(); // flush now + + Array chunks = ARRAY_DICT_INIT; + Array title = ARRAY_DICT_INIT; + ADD(title, STRING_OBJ(cstr_to_string("\n\n--- Terminal info --- {{{\n"))); + ADD(title, STRING_OBJ(cstr_to_string("Title"))); + ADD(chunks, ARRAY_OBJ(title)); + Array info = ARRAY_DICT_INIT; + String str = terminfo_info_msg(ut, data->term); + ADD(info, STRING_OBJ(str)); + ADD(chunks, ARRAY_OBJ(info)); + Array end_fold = ARRAY_DICT_INIT; + ADD(end_fold, STRING_OBJ(cstr_to_string("}}}\n"))); + ADD(end_fold, STRING_OBJ(cstr_to_string("Title"))); + ADD(chunks, ARRAY_OBJ(end_fold)); + + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + ADD(args, ARRAY_OBJ(chunks)); + ADD(args, BOOLEAN_OBJ(true)); // history + Dictionary opts = ARRAY_DICT_INIT; + PUT(opts, "verbose", BOOLEAN_OBJ(true)); + ADD(args, DICTIONARY_OBJ(opts)); + rpc_send_event(ui_client_channel_id, "nvim_echo", args); + } else { + loop_schedule_deferred(&main_loop, event_create(verbose_terminfo_event, 2, + chunks.items, chunks.size)); + } +} + +static void verbose_terminfo_event(void **argv) +{ + Array chunks = { .items = argv[0], .size = (size_t)argv[1] }; + Dict(echo_opts) opts = { .verbose = BOOLEAN_OBJ(true) }; + Error err = ERROR_INIT; + nvim_echo(chunks, true, &opts, &err); + api_free_array(chunks); + if (ERROR_SET(&err)) { + fprintf(stderr, "TUI bought the farm: %s\n", err.msg); + exit(1); + } + api_clear_error(&err); } #ifdef UNIX @@ -1509,6 +1551,8 @@ static void tui_option_set(UI *ui, String name, Object value) data->input.ttimeout = value.data.boolean; } else if (strequal(name.data, "ttimeoutlen")) { data->input.ttimeoutlen = (long)value.data.integer; + } else if (strequal(name.data, "verbose")) { + data->verbose = value.data.integer; } } diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index 6f9cea8f24..9d20229ce1 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -24,6 +24,7 @@ describe('UI receives option updates', function() termguicolors=false, ttimeout=true, ttimeoutlen=50, + verbose=0, ext_cmdline=false, ext_popupmenu=false, ext_tabline=false, |