refactor(tui): use nvim_echo() for verbose terminfo

This is needed for #18375 for the obvious reasons.
note: verbose_terminfo_event is only temporarily needed
until the full TUI process refactor is merged.
This commit is contained in:
bfredl 2022-12-16 13:50:12 +01:00
parent f04087d8ba
commit b42d8a43b9
10 changed files with 140 additions and 59 deletions

View File

@ -717,7 +717,7 @@ nvim_del_var({name}) *nvim_del_var()*
Parameters: ~ Parameters: ~
• {name} Variable name • {name} Variable name
nvim_echo({chunks}, {history}, {opts}) *nvim_echo()* nvim_echo({chunks}, {history}, {*opts}) *nvim_echo()*
Echo a message. Echo a message.
Parameters: ~ Parameters: ~
@ -725,7 +725,11 @@ nvim_echo({chunks}, {history}, {opts}) *nvim_echo()*
chunk with specified highlight. `hl_group` element can be chunk with specified highlight. `hl_group` element can be
omitted for no highlight. omitted for no highlight.
• {history} if true, add to |message-history|. • {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()* nvim_err_write({str}) *nvim_err_write()*
Writes a message to the Vim error buffer. Does not append "\n", the Writes a message to the Vim error buffer. Does not append "\n", the

View File

@ -219,5 +219,8 @@ return {
cmd_opts = { cmd_opts = {
"output"; "output";
}; };
echo_opts = {
"verbose";
};
} }

View File

