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: ~
• {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

View File

@ -219,5 +219,8 @@ return {
cmd_opts = {
"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
/// 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;

View File

@ -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.
///

View File

@ -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

View File

@ -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}
},
{

View File

@ -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),
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),
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),
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",
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",
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);
}

View File

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

View File

@ -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;
}
}

View File

@ -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,