mirror of
https://github.com/neovim/neovim.git
synced 2025-10-04 17:06:30 +00:00
feat(api): nvim_echo can emit Progress messages/events #34846
Problem: Nvim does not have a core concept for indicating "progress" of long-running tasks. The LspProgress event is specific to LSP. Solution: - `nvim_echo` can emit `kind="progress"` messages. - Emits a `Progress` event. - Includes new fields (id, status, percent) in the `msg_show` ui-event. - The UI is expected to overwrite any message having the same id. - Messages have a globally unique ID. - `nvim_echo` returns the message ID. - `nvim_echo(… {id=…})` updates existing messages. Example: local grp = vim.api.nvim_create_augroup("Msg", {clear = true}) vim.api.nvim_create_autocmd('Progress', { pattern={"term"}, group = grp, callback = function(ev) print(string.format('event fired: %s', vim.inspect(ev))..'\n') end }) -- require('vim._extui').enable({enable=true, msg={target='msg', timeout=1000}}) vim.api.nvim_echo({{'searching'}}, true, {kind='progress', percent=80, status='running', title="terminal(ripgrep)"}) local id = vim.api.nvim_echo({{'searching'}}, true, {kind='progress', status='running', percent=10, title="terminal(ripgrep)"}) vim.api.nvim_echo({}, true, {id = id, kind='progress', percent=20, status = 'running', title='find tests'}) vim.api.nvim_echo({}, true, {id = id, kind='progress', status='running', percent=70}) vim.api.nvim_echo({{'complete'}}, true, {id = id, kind='progress', status='success', percent=100, title="find tests"}) Followups: - Integrate with 'statusline' by listening to the Progress autocmd event. - Integrate progress ui-event with `vim._extui`.
This commit is contained in:
@@ -825,7 +825,7 @@ 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
|
message window. Cmdline state is emitted as |ui-cmdline| events, which the UI
|
||||||
must handle.
|
must handle.
|
||||||
|
|
||||||
["msg_show", kind, content, replace_last, history, append] ~
|
["msg_show", kind, content, replace_last, history, append, msg_id, progress] ~
|
||||||
Display a message to the user.
|
Display a message to the user.
|
||||||
|
|
||||||
kind
|
kind
|
||||||
@@ -845,6 +845,7 @@ must handle.
|
|||||||
"list_cmd" List output for various commands (|:ls|, |:set|, …)
|
"list_cmd" List output for various commands (|:ls|, |:set|, …)
|
||||||
"lua_error" Error in |:lua| code
|
"lua_error" Error in |:lua| code
|
||||||
"lua_print" |print()| from |:lua| code
|
"lua_print" |print()| from |:lua| code
|
||||||
|
"progress" Progress message emitted by |nvim_echo()|
|
||||||
"rpc_error" Error response from |rpcrequest()|
|
"rpc_error" Error response from |rpcrequest()|
|
||||||
"quickfix" Quickfix navigation message
|
"quickfix" Quickfix navigation message
|
||||||
"search_cmd" Entered search command
|
"search_cmd" Entered search command
|
||||||
@@ -881,6 +882,22 @@ must handle.
|
|||||||
True if the message should be appeneded to the previous message,
|
True if the message should be appeneded to the previous message,
|
||||||
rather than started on a new line. Is set for |:echon|.
|
rather than started on a new line. Is set for |:echon|.
|
||||||
|
|
||||||
|
msg_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.
|
||||||
|
|
||||||
|
progress
|
||||||
|
Progress-message properties:
|
||||||
|
• title: Title string of the progress message.
|
||||||
|
• status: Status of the progress message. Can contain one of
|
||||||
|
the following values
|
||||||
|
• success: The progress item completed successfully
|
||||||
|
• running: The progress is ongoing
|
||||||
|
• failed: The progress item failed
|
||||||
|
• cancel: The progressing process should be canceled.
|
||||||
|
• percent: How much progress is done on the progress
|
||||||
|
message
|
||||||
|
|
||||||
["msg_clear"] ~
|
["msg_clear"] ~
|
||||||
Clear all messages currently displayed by "msg_show", emitted after
|
Clear all messages currently displayed by "msg_show", emitted after
|
||||||
clearing the screen (messages sent by other "msg_" events below should
|
clearing the screen (messages sent by other "msg_" events below should
|
||||||
|
@@ -662,6 +662,7 @@ nvim_echo({chunks}, {history}, {opts}) *nvim_echo()*
|
|||||||
(optional) name or ID `hl_group`.
|
(optional) name or ID `hl_group`.
|
||||||
• {history} (`boolean`) if true, add to |message-history|.
|
• {history} (`boolean`) if true, add to |message-history|.
|
||||||
• {opts} (`vim.api.keyset.echo_opts`) Optional parameters.
|
• {opts} (`vim.api.keyset.echo_opts`) Optional parameters.
|
||||||
|
• id: message id for updating existing message.
|
||||||
• err: Treat the message like `:echoerr`. Sets `hl_group`
|
• err: Treat the message like `:echoerr`. Sets `hl_group`
|
||||||
to |hl-ErrorMsg| by default.
|
to |hl-ErrorMsg| by default.
|
||||||
• kind: Set the |ui-messages| kind with which this message
|
• kind: Set the |ui-messages| kind with which this message
|
||||||
@@ -669,6 +670,22 @@ nvim_echo({chunks}, {history}, {opts}) *nvim_echo()*
|
|||||||
• verbose: Message is controlled by the 'verbose' option.
|
• verbose: Message is controlled by the 'verbose' option.
|
||||||
Nvim invoked with `-V3log` will write the message to the
|
Nvim invoked with `-V3log` will write the message to the
|
||||||
"log" file instead of standard output.
|
"log" file instead of standard output.
|
||||||
|
• title: The title for |progress-message|.
|
||||||
|
• status: Current status of the |progress-message|. Can be
|
||||||
|
one of the following values
|
||||||
|
• success: The progress item completed successfully
|
||||||
|
• running: The progress is ongoing
|
||||||
|
• failed: The progress item failed
|
||||||
|
• cancel: The progressing process should be canceled.
|
||||||
|
note: Cancel needs to be handled by progress initiator
|
||||||
|
by listening for the `Progress` event
|
||||||
|
• percent: How much progress is done on the progress
|
||||||
|
message
|
||||||
|
• data: dictionary containing additional information
|
||||||
|
|
||||||
|
Return: ~
|
||||||
|
(`integer|string`) Message id.
|
||||||
|
• -1 means nvim_echo didn't show a message
|
||||||
|
|
||||||
nvim_eval_statusline({str}, {opts}) *nvim_eval_statusline()*
|
nvim_eval_statusline({str}, {opts}) *nvim_eval_statusline()*
|
||||||
Evaluates statusline string.
|
Evaluates statusline string.
|
||||||
|
@@ -787,6 +787,31 @@ ModeChanged After changing the mode. The pattern is
|
|||||||
:au ModeChanged [vV\x16]*:* let &l:rnu = mode() =~# '^[vV\x16]'
|
:au ModeChanged [vV\x16]*:* let &l:rnu = mode() =~# '^[vV\x16]'
|
||||||
:au ModeChanged *:[vV\x16]* let &l:rnu = mode() =~# '^[vV\x16]'
|
:au ModeChanged *:[vV\x16]* let &l:rnu = mode() =~# '^[vV\x16]'
|
||||||
:au WinEnter,WinLeave * let &l:rnu = mode() =~# '^[vV\x16]'
|
:au WinEnter,WinLeave * let &l:rnu = mode() =~# '^[vV\x16]'
|
||||||
|
Progress *Progress*
|
||||||
|
After a progress message is created or updated via
|
||||||
|
`nvim_echo`. The pattern is matched against
|
||||||
|
title of the message. The |event-data| contains:
|
||||||
|
id: id of the message
|
||||||
|
text: text of the message
|
||||||
|
title: title of the progress message
|
||||||
|
status: status of the progress message
|
||||||
|
percent: how much progress has been
|
||||||
|
made for this progress item
|
||||||
|
Usage example:
|
||||||
|
>
|
||||||
|
vim.api.nvim_create_autocmd('Progress', {
|
||||||
|
pattern={"term"},
|
||||||
|
callback = function(ev)
|
||||||
|
print(string.format('event fired: %s', vim.inspect(ev)))
|
||||||
|
end
|
||||||
|
})
|
||||||
|
local id = vim.api.nvim_echo({{'searching...'}}, true,
|
||||||
|
{kind='progress', status='running', percent=10, title="term"})
|
||||||
|
vim.api.nvim_echo({{'searching'}}, true,
|
||||||
|
{id = id, kind='progress', status='running', percent=50, title="term"})
|
||||||
|
vim.api.nvim_echo({{'done'}}, true,
|
||||||
|
{id = id, kind='progress', status='success', percent=100, title="term"})
|
||||||
|
|
||||||
< *OptionSet*
|
< *OptionSet*
|
||||||
OptionSet After setting an option (except during
|
OptionSet After setting an option (except during
|
||||||
|startup|). The |autocmd-pattern| is matched
|
|startup|). The |autocmd-pattern| is matched
|
||||||
|
@@ -845,4 +845,29 @@ The |g<| command can be used to see the last page of previous command output.
|
|||||||
This is especially useful if you accidentally typed <Space> at the hit-enter
|
This is especially useful if you accidentally typed <Space> at the hit-enter
|
||||||
prompt.
|
prompt.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
4. PROGRESS MESSAGE *progress-message*
|
||||||
|
|
||||||
|
Nvim can emit progress-message, which are a special kind of |ui-messages|
|
||||||
|
used to report the state of long-running tasks.
|
||||||
|
|
||||||
|
Progress messages are created or updated using |nvim_echo()| with `kind='progress'`
|
||||||
|
and the related options. Each message has a unique `msg_id`. A subsequent
|
||||||
|
message with the same `msg_id` replaces the older one.
|
||||||
|
|
||||||
|
Events: ~
|
||||||
|
• msg_show |ui-messages| event is fired for ext-ui upon creation/update of a
|
||||||
|
progress-message
|
||||||
|
• Updating or creating a progress message also triggers the |Progress| autocommand.
|
||||||
|
|
||||||
|
Example: >
|
||||||
|
local id = vim.api.nvim_echo({{'searching...'}}, true,
|
||||||
|
{kind='progress', status='running', percent=10, title="term"})
|
||||||
|
vim.api.nvim_echo({{'searching'}}, true,
|
||||||
|
{id=id, kind='progress', status='running', percent=50, title="term"})
|
||||||
|
vim.api.nvim_echo({{'done'}}, true,
|
||||||
|
{id=id, kind='progress', status='success', percent=100, title="term"})
|
||||||
|
<
|
||||||
|
See also: |nvim_echo()| |ui-messages| |Progress|
|
||||||
|
|
||||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
vim:tw=78:ts=8:noet:ft=help:norl:
|
||||||
|
@@ -139,9 +139,10 @@ API
|
|||||||
actually trusted.
|
actually trusted.
|
||||||
• Added |vim.lsp.is_enabled()| to check if a given LSP config has been enabled
|
• Added |vim.lsp.is_enabled()| to check if a given LSP config has been enabled
|
||||||
by |vim.lsp.enable()|.
|
by |vim.lsp.enable()|.
|
||||||
• |nvim_echo()| can set the |ui-messages| kind with which to emit the message.
|
|
||||||
• |nvim_ui_send()| writes arbitrary data to a UI's stdout. Use this to write
|
• |nvim_ui_send()| writes arbitrary data to a UI's stdout. Use this to write
|
||||||
escape sequences to the terminal when Nvim is running in the |TUI|.
|
escape sequences to the terminal when Nvim is running in the |TUI|.
|
||||||
|
• |nvim_echo()| can set the |ui-messages| kind with which to emit the message.
|
||||||
|
• |nvim_echo()| can create |Progress| messages
|
||||||
|
|
||||||
BUILD
|
BUILD
|
||||||
|
|
||||||
@@ -189,6 +190,8 @@ EVENTS
|
|||||||
|
|
||||||
• |CmdlineLeavePre| triggered before preparing to leave the command line.
|
• |CmdlineLeavePre| triggered before preparing to leave the command line.
|
||||||
• New `append` paremeter for |ui-messages| `msg_show` event.
|
• New `append` paremeter for |ui-messages| `msg_show` event.
|
||||||
|
• New `msg_id` and `progress` paremeter for |ui-messages| `msg_show` event.
|
||||||
|
• Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event.
|
||||||
|
|
||||||
HIGHLIGHTS
|
HIGHLIGHTS
|
||||||
|
|
||||||
|
@@ -2545,6 +2545,7 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
|OptionSet|,
|
|OptionSet|,
|
||||||
|PackChanged|,
|
|PackChanged|,
|
||||||
|PackChangedPre|,
|
|PackChangedPre|,
|
||||||
|
|Progress|,
|
||||||
|QuickFixCmdPost|,
|
|QuickFixCmdPost|,
|
||||||
|QuickFixCmdPre|,
|
|QuickFixCmdPre|,
|
||||||
|QuitPre|,
|
|QuitPre|,
|
||||||
|
15
runtime/lua/vim/_meta/api.lua
generated
15
runtime/lua/vim/_meta/api.lua
generated
@@ -1100,10 +1100,25 @@ function vim.api.nvim_del_var(name) end
|
|||||||
--- the (optional) name or ID `hl_group`.
|
--- the (optional) name or ID `hl_group`.
|
||||||
--- @param history boolean if true, add to `message-history`.
|
--- @param history boolean if true, add to `message-history`.
|
||||||
--- @param opts vim.api.keyset.echo_opts Optional parameters.
|
--- @param opts vim.api.keyset.echo_opts Optional parameters.
|
||||||
|
--- - id: message id for updating existing message.
|
||||||
--- - err: Treat the message like `:echoerr`. Sets `hl_group` to `hl-ErrorMsg` by default.
|
--- - err: Treat the message like `:echoerr`. Sets `hl_group` to `hl-ErrorMsg` by default.
|
||||||
--- - kind: Set the `ui-messages` kind with which this message will be emitted.
|
--- - kind: Set the `ui-messages` kind with which this message will be emitted.
|
||||||
--- - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log`
|
--- - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log`
|
||||||
--- will write the message to the "log" file instead of standard output.
|
--- will write the message to the "log" file instead of standard output.
|
||||||
|
--- - title: The title for `progress-message`.
|
||||||
|
--- - status: Current status of the `progress-message`. Can be
|
||||||
|
--- one of the following values
|
||||||
|
--- - success: The progress item completed successfully
|
||||||
|
--- - running: The progress is ongoing
|
||||||
|
--- - failed: The progress item failed
|
||||||
|
--- - cancel: The progressing process should be canceled.
|
||||||
|
--- note: Cancel needs to be handled by progress
|
||||||
|
--- initiator by listening for the `Progress` event
|
||||||
|
--- - percent: How much progress is done on the progress
|
||||||
|
--- message
|
||||||
|
--- - data: dictionary containing additional information
|
||||||
|
--- @return integer|string # Message id.
|
||||||
|
--- - -1 means nvim_echo didn't show a message
|
||||||
function vim.api.nvim_echo(chunks, history, opts) end
|
function vim.api.nvim_echo(chunks, history, opts) end
|
||||||
|
|
||||||
--- @deprecated
|
--- @deprecated
|
||||||
|
6
runtime/lua/vim/_meta/api_keysets.lua
generated
6
runtime/lua/vim/_meta/api_keysets.lua
generated
@@ -165,6 +165,7 @@ error('Cannot require a meta file')
|
|||||||
--- |'OptionSet'
|
--- |'OptionSet'
|
||||||
--- |'PackChanged'
|
--- |'PackChanged'
|
||||||
--- |'PackChangedPre'
|
--- |'PackChangedPre'
|
||||||
|
--- |'Progress'
|
||||||
--- |'QuickFixCmdPost'
|
--- |'QuickFixCmdPost'
|
||||||
--- |'QuickFixCmdPre'
|
--- |'QuickFixCmdPre'
|
||||||
--- |'QuitPre'
|
--- |'QuitPre'
|
||||||
@@ -233,6 +234,11 @@ error('Cannot require a meta file')
|
|||||||
--- @field err? boolean
|
--- @field err? boolean
|
||||||
--- @field verbose? boolean
|
--- @field verbose? boolean
|
||||||
--- @field kind? string
|
--- @field kind? string
|
||||||
|
--- @field id? integer|string
|
||||||
|
--- @field title? string
|
||||||
|
--- @field status? string
|
||||||
|
--- @field percent? integer
|
||||||
|
--- @field data? table<string,any>
|
||||||
|
|
||||||
--- @class vim.api.keyset.empty
|
--- @class vim.api.keyset.empty
|
||||||
|
|
||||||
|
1
runtime/lua/vim/_meta/options.lua
generated
1
runtime/lua/vim/_meta/options.lua
generated
@@ -2224,6 +2224,7 @@ vim.go.ei = vim.go.eventignore
|
|||||||
--- `OptionSet`,
|
--- `OptionSet`,
|
||||||
--- `PackChanged`,
|
--- `PackChanged`,
|
||||||
--- `PackChangedPre`,
|
--- `PackChangedPre`,
|
||||||
|
--- `Progress`,
|
||||||
--- `QuickFixCmdPost`,
|
--- `QuickFixCmdPost`,
|
||||||
--- `QuickFixCmdPre`,
|
--- `QuickFixCmdPre`,
|
||||||
--- `QuitPre`,
|
--- `QuitPre`,
|
||||||
|
@@ -395,7 +395,8 @@ local function new_progress_report(title)
|
|||||||
local progress = kind == 'end' and 'done' or ('%3d%%'):format(percent)
|
local progress = kind == 'end' and 'done' or ('%3d%%'):format(percent)
|
||||||
local details = (' %s %s'):format(title, fmt:format(...))
|
local details = (' %s %s'):format(title, fmt:format(...))
|
||||||
local chunks = { { 'vim.pack', 'ModeMsg' }, { ': ' }, { progress, 'WarningMsg' }, { details } }
|
local chunks = { { 'vim.pack', 'ModeMsg' }, { ': ' }, { progress, 'WarningMsg' }, { details } }
|
||||||
vim.api.nvim_echo(chunks, true, { kind = 'progress' })
|
-- TODO: need to add support for progress-messages api
|
||||||
|
api.nvim_echo(chunks, true, {})
|
||||||
-- Force redraw to show installation progress during startup
|
-- Force redraw to show installation progress during startup
|
||||||
vim.cmd.redraw({ bang = true })
|
vim.cmd.redraw({ bang = true })
|
||||||
end)
|
end)
|
||||||
|
@@ -336,6 +336,11 @@ typedef struct {
|
|||||||
Boolean err;
|
Boolean err;
|
||||||
Boolean verbose;
|
Boolean verbose;
|
||||||
String kind;
|
String kind;
|
||||||
|
Union(Integer, String) id;
|
||||||
|
String title;
|
||||||
|
String status;
|
||||||
|
Integer percent;
|
||||||
|
DictOf(Object) data;
|
||||||
} Dict(echo_opts);
|
} Dict(echo_opts);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@@ -164,7 +164,8 @@ void wildmenu_select(Integer selected)
|
|||||||
void wildmenu_hide(void)
|
void wildmenu_hide(void)
|
||||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
||||||
|
|
||||||
void msg_show(String kind, Array content, Boolean replace_last, Boolean history, Boolean append)
|
void msg_show(String kind, Array content, Boolean replace_last, Boolean history, Boolean append,
|
||||||
|
Object id, Dict progress)
|
||||||
FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
|
||||||
void msg_clear(void)
|
void msg_clear(void)
|
||||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
|
@@ -758,14 +758,31 @@ void nvim_set_vvar(String name, Object value, Error *err)
|
|||||||
/// the (optional) name or ID `hl_group`.
|
/// the (optional) name or ID `hl_group`.
|
||||||
/// @param history if true, add to |message-history|.
|
/// @param history if true, add to |message-history|.
|
||||||
/// @param opts Optional parameters.
|
/// @param opts Optional parameters.
|
||||||
|
/// - id: message id for updating existing message.
|
||||||
/// - err: Treat the message like `:echoerr`. Sets `hl_group` to |hl-ErrorMsg| by default.
|
/// - err: Treat the message like `:echoerr`. Sets `hl_group` to |hl-ErrorMsg| by default.
|
||||||
/// - kind: Set the |ui-messages| kind with which this message will be emitted.
|
/// - kind: Set the |ui-messages| kind with which this message will be emitted.
|
||||||
/// - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log`
|
/// - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log`
|
||||||
/// will write the message to the "log" file instead of standard output.
|
/// will write the message to the "log" file instead of standard output.
|
||||||
void nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Boolean history, Dict(echo_opts) *opts,
|
/// - title: The title for |progress-message|.
|
||||||
Error *err)
|
/// - status: Current status of the |progress-message|. Can be
|
||||||
|
/// one of the following values
|
||||||
|
/// - success: The progress item completed successfully
|
||||||
|
/// - running: The progress is ongoing
|
||||||
|
/// - failed: The progress item failed
|
||||||
|
/// - cancel: The progressing process should be canceled.
|
||||||
|
/// note: Cancel needs to be handled by progress
|
||||||
|
/// initiator by listening for the `Progress` event
|
||||||
|
/// - percent: How much progress is done on the progress
|
||||||
|
/// message
|
||||||
|
/// - data: dictionary containing additional information
|
||||||
|
/// @return Message id.
|
||||||
|
/// - -1 means nvim_echo didn't show a message
|
||||||
|
Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Boolean history,
|
||||||
|
Dict(echo_opts) *opts,
|
||||||
|
Error *err)
|
||||||
FUNC_API_SINCE(7)
|
FUNC_API_SINCE(7)
|
||||||
{
|
{
|
||||||
|
MsgID id = INTEGER_OBJ(-1);
|
||||||
HlMessage hl_msg = parse_hl_msg(chunks, opts->err, err);
|
HlMessage hl_msg = parse_hl_msg(chunks, opts->err, err);
|
||||||
if (ERROR_SET(err)) {
|
if (ERROR_SET(err)) {
|
||||||
goto error;
|
goto error;
|
||||||
@@ -778,20 +795,52 @@ void nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Boolean history, Dict(
|
|||||||
kind = opts->err ? "echoerr" : history ? "echomsg" : "echo";
|
kind = opts->err ? "echoerr" : history ? "echomsg" : "echo";
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_multihl(hl_msg, kind, history, opts->err);
|
bool is_progress = strequal(kind, "progress");
|
||||||
|
|
||||||
|
VALIDATE(is_progress
|
||||||
|
|| (opts->status.size == 0 && opts->title.size == 0 && opts->percent == 0
|
||||||
|
&& opts->data.size == 0),
|
||||||
|
"%s",
|
||||||
|
"title, status, percent and data fields can only be used with progress messages",
|
||||||
|
{
|
||||||
|
goto error;
|
||||||
|
});
|
||||||
|
|
||||||
|
VALIDATE_EXP((!is_progress || strequal(opts->status.data, "success")
|
||||||
|
|| strequal(opts->status.data, "failed")
|
||||||
|
|| strequal(opts->status.data, "running")
|
||||||
|
|| strequal(opts->status.data, "cancel")),
|
||||||
|
"status", "success|failed|running|cancel", opts->status.data, {
|
||||||
|
goto error;
|
||||||
|
});
|
||||||
|
|
||||||
|
VALIDATE_RANGE(!is_progress || (opts->percent >= 0 && opts->percent <= 100),
|
||||||
|
"percent", {
|
||||||
|
goto error;
|
||||||
|
});
|
||||||
|
|
||||||
|
MessageData msg_data = { .title = opts->title, .status = opts->status,
|
||||||
|
.percent = opts->percent, .data = opts->data };
|
||||||
|
|
||||||
|
id = msg_multihl(opts->id, hl_msg, kind, history, opts->err, &msg_data);
|
||||||
|
|
||||||
if (opts->verbose) {
|
if (opts->verbose) {
|
||||||
verbose_leave();
|
verbose_leave();
|
||||||
verbose_stop(); // flush now
|
verbose_stop(); // flush now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_progress) {
|
||||||
|
do_autocmd_progress(id, hl_msg, &msg_data);
|
||||||
|
}
|
||||||
|
|
||||||
if (history) {
|
if (history) {
|
||||||
// history takes ownership
|
// history takes ownership
|
||||||
return;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
error:
|
||||||
hl_msg_free(hl_msg);
|
hl_msg_free(hl_msg);
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current list of buffers.
|
/// Gets the current list of buffers.
|
||||||
|
@@ -89,6 +89,7 @@ return {
|
|||||||
QuitPre = false, -- before :quit
|
QuitPre = false, -- before :quit
|
||||||
PackChangedPre = false, -- before trying to change state of `vim.pack` plugin
|
PackChangedPre = false, -- before trying to change state of `vim.pack` plugin
|
||||||
PackChanged = false, -- after changing state of `vim.pack` plugin
|
PackChanged = false, -- after changing state of `vim.pack` plugin
|
||||||
|
Progress = false, -- after showing/updating a progress message
|
||||||
RecordingEnter = true, -- when starting to record a macro
|
RecordingEnter = true, -- when starting to record a macro
|
||||||
RecordingLeave = true, -- just before a macro stops recording
|
RecordingLeave = true, -- just before a macro stops recording
|
||||||
RemoteReply = false, -- upon string reception from a remote vim
|
RemoteReply = false, -- upon string reception from a remote vim
|
||||||
@@ -162,6 +163,7 @@ return {
|
|||||||
LspTokenUpdate = true,
|
LspTokenUpdate = true,
|
||||||
PackChangedPre = true,
|
PackChangedPre = true,
|
||||||
PackChanged = true,
|
PackChanged = true,
|
||||||
|
Progress = true,
|
||||||
RecordingEnter = true,
|
RecordingEnter = true,
|
||||||
RecordingLeave = true,
|
RecordingLeave = true,
|
||||||
Signal = true,
|
Signal = true,
|
||||||
|
@@ -966,7 +966,7 @@ static void nlua_print_event(void **argv)
|
|||||||
HlMessage msg = KV_INITIAL_VALUE;
|
HlMessage msg = KV_INITIAL_VALUE;
|
||||||
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
|
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
|
||||||
kv_push(msg, chunk);
|
kv_push(msg, chunk);
|
||||||
msg_multihl(msg, "lua_print", true, false);
|
msg_multihl(INTEGER_OBJ(0), msg, "lua_print", true, false, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print as a Vim message
|
/// Print as a Vim message
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
#include "nvim/ascii_defs.h"
|
#include "nvim/ascii_defs.h"
|
||||||
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/buffer_defs.h"
|
#include "nvim/buffer_defs.h"
|
||||||
#include "nvim/channel.h"
|
#include "nvim/channel.h"
|
||||||
#include "nvim/charset.h"
|
#include "nvim/charset.h"
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
#include "nvim/memory.h"
|
#include "nvim/memory.h"
|
||||||
#include "nvim/memory_defs.h"
|
#include "nvim/memory_defs.h"
|
||||||
#include "nvim/message.h"
|
#include "nvim/message.h"
|
||||||
|
#include "nvim/message_defs.h"
|
||||||
#include "nvim/mouse.h"
|
#include "nvim/mouse.h"
|
||||||
#include "nvim/ops.h"
|
#include "nvim/ops.h"
|
||||||
#include "nvim/option.h"
|
#include "nvim/option.h"
|
||||||
@@ -149,6 +151,8 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
|
|||||||
|
|
||||||
// Extended msg state, currently used for external UIs with ext_messages
|
// Extended msg state, currently used for external UIs with ext_messages
|
||||||
static const char *msg_ext_kind = NULL;
|
static const char *msg_ext_kind = NULL;
|
||||||
|
static MsgID msg_ext_id = { .type = kObjectTypeInteger, .data.integer = 0 };
|
||||||
|
static DictOf(Object) msg_ext_progress = ARRAY_DICT_INIT;
|
||||||
static Array *msg_ext_chunks = NULL;
|
static Array *msg_ext_chunks = NULL;
|
||||||
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
|
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
|
||||||
static sattr_T msg_ext_last_attr = -1;
|
static sattr_T msg_ext_last_attr = -1;
|
||||||
@@ -158,6 +162,8 @@ static bool msg_ext_history = false; ///< message was added to history
|
|||||||
|
|
||||||
static int msg_grid_pos_at_flush = 0;
|
static int msg_grid_pos_at_flush = 0;
|
||||||
|
|
||||||
|
static int64_t msg_id_next = 1; ///< message id to be allocated to next message
|
||||||
|
|
||||||
static void ui_ext_msg_set_pos(int row, bool scrolled)
|
static void ui_ext_msg_set_pos(int row, bool scrolled)
|
||||||
{
|
{
|
||||||
char buf[MAX_SCHAR_SIZE];
|
char buf[MAX_SCHAR_SIZE];
|
||||||
@@ -293,7 +299,8 @@ static bool is_multihl = false;
|
|||||||
/// @param kind Message kind (can be NULL to avoid setting kind)
|
/// @param kind Message kind (can be NULL to avoid setting kind)
|
||||||
/// @param history Whether to add message to history
|
/// @param history Whether to add message to history
|
||||||
/// @param err Whether to print message as an error
|
/// @param err Whether to print message as an error
|
||||||
void msg_multihl(HlMessage hl_msg, const char *kind, bool history, bool err)
|
MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bool err,
|
||||||
|
MessageData *msg_data)
|
||||||
{
|
{
|
||||||
no_wait_return++;
|
no_wait_return++;
|
||||||
msg_start();
|
msg_start();
|
||||||
@@ -305,6 +312,17 @@ void msg_multihl(HlMessage hl_msg, const char *kind, bool history, bool err)
|
|||||||
}
|
}
|
||||||
is_multihl = true;
|
is_multihl = true;
|
||||||
msg_ext_skip_flush = true;
|
msg_ext_skip_flush = true;
|
||||||
|
|
||||||
|
// provide a new id if not given
|
||||||
|
if (id.type == kObjectTypeNil) {
|
||||||
|
id = INTEGER_OBJ(msg_id_next++);
|
||||||
|
} else if (id.type == kObjectTypeInteger) {
|
||||||
|
id = id.data.integer > 0 ? id : INTEGER_OBJ(msg_id_next++);
|
||||||
|
if (msg_id_next < id.data.integer) {
|
||||||
|
msg_id_next = id.data.integer + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
|
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
|
||||||
HlMessageChunk chunk = kv_A(hl_msg, i);
|
HlMessageChunk chunk = kv_A(hl_msg, i);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -315,12 +333,14 @@ void msg_multihl(HlMessage hl_msg, const char *kind, bool history, bool err)
|
|||||||
assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind);
|
assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind);
|
||||||
}
|
}
|
||||||
if (history && kv_size(hl_msg)) {
|
if (history && kv_size(hl_msg)) {
|
||||||
msg_hist_add_multihl(hl_msg, false);
|
msg_hist_add_multihl(id, hl_msg, false, msg_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_ext_skip_flush = false;
|
msg_ext_skip_flush = false;
|
||||||
is_multihl = false;
|
is_multihl = false;
|
||||||
no_wait_return--;
|
no_wait_return--;
|
||||||
msg_end();
|
msg_end();
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @param keep set keep_msg if it doesn't scroll
|
/// @param keep set keep_msg if it doesn't scroll
|
||||||
@@ -1018,12 +1038,35 @@ static void msg_hist_add(const char *s, int len, int hl_id)
|
|||||||
|
|
||||||
HlMessage msg = KV_INITIAL_VALUE;
|
HlMessage msg = KV_INITIAL_VALUE;
|
||||||
kv_push(msg, ((HlMessageChunk){ text, hl_id }));
|
kv_push(msg, ((HlMessageChunk){ text, hl_id }));
|
||||||
msg_hist_add_multihl(msg, false);
|
msg_hist_add_multihl(INTEGER_OBJ(0), msg, false, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool do_clear_hist_temp = true;
|
static bool do_clear_hist_temp = true;
|
||||||
|
|
||||||
static void msg_hist_add_multihl(HlMessage msg, bool temp)
|
void do_autocmd_progress(MsgID msg_id, HlMessage msg, MessageData *msg_data)
|
||||||
|
{
|
||||||
|
MAXSIZE_TEMP_DICT(data, 7);
|
||||||
|
ArrayOf(String) messages = ARRAY_DICT_INIT;
|
||||||
|
for (size_t i = 0; i < msg.size; i++) {
|
||||||
|
ADD(messages, STRING_OBJ(msg.items[i].text));
|
||||||
|
}
|
||||||
|
|
||||||
|
PUT_C(data, "id", OBJECT_OBJ(msg_id));
|
||||||
|
PUT_C(data, "text", ARRAY_OBJ(messages));
|
||||||
|
if (msg_data != NULL) {
|
||||||
|
PUT_C(data, "percent", INTEGER_OBJ(msg_data->percent));
|
||||||
|
PUT_C(data, "status", STRING_OBJ(msg_data->status));
|
||||||
|
PUT_C(data, "title", STRING_OBJ(msg_data->title));
|
||||||
|
PUT_C(data, "data", DICT_OBJ(msg_data->data));
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_autocmds_group(EVENT_PROGRESS, msg_data ? msg_data->title.data : "", NULL, true,
|
||||||
|
AUGROUP_ALL, NULL,
|
||||||
|
NULL, &DICT_OBJ(data));
|
||||||
|
kv_destroy(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msg_hist_add_multihl(MsgID msg_id, HlMessage msg, bool temp, MessageData *msg_data)
|
||||||
{
|
{
|
||||||
if (do_clear_hist_temp) {
|
if (do_clear_hist_temp) {
|
||||||
msg_hist_clear_temp();
|
msg_hist_clear_temp();
|
||||||
@@ -1061,6 +1104,20 @@ static void msg_hist_add_multihl(HlMessage msg, bool temp)
|
|||||||
msg_hist_len += !temp;
|
msg_hist_len += !temp;
|
||||||
msg_hist_last = entry;
|
msg_hist_last = entry;
|
||||||
msg_ext_history = true;
|
msg_ext_history = true;
|
||||||
|
|
||||||
|
msg_ext_id = msg_id;
|
||||||
|
if (strequal(msg_ext_kind, "progress") && msg_data != NULL && ui_has(kUIMessages)) {
|
||||||
|
kv_resize(msg_ext_progress, 3);
|
||||||
|
if (msg_data->title.size != 0) {
|
||||||
|
PUT_C(msg_ext_progress, "title", STRING_OBJ(msg_data->title));
|
||||||
|
}
|
||||||
|
if (msg_data->status.size != 0) {
|
||||||
|
PUT_C(msg_ext_progress, "status", STRING_OBJ(msg_data->status));
|
||||||
|
}
|
||||||
|
if (msg_data->percent >= 0) {
|
||||||
|
PUT_C(msg_ext_progress, "percent", INTEGER_OBJ(msg_data->percent));
|
||||||
|
}
|
||||||
|
}
|
||||||
msg_hist_clear(msg_hist_max);
|
msg_hist_clear(msg_hist_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1205,7 +1262,7 @@ void ex_messages(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
if (redirecting() || !ui_has(kUIMessages)) {
|
if (redirecting() || !ui_has(kUIMessages)) {
|
||||||
msg_silent += ui_has(kUIMessages);
|
msg_silent += ui_has(kUIMessages);
|
||||||
msg_multihl(p->msg, p->kind, false, false);
|
msg_multihl(INTEGER_OBJ(0), p->msg, p->kind, false, false, NULL);
|
||||||
msg_silent -= ui_has(kUIMessages);
|
msg_silent -= ui_has(kUIMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2152,7 +2209,9 @@ 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.
|
// Don't print anything when using ":silent cmd" or empty message.
|
||||||
if (msg_silent != 0 || *str == NUL) {
|
if (msg_silent != 0 || *str == NUL) {
|
||||||
if (*str == NUL && ui_has(kUIMessages)) {
|
if (*str == NUL && ui_has(kUIMessages)) {
|
||||||
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false);
|
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false,
|
||||||
|
INTEGER_OBJ(-1),
|
||||||
|
(Dict)ARRAY_DICT_INIT);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3178,8 +3237,10 @@ void msg_ext_ui_flush(void)
|
|||||||
msg_ext_emit_chunk();
|
msg_ext_emit_chunk();
|
||||||
if (msg_ext_chunks->size > 0) {
|
if (msg_ext_chunks->size > 0) {
|
||||||
Array *tofree = msg_ext_init_chunks();
|
Array *tofree = msg_ext_init_chunks();
|
||||||
|
|
||||||
ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history,
|
ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history,
|
||||||
msg_ext_append);
|
msg_ext_append, msg_ext_id, msg_ext_progress);
|
||||||
|
// clear info after emiting message.
|
||||||
if (msg_ext_history) {
|
if (msg_ext_history) {
|
||||||
api_free_array(*tofree);
|
api_free_array(*tofree);
|
||||||
} else {
|
} else {
|
||||||
@@ -3191,13 +3252,15 @@ void msg_ext_ui_flush(void)
|
|||||||
xfree(chunk);
|
xfree(chunk);
|
||||||
}
|
}
|
||||||
xfree(tofree->items);
|
xfree(tofree->items);
|
||||||
msg_hist_add_multihl(msg, true);
|
msg_hist_add_multihl(INTEGER_OBJ(0), msg, true, NULL);
|
||||||
}
|
}
|
||||||
xfree(tofree);
|
xfree(tofree);
|
||||||
msg_ext_overwrite = false;
|
msg_ext_overwrite = false;
|
||||||
msg_ext_history = false;
|
msg_ext_history = false;
|
||||||
msg_ext_append = false;
|
msg_ext_append = false;
|
||||||
msg_ext_kind = NULL;
|
msg_ext_kind = NULL;
|
||||||
|
msg_ext_id = INTEGER_OBJ(0);
|
||||||
|
kv_destroy(msg_ext_progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,7 +10,14 @@ typedef struct {
|
|||||||
} HlMessageChunk;
|
} HlMessageChunk;
|
||||||
|
|
||||||
typedef kvec_t(HlMessageChunk) HlMessage;
|
typedef kvec_t(HlMessageChunk) HlMessage;
|
||||||
|
#define MsgID Union(Integer, String)
|
||||||
|
|
||||||
|
typedef struct msg_data {
|
||||||
|
Integer percent; ///< Progress percentage
|
||||||
|
String title; ///< Title for progress message
|
||||||
|
String status; ///< Status for progress message
|
||||||
|
DictOf(String, Object) data; ///< Extra info for 'echo' messages
|
||||||
|
} MessageData;
|
||||||
/// Message history for `:messages`
|
/// Message history for `:messages`
|
||||||
typedef struct msg_hist {
|
typedef struct msg_hist {
|
||||||
struct msg_hist *next; ///< Next message.
|
struct msg_hist *next; ///< Next message.
|
||||||
|
@@ -3138,3 +3138,385 @@ it('pager works in headless mode with UI attached', function()
|
|||||||
-- More --^ |
|
-- More --^ |
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('progress-message', function()
|
||||||
|
local screen
|
||||||
|
|
||||||
|
local function setup_autocmd(pattern)
|
||||||
|
exec_lua(function()
|
||||||
|
local grp = vim.api.nvim_create_augroup('ProgressListener', { clear = true })
|
||||||
|
vim.api.nvim_create_autocmd('Progress', {
|
||||||
|
pattern = pattern,
|
||||||
|
group = grp,
|
||||||
|
callback = function(ev)
|
||||||
|
_G.progress_autocmd_result = ev.data
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function assert_progress_autocmd(expected, context)
|
||||||
|
local progress_autocmd_result = exec_lua(function()
|
||||||
|
return _G.progress_autocmd_result
|
||||||
|
end)
|
||||||
|
eq(expected, progress_autocmd_result, context)
|
||||||
|
exec_lua(function()
|
||||||
|
_G.progress_autocmd_result = nil
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_screen(with_ext_msg)
|
||||||
|
if with_ext_msg then
|
||||||
|
screen = Screen.new(25, 5, { ext_messages = true })
|
||||||
|
screen:add_extra_attr_ids {
|
||||||
|
[100] = { undercurl = true, special = Screen.colors.Red },
|
||||||
|
[101] = { foreground = Screen.colors.Magenta1, bold = true },
|
||||||
|
}
|
||||||
|
else
|
||||||
|
screen = Screen.new(40, 5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
setup_screen(true)
|
||||||
|
setup_autocmd()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can be sent by nvim_echo', function()
|
||||||
|
local id = api.nvim_echo(
|
||||||
|
{ { 'test-message' } },
|
||||||
|
true,
|
||||||
|
{ kind = 'progress', title = 'testsuit', percent = 10, status = 'running' }
|
||||||
|
)
|
||||||
|
|
||||||
|
screen:expect({
|
||||||
|
grid = [[
|
||||||
|
^ |
|
||||||
|
{1:~ }|*4
|
||||||
|
]],
|
||||||
|
messages = {
|
||||||
|
{
|
||||||
|
content = { { 'test-message' } },
|
||||||
|
progress = {
|
||||||
|
percent = 10,
|
||||||
|
status = 'running',
|
||||||
|
title = 'testsuit',
|
||||||
|
},
|
||||||
|
history = true,
|
||||||
|
id = 1,
|
||||||
|
kind = 'progress',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert_progress_autocmd({
|
||||||
|
text = { 'test-message' },
|
||||||
|
percent = 10,
|
||||||
|
status = 'running',
|
||||||
|
title = 'testsuit',
|
||||||
|
id = 1,
|
||||||
|
data = {},
|
||||||
|
}, 'progress autocmd receives progress messages')
|
||||||
|
|
||||||
|
-- can update progress messages
|
||||||
|
api.nvim_echo(
|
||||||
|
{ { 'test-message-updated' } },
|
||||||
|
true,
|
||||||
|
{ id = id, kind = 'progress', title = 'TestSuit', percent = 50, status = 'running' }
|
||||||
|
)
|
||||||
|
screen:expect({
|
||||||
|
grid = [[
|
||||||
|
^ |
|
||||||
|
{1:~ }|*4
|
||||||
|
]],
|
||||||
|
messages = {
|
||||||
|
{
|
||||||
|
content = { { 'test-message-updated' } },
|
||||||
|
progress = {
|
||||||
|
percent = 50,
|
||||||
|
status = 'running',
|
||||||
|
title = 'TestSuit',
|
||||||
|
},
|
||||||
|
history = true,
|
||||||
|
id = 1,
|
||||||
|
kind = 'progress',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert_progress_autocmd({
|
||||||
|
text = { 'test-message-updated' },
|
||||||
|
percent = 50,
|
||||||
|
status = 'running',
|
||||||
|
title = 'TestSuit',
|
||||||
|
id = 1,
|
||||||
|
data = {},
|
||||||
|
}, 'Progress autocmd receives progress update')
|
||||||
|
|
||||||
|
-- progress event can filter by title
|
||||||
|
setup_autocmd('Special Title')
|
||||||
|
api.nvim_echo(
|
||||||
|
{ { 'test-message-updated' } },
|
||||||
|
true,
|
||||||
|
{ id = id, kind = 'progress', percent = 80, status = 'running' }
|
||||||
|
)
|
||||||
|
assert_progress_autocmd(nil, 'No progress message with Special Title yet')
|
||||||
|
|
||||||
|
api.nvim_echo(
|
||||||
|
{ { 'test-message-updated' } },
|
||||||
|
true,
|
||||||
|
{ id = id, kind = 'progress', title = 'Special Title', percent = 100, status = 'success' }
|
||||||
|
)
|
||||||
|
assert_progress_autocmd({
|
||||||
|
text = { 'test-message-updated' },
|
||||||
|
percent = 100,
|
||||||
|
status = 'success',
|
||||||
|
title = 'Special Title',
|
||||||
|
id = 1,
|
||||||
|
data = {},
|
||||||
|
}, 'Progress autocmd receives progress update')
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('user-defined data in `data` field', function()
|
||||||
|
api.nvim_echo({ { 'test-message' } }, true, {
|
||||||
|
kind = 'progress',
|
||||||
|
title = 'TestSuit',
|
||||||
|
percent = 10,
|
||||||
|
status = 'running',
|
||||||
|
data = { test_attribute = 1 },
|
||||||
|
})
|
||||||
|
|
||||||
|
screen:expect({
|
||||||
|
grid = [[
|
||||||
|
^ |
|
||||||
|
{1:~ }|*4
|
||||||
|
]],
|
||||||
|
messages = {
|
||||||
|
{
|
||||||
|
content = { { 'test-message' } },
|
||||||
|
history = true,
|
||||||
|
id = 1,
|
||||||
|
kind = 'progress',
|
||||||
|
progress = {
|
||||||
|
percent = 10,
|
||||||
|
status = 'running',
|
||||||
|
title = 'TestSuit',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert_progress_autocmd({
|
||||||
|
text = { 'test-message' },
|
||||||
|
percent = 10,
|
||||||
|
status = 'running',
|
||||||
|
title = 'TestSuit',
|
||||||
|
id = 1,
|
||||||
|
data = { test_attribute = 1 },
|
||||||
|
}, 'Progress autocmd receives progress messages')
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('validates', function()
|
||||||
|
-- throws error if title, status, percent, data is used in non progress message
|
||||||
|
eq(
|
||||||
|
'title, status, percent and data fields can only be used with progress messages',
|
||||||
|
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { title = 'TestSuit' })
|
||||||
|
)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
'title, status, percent and data fields can only be used with progress messages',
|
||||||
|
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { status = 'running' })
|
||||||
|
)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
'title, status, percent and data fields can only be used with progress messages',
|
||||||
|
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { percent = 10 })
|
||||||
|
)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
'title, status, percent and data fields can only be used with progress messages',
|
||||||
|
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { data = { tag = 'test' } })
|
||||||
|
)
|
||||||
|
|
||||||
|
-- throws error if anything other then running/success/failed/cancel is used in status
|
||||||
|
eq(
|
||||||
|
"Invalid 'status': expected success|failed|running|cancel, got live",
|
||||||
|
t.pcall_err(
|
||||||
|
api.nvim_echo,
|
||||||
|
{ { 'test-message' } },
|
||||||
|
false,
|
||||||
|
{ kind = 'progress', status = 'live' }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- throws error if parcent is not in 0-100
|
||||||
|
eq(
|
||||||
|
"Invalid 'percent': out of range",
|
||||||
|
t.pcall_err(
|
||||||
|
api.nvim_echo,
|
||||||
|
{ { 'test-message' } },
|
||||||
|
false,
|
||||||
|
{ kind = 'progress', status = 'running', percent = -1 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
"Invalid 'percent': out of range",
|
||||||
|
t.pcall_err(
|
||||||
|
api.nvim_echo,
|
||||||
|
{ { 'test-message' } },
|
||||||
|
false,
|
||||||
|
{ kind = 'progress', status = 'running', percent = 101 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- throws error if data is not a dictionary
|
||||||
|
eq(
|
||||||
|
"Invalid 'data': expected Dict, got String",
|
||||||
|
t.pcall_err(
|
||||||
|
api.nvim_echo,
|
||||||
|
{ { 'test-message' } },
|
||||||
|
false,
|
||||||
|
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running', data = 'test' }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('gets placed in history', function()
|
||||||
|
local id = api.nvim_echo(
|
||||||
|
{ { 'test-message 10' } },
|
||||||
|
true,
|
||||||
|
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' }
|
||||||
|
)
|
||||||
|
eq('test-message 10', exec_capture('messages'))
|
||||||
|
|
||||||
|
api.nvim_echo(
|
||||||
|
{ { 'test-message 20' } },
|
||||||
|
true,
|
||||||
|
{ id = id, kind = 'progress', title = 'TestSuit', percent = 20, status = 'running' }
|
||||||
|
)
|
||||||
|
eq('test-message 10\ntest-message 20', exec_capture('messages'))
|
||||||
|
|
||||||
|
api.nvim_echo({ { 'middle msg' } }, true, {})
|
||||||
|
eq('test-message 10\ntest-message 20\nmiddle msg', exec_capture('messages'))
|
||||||
|
api.nvim_echo(
|
||||||
|
{ { 'test-message 30' } },
|
||||||
|
true,
|
||||||
|
{ id = id, kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' }
|
||||||
|
)
|
||||||
|
eq('test-message 10\ntest-message 20\nmiddle msg\ntest-message 30', exec_capture('messages'))
|
||||||
|
|
||||||
|
api.nvim_echo(
|
||||||
|
{ { 'test-message 50' } },
|
||||||
|
true,
|
||||||
|
{ id = id, kind = 'progress', title = 'TestSuit', percent = 50, status = 'running' }
|
||||||
|
)
|
||||||
|
eq(
|
||||||
|
'test-message 10\ntest-message 20\nmiddle msg\ntest-message 30\ntest-message 50',
|
||||||
|
exec_capture('messages')
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('sets msg-id correctly', function()
|
||||||
|
local id1 = api.nvim_echo(
|
||||||
|
{ { 'test-message 10' } },
|
||||||
|
true,
|
||||||
|
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' }
|
||||||
|
)
|
||||||
|
eq(1, id1)
|
||||||
|
|
||||||
|
local id2 = api.nvim_echo(
|
||||||
|
{ { 'test-message 20' } },
|
||||||
|
true,
|
||||||
|
{ kind = 'progress', title = 'TestSuit', percent = 20, status = 'running' }
|
||||||
|
)
|
||||||
|
eq(2, id2)
|
||||||
|
|
||||||
|
local id3 = api.nvim_echo({ { 'normal message' } }, true, {})
|
||||||
|
eq(3, id3)
|
||||||
|
|
||||||
|
local id4 = api.nvim_echo({ { 'without history' } }, false, {})
|
||||||
|
eq(4, id4)
|
||||||
|
|
||||||
|
local id5 = api.nvim_echo(
|
||||||
|
{ { 'test-message 30' } },
|
||||||
|
true,
|
||||||
|
{ id = 10, kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' }
|
||||||
|
)
|
||||||
|
eq(10, id5)
|
||||||
|
|
||||||
|
-- updating progress message does not create new msg-id
|
||||||
|
local id5_update = api.nvim_echo(
|
||||||
|
{ { 'test-message 40' } },
|
||||||
|
true,
|
||||||
|
{ id = id5, kind = 'progress', title = 'TestSuit', percent = 40, status = 'running' }
|
||||||
|
)
|
||||||
|
eq(id5, id5_update)
|
||||||
|
|
||||||
|
local id6 = api.nvim_echo(
|
||||||
|
{ { 'test-message 30' } },
|
||||||
|
true,
|
||||||
|
{ kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' }
|
||||||
|
)
|
||||||
|
eq(11, id6)
|
||||||
|
|
||||||
|
local id7 = api.nvim_echo(
|
||||||
|
{ { 'supports str-id' } },
|
||||||
|
true,
|
||||||
|
{ id = 'str-id', kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' }
|
||||||
|
)
|
||||||
|
eq('str-id', id7)
|
||||||
|
|
||||||
|
local id8 = api.nvim_echo(
|
||||||
|
{ { 'test-message 30' } },
|
||||||
|
true,
|
||||||
|
{ kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' }
|
||||||
|
)
|
||||||
|
eq(12, id8)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('supports string ids', function()
|
||||||
|
-- string id works
|
||||||
|
local id = api.nvim_echo(
|
||||||
|
{ { 'supports str-id' } },
|
||||||
|
true,
|
||||||
|
{ id = 'str-id', kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' }
|
||||||
|
)
|
||||||
|
eq('str-id', id)
|
||||||
|
|
||||||
|
screen:expect({
|
||||||
|
grid = [[
|
||||||
|
^ |
|
||||||
|
{1:~ }|*4
|
||||||
|
]],
|
||||||
|
messages = {
|
||||||
|
{
|
||||||
|
content = { { 'supports str-id' } },
|
||||||
|
history = true,
|
||||||
|
id = 'str-id',
|
||||||
|
kind = 'progress',
|
||||||
|
progress = {
|
||||||
|
percent = 30,
|
||||||
|
status = 'running',
|
||||||
|
title = 'TestSuit',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local id_update = api.nvim_echo(
|
||||||
|
{ { 'supports str-id updated' } },
|
||||||
|
true,
|
||||||
|
{ id = id, kind = 'progress', title = 'testsuit', percent = 40, status = 'running' }
|
||||||
|
)
|
||||||
|
eq(id, id_update)
|
||||||
|
assert_progress_autocmd({
|
||||||
|
text = { 'supports str-id updated' },
|
||||||
|
percent = 40,
|
||||||
|
status = 'running',
|
||||||
|
title = 'testsuit',
|
||||||
|
id = 'str-id',
|
||||||
|
data = {},
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
@@ -1397,12 +1397,19 @@ function Screen:_handle_wildmenu_hide()
|
|||||||
self.wildmenu_items, self.wildmenu_pos = nil, nil
|
self.wildmenu_items, self.wildmenu_pos = nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function Screen:_handle_msg_show(kind, chunks, replace_last, history, append)
|
function Screen:_handle_msg_show(kind, chunks, replace_last, history, append, id, progress)
|
||||||
local pos = #self.messages
|
local pos = #self.messages
|
||||||
if not replace_last or pos == 0 then
|
if not replace_last or pos == 0 then
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
end
|
end
|
||||||
self.messages[pos] = { kind = kind, content = chunks, history = history, append = append }
|
self.messages[pos] = {
|
||||||
|
kind = kind,
|
||||||
|
content = chunks,
|
||||||
|
history = history,
|
||||||
|
append = append,
|
||||||
|
id = id,
|
||||||
|
progress = progress,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function Screen:_handle_msg_clear()
|
function Screen:_handle_msg_clear()
|
||||||
@@ -1533,6 +1540,8 @@ function Screen:_extstate_repr(attr_state)
|
|||||||
content = self:_chunks_repr(entry.content, attr_state),
|
content = self:_chunks_repr(entry.content, attr_state),
|
||||||
history = entry.history or nil,
|
history = entry.history or nil,
|
||||||
append = entry.append 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,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user