mirror of
https://github.com/neovim/neovim.git
synced 2026-05-24 05:40:08 +00:00
fix(messages): fast context for for nvim_echo({kind}) callback #39755
Problem: vim.ui_attach() callback for nvim_echo() call that spoofs an
internal message kind is executed in fast context.
Solution: Set msg_show callback |api-fast| context dynamically at
external message callsites, and for internal list_cmd",
"progress" and "shell*" messages.
This commit is contained in:
@@ -924,6 +924,7 @@ static void write_msg(String message, bool to_err, bool writeln)
|
||||
static StringBuilder err_line_buf = KV_INITIAL_VALUE;
|
||||
StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
|
||||
|
||||
msg_ext_no_fast();
|
||||
#define PUSH_CHAR(c) \
|
||||
if (kv_max(*line_buf) == 0) { \
|
||||
kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
|
||||
|
||||
@@ -906,6 +906,7 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
|
||||
msg_didany = true;
|
||||
msg_no_more = true;
|
||||
}
|
||||
msg_ext_no_fast();
|
||||
id = msg_multihl(opts->id, hl_msg, kind, history, opts->err, &msg_data, &needs_clear);
|
||||
if (opts->_truncate) {
|
||||
msg_no_more = false;
|
||||
|
||||
@@ -6163,6 +6163,7 @@ void ex_echo(exarg_T *eap)
|
||||
if (atstart) {
|
||||
atstart = false;
|
||||
msg_ext_set_append(eap->cmdidx == CMD_echon);
|
||||
msg_ext_no_fast();
|
||||
msg_ext_set_kind("echo");
|
||||
// Call msg_start() after eval1(), evaluating the expression
|
||||
// may cause a message to appear.
|
||||
@@ -6263,11 +6264,13 @@ void ex_execute(exarg_T *eap)
|
||||
|
||||
if (ret != FAIL && ga.ga_data != NULL) {
|
||||
if (eap->cmdidx == CMD_echomsg) {
|
||||
msg_ext_no_fast();
|
||||
msg_ext_set_kind("echomsg");
|
||||
msg(ga.ga_data, echo_hl_id);
|
||||
} else if (eap->cmdidx == CMD_echoerr) {
|
||||
// We don't want to abort following commands, restore did_emsg.
|
||||
int save_did_emsg = did_emsg;
|
||||
msg_ext_no_fast();
|
||||
emsg_multiline(ga.ga_data, "echoerr", HLF_E, true);
|
||||
if (!force_abort) {
|
||||
did_emsg = save_did_emsg;
|
||||
|
||||
@@ -1219,6 +1219,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
|
||||
if (addr_count == 0) { // :!
|
||||
// echo the command
|
||||
msg_start();
|
||||
msg_ext_no_fast();
|
||||
msg_ext_set_kind("shell_cmd");
|
||||
msg_putchar(':');
|
||||
msg_putchar('!');
|
||||
|
||||
@@ -306,6 +306,7 @@ void nlua_error(lua_State *const lstate, const char *const msg)
|
||||
fprintf(stderr, msg, (int)len, str);
|
||||
fprintf(stderr, "\n");
|
||||
} else {
|
||||
msg_ext_no_fast();
|
||||
semsg_multiline("lua_error", msg, (int)len, str);
|
||||
}
|
||||
|
||||
@@ -342,6 +343,7 @@ static void nlua_luv_error_event(void **argv)
|
||||
luv_err_t type = (luv_err_t)(intptr_t)argv[1];
|
||||
switch (type) {
|
||||
case kCallback:
|
||||
msg_ext_no_fast();
|
||||
semsg_multiline("lua_error", "Lua callback:\n%s", error);
|
||||
break;
|
||||
case kThread:
|
||||
@@ -1049,6 +1051,7 @@ static void nlua_print_event(void **argv)
|
||||
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
|
||||
kv_push(msg, chunk);
|
||||
bool needs_clear = false;
|
||||
msg_ext_no_fast();
|
||||
msg_multihl(NIL, msg, "lua_print", true, false, NULL, &needs_clear);
|
||||
}
|
||||
|
||||
|
||||
@@ -1111,6 +1111,7 @@ char *msg_progress(char *s, char *id, char *status, int hl_id, bool hist, bool t
|
||||
};
|
||||
HlMessage chunks = KV_INITIAL_VALUE;
|
||||
kv_push(chunks, ((HlMessageChunk){ cstr_as_string(s), hl_id }));
|
||||
msg_ext_no_fast();
|
||||
msg_multihl(CSTR_AS_OBJ(id), chunks, "progress", false, false, &data, &clear);
|
||||
kv_destroy(chunks);
|
||||
ui_flush();
|
||||
@@ -1705,6 +1706,10 @@ void msg_ext_set_kind(const char *msg_kind)
|
||||
// the kind but this is called more consistently at the start of a message
|
||||
// than msg_start() at this point.
|
||||
redir_col = msg_ext_append ? redir_col : 0;
|
||||
|
||||
if (strcmp("list_cmd", msg_kind) == 0) {
|
||||
msg_ext_no_fast();
|
||||
}
|
||||
}
|
||||
|
||||
void msg_ext_set_append(bool append)
|
||||
@@ -1719,6 +1724,13 @@ void msg_ext_set_trigger(const char *trigger)
|
||||
msg_ext_trigger = trigger;
|
||||
}
|
||||
|
||||
// Should be executed at all callsites emitting non-internal messages.
|
||||
void msg_ext_no_fast(void)
|
||||
{
|
||||
msg_ext_ui_flush();
|
||||
msg_ext_fast = false;
|
||||
}
|
||||
|
||||
/// Prepare for outputting characters in the command line.
|
||||
void msg_start(void)
|
||||
{
|
||||
@@ -2366,7 +2378,7 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
|
||||
// Don't print anything when using ":silent cmd" or empty message.
|
||||
if (msg_silent != 0 || *str == NUL) {
|
||||
if (*str == NUL && ui_has(kUIMessages)) {
|
||||
msg_ext_ui_flush(); // ensure messages until now are emitted
|
||||
msg_ext_no_fast();
|
||||
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false,
|
||||
INTEGER_OBJ(-1), (String)STRING_INIT);
|
||||
cmdline_was_last_drawn = false;
|
||||
@@ -3421,6 +3433,7 @@ void msg_ext_ui_flush(void)
|
||||
msg_ext_overwrite = false;
|
||||
msg_ext_history = false;
|
||||
msg_ext_append = false;
|
||||
msg_ext_fast = true;
|
||||
msg_ext_kind = NULL;
|
||||
msg_id_next += (msg_ext_id.data.integer == msg_id_next);
|
||||
msg_ext_id = INTEGER_OBJ(msg_id_next);
|
||||
|
||||
@@ -37,6 +37,8 @@ EXTERN bool msg_ext_skip_flush INIT( = false);
|
||||
EXTERN bool msg_ext_overwrite INIT( = false);
|
||||
/// Set to true to avoid setting "verbose" kind for "last set" messages.
|
||||
EXTERN bool msg_ext_skip_verbose INIT( = false);
|
||||
/// Set to false for non-internal messages to determine UI callback |api-fast| context.
|
||||
EXTERN bool msg_ext_fast INIT( = true);
|
||||
|
||||
/// allocated grid for messages. Used unless ext_messages is active.
|
||||
/// See also the description at msg_scroll_flush()
|
||||
|
||||
@@ -701,6 +701,7 @@ int os_call_shell(char *cmd, int opts, char *extra_args)
|
||||
|
||||
if (!emsg_silent && exitcode != 0 && !(opts & kShellOptSilent)) {
|
||||
msg_ext_set_kind("shell_ret");
|
||||
msg_ext_no_fast();
|
||||
if (!ui_has(kUIMessages)) {
|
||||
msg_putchar('\n');
|
||||
}
|
||||
@@ -1126,6 +1127,7 @@ static void out_data_event(void **argv)
|
||||
int hl = (int)(intptr_t)argv[2] == STDERR_FILENO ? HLF_SE : HLF_SO;
|
||||
msg_ext_set_kind((int)(intptr_t)argv[2] == STDERR_FILENO ? "shell_err" : "shell_out");
|
||||
msg_ext_set_append(true);
|
||||
msg_ext_no_fast();
|
||||
msg_multiline(cbuf_as_string((char *)argv[0], (size_t)argv[1]), hl, false, false, &need_clear);
|
||||
xfree(argv[0]);
|
||||
ui_flush();
|
||||
|
||||
@@ -777,28 +777,6 @@ static void ui_attach_error(uint32_t ns_id, const char *name, const char *msg)
|
||||
|
||||
void ui_call_event(char *name, Array args)
|
||||
{
|
||||
// Internal messages are considered unsafe and are executed in fast context.
|
||||
bool fast = strcmp(name, "msg_show") == 0;
|
||||
const char *not_fast[] = {
|
||||
"empty",
|
||||
"echo",
|
||||
"echomsg",
|
||||
"echoerr",
|
||||
"list_cmd",
|
||||
"lua_error",
|
||||
"lua_print",
|
||||
"progress",
|
||||
"shell_cmd",
|
||||
"shell_err",
|
||||
"shell_out",
|
||||
"shell_ret",
|
||||
NULL,
|
||||
};
|
||||
|
||||
for (int i = 0; fast && not_fast[i]; i++) {
|
||||
fast = !strequal(not_fast[i], args.items[0].data.string.data);
|
||||
}
|
||||
|
||||
// Don't impose textlock restrictions upon UI event handlers.
|
||||
int save_expr_map_lock = expr_map_lock;
|
||||
int save_textlock = textlock;
|
||||
@@ -806,6 +784,7 @@ void ui_call_event(char *name, Array args)
|
||||
textlock = 0;
|
||||
|
||||
bool handled = false;
|
||||
bool fast = msg_ext_fast && strcmp("msg_show", name) == 0;
|
||||
UIEventCallback *event_cb;
|
||||
map_foreach(&ui_event_cbs, ui_event_ns_id, event_cb, {
|
||||
Error err = ERROR_INIT;
|
||||
|
||||
@@ -691,6 +691,19 @@ describe('messages2', function()
|
||||
{1:~ }|*12
|
||||
foo |
|
||||
]])
|
||||
feed('<CR>')
|
||||
-- Fast context is not determined by message kind #39666
|
||||
exec_lua(function()
|
||||
vim.schedule(function()
|
||||
vim.api.nvim_echo({ { 'bar' } }, false, { kind = 'search_cmd' })
|
||||
vim.fn.getchar()
|
||||
end)
|
||||
end)
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*12
|
||||
bar |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('properly formatted carriage return messages', function()
|
||||
|
||||
Reference in New Issue
Block a user