Merge #37926 msg_show UI event indicates user-interactive

This commit is contained in:
Justin M. Keyes
2026-03-10 17:53:11 -04:00
committed by GitHub
10 changed files with 72 additions and 21 deletions

View File

@@ -829,9 +829,9 @@ will be set to zero, but can be changed and used for the replacing cmdline or
message window. Cmdline state is emitted as |ui-cmdline| events, which the UI
must handle.
["msg_show", kind, content, replace_last, history, append, msg_id] ~
["msg_show", kind, content, replace_last, history, append, id, trigger] ~
Display a message to the user. Update (replace) any existing message
matching `msg_id`.
matching `id`.
kind
Name indicating the message kind:
@@ -887,9 +887,14 @@ must handle.
True if the message should be appended to the previous message,
rather than started on a new line. Is set for |:echon|.
msg_id
id
Unique identifier for the message. It can either be an integer or
string. When message of same id appears it should replace the older message.
string. A (visible) message with the same id should be replaced.
trigger
Type of action that triggered the message:
"" (empty) Unknown (consider a |feature-request|)
"typed_cmd" Interactively typed command on the |cmdline|
["msg_clear"] ~
Clear all messages currently displayed by "msg_show", emitted after

View File

@@ -5288,7 +5288,7 @@ To enable the experimental UI (default opts shown): >lua
---@type 'cmd'|'msg' Default message target, either in the
---cmdline or in a separate ephemeral message window.
---@type string|table<string, 'cmd'|'msg'|'pager'> Default message target
or table mapping |ui-messages| kinds to a target.
or table mapping |ui-messages| kinds and triggers to a target.
targets = 'cmd',
timeout = 4000, -- Time a message is visible in the message window.
},

View File

@@ -236,8 +236,7 @@ EVENTS
• A new `empty` message kind is emitted for an empty (e.g. `:echo ""`) message.
• |CmdlineLeave| sets |v:char| to the character that stops the Cmdline mode.
• |CmdlineLeavePre| triggered before preparing to leave the command line.
• New `append` parameter for |ui-messages| `msg_show` event.
• New `msg_id` parameter for |ui-messages| `msg_show` event.
• New `append`, `id` and `trigger` parameter for |ui-messages| `msg_show` event.
• 'rulerformat' is emitted as `msg_ruler` when not part of the statusline.
• Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event.
• |MarkSet| is triggered after a |mark| is set by the user (currently doesn't

View File

@@ -11,7 +11,7 @@
--- ---@type 'cmd'|'msg' Default message target, either in the
--- ---cmdline or in a separate ephemeral message window.
--- ---@type string|table<string, 'cmd'|'msg'|'pager'> Default message target
--- or table mapping |ui-messages| kinds to a target.
--- or table mapping |ui-messages| kinds and triggers to a target.
--- targets = 'cmd',
--- timeout = 4000, -- Time a message is visible in the message window.
--- },

View File

@@ -379,9 +379,14 @@ end
--@param history boolean
---@param append boolean
---@param id integer|string
function M.msg_show(kind, content, replace_last, _, append, id)
-- Set the entered search command in the cmdline (if available).
local tgt = kind == 'search_cmd' and 'cmd' or ui.cfg.msg.targets[kind] or ui.cfg.msg.target
---@param trigger string
function M.msg_show(kind, content, replace_last, _, append, id, trigger)
-- Set the entered search command in the cmdline (if available). Otherwise route
-- to configured target: 'trigger' takes precedence over 'kind.'
local tgt = kind == 'search_cmd' and 'cmd'
or ui.cfg.msg.targets[trigger]
or ui.cfg.msg.targets[kind]
or ui.cfg.msg.target
if kind == 'search_cmd' and ui.cmdheight == 0 then
-- Blocked by messaging() without ext_messages. TODO: look at other messaging() guards.
return

View File