@ -726,8 +726,11 @@ void nvim_set_vvar(String name, Object value, Error *err)
/// text chunk with specified highlight. `hl_group` element /// text chunk with specified highlight. `hl_group` element
/// can be omitted for no highlight. /// can be omitted for no highlight.
/// @param history if true, add to |message-history|. /// @param history if true, add to |message-history|.
/// @param opts Optional parameters. Reserved for future use. /// @param opts Optional parameters.
void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err) /// - 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) FUNC_API_SINCE(7)
{ {
HlMessage hl_msg = parse_hl_msg(chunks, err); 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; goto error;
} }
if (opts.size > 0) { bool verbose = api_object_to_bool(opts->verbose, "verbose", false, err);
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
goto error; if (verbose) {
verbose_enter();
} }
msg_multiattr(hl_msg, history ? "echomsg" : "echo", history); msg_multiattr(hl_msg, history ? "echomsg" : "echo", history);
if (verbose) {
verbose_leave();
verbose_stop(); // flush now
}
if (history) { if (history) {
// history takes ownership // history takes ownership
return; return;

View File

@ -418,6 +418,22 @@ char *transstr(const char *const s, bool untab)
return buf; 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. /// Convert the string "str[orglen]" to do ignore-case comparing.
/// Use the current locale. /// Use the current locale.
/// ///

View File

@ -7,6 +7,7 @@
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/option_defs.h" #include "nvim/option_defs.h"
#include "nvim/pos.h" #include "nvim/pos.h"
#include "nvim/strings.h"
#include "nvim/types.h" #include "nvim/types.h"
/// Return the folded-case equivalent of the given character /// Return the folded-case equivalent of the given character

View File

@ -2690,7 +2690,7 @@ return {
full_name='verbose', abbreviation='vbs', full_name='verbose', abbreviation='vbs',
short_desc=N_("give informative messages"), short_desc=N_("give informative messages"),
type='number', scope={'global'}, type='number', scope={'global'},
varname='p_verbose', varname='p_verbose', redraw={'ui_option'},
defaults={if_true=0} defaults={if_true=0}
}, },
{ {

View File

@ -7,10 +7,13 @@
#include <string.h> #include <string.h>
#include <unibilium.h> #include <unibilium.h>
#include "nvim/api/private/helpers.h"
#include "nvim/charset.h"
#include "nvim/globals.h" #include "nvim/globals.h"
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/message.h" #include "nvim/message.h"
#include "nvim/option.h" #include "nvim/option.h"
#include "nvim/strings.h"
#include "nvim/tui/terminfo.h" #include "nvim/tui/terminfo.h"
#include "nvim/tui/terminfo_defs.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). /// Serves a similar purpose as Vim `:set termcap` (removed in Nvim).
/// ///
/// @note adapted from unibilium unibi-dump.c /// @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) { StringBuilder data = KV_INITIAL_VALUE;
return;
}
msg_puts_title("\n\n--- Terminal info --- {{{\n");
char *term; kv_printf(data, "&term: %s\n", termname);
get_tty_option("term", &term); kv_printf(data, "Description: %s\n", unibi_get_name(ut));
msg_printf_attr(0, "&term: %s\n", term);
msg_printf_attr(0, "Description: %s\n", unibi_get_name(ut));
const char **a = unibi_get_aliases(ut); const char **a = unibi_get_aliases(ut);
if (*a) { if (*a) {
msg_puts("Aliases: "); kv_printf(data, "Aliases: ");
do { do {
msg_printf_attr(0, "%s%s\n", *a, a[1] ? " | " : ""); kv_printf(data, "%s%s\n", *a, a[1] ? " | " : "");
a++; a++;
} while (*a); } while (*a);
} }
msg_puts("Boolean capabilities:\n"); kv_printf(data, "Boolean capabilities:\n");
for (enum unibi_boolean i = unibi_boolean_begin_ + 1; for (enum unibi_boolean i = unibi_boolean_begin_ + 1;
i < unibi_boolean_end_; i++) { i < unibi_boolean_end_; i++) {
msg_printf_attr(0, " %-25s %-10s = %s\n", unibi_name_bool(i), kv_printf(data, " %-25s %-10s = %s\n", unibi_name_bool(i),
unibi_short_name_bool(i), unibi_short_name_bool(i),
unibi_get_bool(ut, i) ? "true" : "false"); 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; for (enum unibi_numeric i = unibi_numeric_begin_ + 1;
i < unibi_numeric_end_; i++) { i < unibi_numeric_end_; i++) {
int n = unibi_get_num(ut, i); // -1 means "empty" int n = unibi_get_num(ut, i); // -1 means "empty"
msg_printf_attr(0, " %-25s %-10s = %d\n", unibi_name_num(i), kv_printf(data, " %-25s %-10s = %d\n", unibi_name_num(i),
unibi_short_name_num(i), n); 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; for (enum unibi_string i = unibi_string_begin_ + 1;
i < unibi_string_end_; i++) { i < unibi_string_end_; i++) {
const char *s = unibi_get_str(ut, i); const char *s = unibi_get_str(ut, i);
if (s) { if (s) {
msg_printf_attr(0, " %-25s %-10s = ", unibi_name_str(i), kv_printf(data, " %-25s %-10s = ", unibi_name_str(i),
unibi_short_name_str(i)); unibi_short_name_str(i));
// Most of these strings will contain escape sequences. // Most of these strings will contain escape sequences.
msg_outtrans_special(s, false, 0); kv_transstr(&data, s, false);
msg_putchar('\n'); kv_push(data, '\n');
} }
} }
if (unibi_count_ext_bool(ut)) { 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++) { for (size_t i = 0; i < unibi_count_ext_bool(ut); i++) {
msg_printf_attr(0, " %-25s = %s\n", kv_printf(data, " %-25s = %s\n",
unibi_get_ext_bool_name(ut, i), unibi_get_ext_bool_name(ut, i),
unibi_get_ext_bool(ut, i) ? "true" : "false"); unibi_get_ext_bool(ut, i) ? "true" : "false");
} }
} }
if (unibi_count_ext_num(ut)) { 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++) { for (size_t i = 0; i < unibi_count_ext_num(ut); i++) {
msg_printf_attr(0, " %-25s = %d\n", kv_printf(data, " %-25s = %d\n",
unibi_get_ext_num_name(ut, i), unibi_get_ext_num_name(ut, i),
unibi_get_ext_num(ut, i)); unibi_get_ext_num(ut, i));
} }
} }
if (unibi_count_ext_str(ut)) { 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++) { for (size_t i = 0; i < unibi_count_ext_str(ut); i++) {
msg_printf_attr(0, " %-25s = ", unibi_get_ext_str_name(ut, i)); kv_printf(data, " %-25s = ", unibi_get_ext_str_name(ut, i));
msg_outtrans_special(unibi_get_ext_str(ut, i), false, 0); // NOTE: unibi_get_ext_str(ut, i) might be NULL, as termcap
msg_putchar('\n'); // 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"); return cbuf_as_string(data.items, data.size - 1);
xfree(term);
} }

View File

@ -3,6 +3,8 @@
#include <unibilium.h> #include <unibilium.h>
#include "nvim/api/private/defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/terminfo.h.generated.h" # include "tui/terminfo.h.generated.h"
#endif #endif

View File

@ -15,6 +15,8 @@
#include "auto/config.h" #include "auto/config.h"
#include "klib/kvec.h" #include "klib/kvec.h"
#include "nvim/api/private/defs.h" #include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/event/defs.h" #include "nvim/event/defs.h"
#include "nvim/event/loop.h" #include "nvim/event/loop.h"
@ -25,10 +27,12 @@
#include "nvim/grid_defs.h" #include "nvim/grid_defs.h"
#include "nvim/highlight_defs.h" #include "nvim/highlight_defs.h"
#include "nvim/log.h" #include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h" #include "nvim/main.h"
#include "nvim/mbyte.h" #include "nvim/mbyte.h"
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/message.h" #include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/option.h" #include "nvim/option.h"
#include "nvim/os/input.h" #include "nvim/os/input.h"
#include "nvim/os/os.h" #include "nvim/os/os.h"
@ -98,6 +102,7 @@ struct TUIData {
TermInput input; TermInput input;
uv_loop_t write_loop; uv_loop_t write_loop;
unibi_term *ut; unibi_term *ut;
char *term; // value of $TERM
union { union {
uv_tty_t tty; uv_tty_t tty;
uv_pipe_t pipe; uv_pipe_t pipe;
@ -132,6 +137,7 @@ struct TUIData {
bool default_attr; bool default_attr;
bool can_clear_attr; bool can_clear_attr;
ModeShape showing_mode; ModeShape showing_mode;
Integer verbose;
struct { struct {
int enable_mouse, disable_mouse; int enable_mouse, disable_mouse;
int enable_mouse_move, disable_mouse_move; int enable_mouse_move, disable_mouse_move;
@ -295,6 +301,7 @@ static void terminfo_start(UI *ui)
os_env_var_unlock(); os_env_var_unlock();
if (data->ut) { if (data->ut) {
termname = xstrdup(term); termname = xstrdup(term);
data->term = xstrdup(term);
} }
} }
if (!data->ut) { 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. // Allow main thread to continue, we are ready to handle UI callbacks.
CONTINUE(bridge); CONTINUE(bridge);
loop_schedule_deferred(&main_loop,
event_create(show_termcap_event, 1, data->ut));
// "Active" loop: first ~100 ms of startup. // "Active" loop: first ~100 ms of startup.
for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) { for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) {
ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1); 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->invalid_regions);
kv_destroy(data->attrs); kv_destroy(data->attrs);
xfree(data->space_buf); xfree(data->space_buf);
xfree(data->term);
xfree(data); xfree(data);
} }
@ -1246,6 +1251,11 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx)
} }
#endif #endif
tui_set_mode(ui, (ModeShape)mode_idx); 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->is_starting = false; // mode entered, no longer starting
data->showing_mode = (ModeShape)mode_idx; 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. /// 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) { const unibi_term *const ut = data->ut;
return;
}
const unibi_term *const ut = argv[0];
if (!ut) { if (!ut) {
abort(); abort();
} }
verbose_enter();
// XXX: (future) if unibi_term is modified (e.g. after a terminal Array chunks = ARRAY_DICT_INIT;
// query-response) this is a race condition. Array title = ARRAY_DICT_INIT;
terminfo_info_msg(ut); ADD(title, STRING_OBJ(cstr_to_string("\n\n--- Terminal info --- {{{\n")));
verbose_leave(); ADD(title, STRING_OBJ(cstr_to_string("Title")));
verbose_stop(); // flush now 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 #ifdef UNIX
@ -1509,6 +1551,8 @@ static void tui_option_set(UI *ui, String name, Object value)
data->input.ttimeout = value.data.boolean; data->input.ttimeout = value.data.boolean;
} else if (strequal(name.data, "ttimeoutlen")) { } else if (strequal(name.data, "ttimeoutlen")) {
data->input.ttimeoutlen = (long)value.data.integer; data->input.ttimeoutlen = (long)value.data.integer;
} else if (strequal(name.data, "verbose")) {
data->verbose = value.data.integer;
} }
} }

View File

@ -24,6 +24,7 @@ describe('UI receives option updates', function()
termguicolors=false, termguicolors=false,
ttimeout=true, ttimeout=true,
ttimeoutlen=50, ttimeoutlen=50,
verbose=0,
ext_cmdline=false, ext_cmdline=false,
ext_popupmenu=false, ext_popupmenu=false,
ext_tabline=false, ext_tabline=false,