aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/api.txt8
-rw-r--r--src/nvim/api/keysets.lua3
-rw-r--r--src/nvim/api/vim.c19
-rw-r--r--src/nvim/charset.c16
-rw-r--r--src/nvim/charset.h1
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/tui/terminfo.c75
-rw-r--r--src/nvim/tui/terminfo.h2
-rw-r--r--src/nvim/tui/tui.c72
-rw-r--r--test/functional/ui/options_spec.lua1
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,