@@ -165,7 +165,7 @@ void wildmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void msg_show(String kind, Array content, Boolean replace_last, Boolean history, Boolean append,
Object id)
Object id, String trigger)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void msg_clear(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;

View File

@@ -152,6 +152,7 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
// Extended msg state, currently used for external UIs with ext_messages
static const char *msg_ext_kind = NULL;
static const char *msg_ext_trigger = NULL;
static MsgID msg_ext_id = { .type = kObjectTypeInteger, .data.integer = 1 };
static Array *msg_ext_chunks = NULL;
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
@@ -1650,6 +1651,13 @@ void msg_ext_set_kind(const char *msg_kind)
redir_col = msg_ext_append ? redir_col : 0;
}
void msg_ext_set_trigger(const char *trigger)
{
// Don't change the trigger of an existing batch:
msg_ext_ui_flush();
msg_ext_trigger = trigger;
}
/// Prepare for outputting characters in the command line.
void msg_start(void)
{
@@ -2294,7 +2302,7 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
if (msg_silent != 0 || *str == NUL) {
if (*str == NUL && ui_has(kUIMessages)) {
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false,
INTEGER_OBJ(-1));
INTEGER_OBJ(-1), (String)STRING_INIT);
}
return;
}
@@ -3326,7 +3334,7 @@ void msg_ext_ui_flush(void)
Array *tofree = msg_ext_init_chunks();
ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history,
msg_ext_append, msg_ext_id);
msg_ext_append, msg_ext_id, cstr_as_string(msg_ext_trigger));
// clear info after emitting message.
if (msg_ext_history) {
api_free_array(*tofree);

View File

@@ -3186,9 +3186,9 @@ static void nv_colon(cmdarg_T *cap)
}
}
// When typing, don't type below an old message
if (KeyTyped) {
compute_cmdrow();
msg_ext_set_trigger("typed_cmd"); // distinguish msg_show emitted for typed cmd
compute_cmdrow(); // when typing, don't type below an old message
}
if (is_lua) {
@@ -3198,6 +3198,7 @@ static void nv_colon(cmdarg_T *cap)
cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL,
cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
}
msg_ext_set_trigger("");
if (cmd_result == false) {
// The Ex command failed, do not execute the operator.

View File

@@ -1638,6 +1638,34 @@ stack traceback:
},
})
end)
it('trigger', function()
command('echo "foo"')
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = { { content = { { 'foo' } }, kind = 'echo', trigger = '' } },
})
command('map Q :echo "foo"<CR>')
feed('Q')
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = { { content = { { 'foo' } }, kind = 'echo', trigger = '' } },
})
feed(':echo "foo"<CR>')
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = { { content = { { 'foo' } }, kind = 'echo', trigger = 'typed_cmd' } },
})
end)
end)
describe('ui/builtin messages', function()

View File

@@ -671,7 +671,7 @@ screen:redraw_debug() to show all intermediate screen states.]]
-- the ext_ feature being disabled, or the feature currently not activated
-- (e.g. no external cmdline visible). Some extensions require
-- preprocessing to represent highlights in a reproducible way.
local extstate = self:_extstate_repr(attr_state)
local extstate = self:_extstate_repr(attr_state, expected)
if expected.mode ~= nil then
extstate.mode = self.mode
end
@@ -1440,7 +1440,7 @@ function Screen:_handle_wildmenu_hide()
self.wildmenu_items, self.wildmenu_pos = nil, nil
end
function Screen:_handle_msg_show(kind, chunks, replace_last, history, append, id, progress)
function Screen:_handle_msg_show(kind, chunks, replace_last, history, append, id, trigger)
local pos = #self.messages
if not replace_last or pos == 0 then
pos = pos + 1
@@ -1451,7 +1451,7 @@ function Screen:_handle_msg_show(kind, chunks, replace_last, history, append, id
history = history,
append = append,
id = id,
progress = progress,
trigger = trigger,
}
end
@@ -1560,7 +1560,7 @@ local function hl_id_to_name(self, id)
return id and self.hl_names[id] or nil
end
function Screen:_extstate_repr(attr_state)
function Screen:_extstate_repr(attr_state, exp)
local cmdline = {}
for i, entry in pairs(self.cmdline) do
entry = shallowcopy(entry)
@@ -1578,13 +1578,18 @@ function Screen:_extstate_repr(attr_state)
local messages = {}
for i, entry in ipairs(self.messages) do
local trigger = nil
if exp and exp.messages and exp.messages[i] and exp.messages[i].trigger ~= nil then
-- Late addition, only include when expected state includes it.
trigger = entry.trigger
end
messages[i] = {
kind = entry.kind,
content = self:_chunks_repr(entry.content, attr_state),
history = entry.history or nil,
append = entry.append or nil,
id = entry.kind == 'progress' and entry.id or nil,
progress = entry.kind == 'progress' and entry.progress or nil,
trigger = trigger,
}
end