diff --git a/runtime/doc/api-ui-events.txt b/runtime/doc/api-ui-events.txt index be6ff7b4c3..67ef56c828 100644 --- a/runtime/doc/api-ui-events.txt +++ b/runtime/doc/api-ui-events.txt @@ -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 diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 6467c0f2e6..ae3f0e1ac8 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -234,8 +234,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 diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 609d7d50a4..c58f577c56 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -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; diff --git a/src/nvim/message.c b/src/nvim/message.c index 723362f681..4dae0847f0 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -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) { @@ -2285,7 +2293,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; } @@ -3317,7 +3325,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); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 3a7e8b9856..64158a5fe4 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -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. diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 1e1147e9ee..5ac8466d76 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1636,6 +1636,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"') + feed('Q') + screen:expect({ + grid = [[ + ^ | + {1:~ }|*4 + ]], + messages = { { content = { { 'foo' } }, kind = 'echo', trigger = '' } }, + }) + feed(':echo "foo"') + screen:expect({ + grid = [[ + ^ | + {1:~ }|*4 + ]], + messages = { { content = { { 'foo' } }, kind = 'echo', trigger = 'typed_cmd' } }, + }) + end) end) describe('ui/builtin messages', function() diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index b372e9995d..d9501b8400 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -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