feat(progress): set Progress-event pattern to "source" #38495

Problem:
Currently, there's no way to distinguish progress messages coming from
different sources. Nor can Progress event be easily filtered based on
source.

Solution:
- Add "source" field to nvim_echo-opts.
- The Progress event pattern is now defined by the "source" field.
- Include the "title" as ev.data.
- Unrelated change: set force=false to disable nesting.
This commit is contained in:
Shadman
2026-03-27 16:24:14 +06:00
committed by GitHub
parent 925e9e8722
commit 4b643d7068
10 changed files with 55 additions and 23 deletions

View File

@@ -696,6 +696,7 @@ nvim_echo({chunks}, {history}, {opts}) *nvim_echo()*
emitted message. Set "progress" to emit a
|progress-message|.
• percent (`integer?`) |progress-message| percentage.
• source (`string?`) |progress-message| source.
• status (`string?`) |progress-message| status:
• "success": Process completed successfully.
• "running": Process is ongoing.

View File

@@ -820,13 +820,16 @@ ModeChanged After changing the mode. The pattern is
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:
`source` of the message. The |event-data| contains:
id: id of the message
text: text of the message
title: title of the progress message
source: source of the progress message
status: status of the progress message
percent: how much progress has been
made for this progress item
data: arbitaray data sent with
progress message
Usage example:
>lua
vim.api.nvim_create_autocmd('Progress', {
@@ -835,9 +838,9 @@ Progress *Progress*
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'})
local id = vim.api.nvim_echo({{'searching...'}}, true, {kind='progress', status='running', percent=10, title='term', source='search'})
vim.api.nvim_echo({{'searching'}}, true, {id = id, kind='progress', status='running', percent=50, title='term', source='search'})
vim.api.nvim_echo({{'done'}}, true, {id = id, kind='progress', status='success', percent=100, title='term', source='search'})
< *OptionSet*
OptionSet After setting an option (except during

View File

@@ -1139,6 +1139,7 @@ function vim.api.nvim_del_var(name) end
--- - kind (`string?`) Decides the `ui-messages` kind in the emitted message. Set "progress"
--- to emit a `progress-message`.
--- - percent (`integer?`) `progress-message` percentage.
--- - source (`string?`) `progress-message` source.
--- - status (`string?`) `progress-message` status:
--- - "success": Process completed successfully.
--- - "running": Process is ongoing.

View File

@@ -242,6 +242,7 @@ error('Cannot require a meta file')
--- @field title? string
--- @field status? string
--- @field percent? integer
--- @field source? string
--- @field data? table<string,any>
--- @class vim.api.keyset.empty

View File

@@ -358,6 +358,7 @@ typedef struct {
String title;
String status;
Integer percent;
String source;
DictOf(Object) data;
} Dict(echo_opts);

View File

@@ -818,6 +818,7 @@ void nvim_set_vvar(String name, Object value, Error *err)
/// - kind (`string?`) Decides the |ui-messages| kind in the emitted message. Set "progress"
/// to emit a |progress-message|.
/// - percent (`integer?`) |progress-message| percentage.
/// - source (`string?`) |progress-message| source.
/// - status (`string?`) |progress-message| status:
/// - "success": Process completed successfully.
/// - "running": Process is ongoing.
@@ -850,8 +851,8 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
VALIDATE(is_progress
|| (opts->status.size == 0 && opts->title.size == 0 && opts->percent == 0
&& opts->data.size == 0),
"Conflict: title/status/percent/data not allowed with kind='%s'", kind,
&& opts->data.size == 0 && opts->source.size == 0),
"Conflict: title/source/status/percent/data not allowed with kind='%s'", kind,
{
goto error;
});
@@ -876,7 +877,8 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
});
MessageData msg_data = { .title = opts->title, .status = opts->status,
.percent = opts->percent, .data = opts->data };
.percent = opts->percent, .data = opts->data,
.source = opts->source };
id = msg_multihl(opts->id, hl_msg, kind, history, opts->err, &msg_data, &needs_clear);

View File

@@ -1806,6 +1806,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
|| event == EVENT_MENUPOPUP
|| event == EVENT_MODECHANGED
|| event == EVENT_OPTIONSET
|| event == EVENT_PROGRESS
|| event == EVENT_QUICKFIXCMDPOST
|| event == EVENT_QUICKFIXCMDPRE
|| event == EVENT_REMOTEREPLY

View File

@@ -1135,7 +1135,7 @@ void do_autocmd_progress(MsgID msg_id, HlMessage msg, MessageData *msg_data)
return;
}
MAXSIZE_TEMP_DICT(data, 6);
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));
@@ -1145,12 +1145,15 @@ void do_autocmd_progress(MsgID msg_id, HlMessage msg, MessageData *msg_data)
PUT_C(data, "text", ARRAY_OBJ(messages));
if (msg_data != NULL) {
PUT_C(data, "percent", INTEGER_OBJ(msg_data->percent));
PUT_C(data, "source", STRING_OBJ(msg_data->source));
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,
apply_autocmds_group(EVENT_PROGRESS,
(msg_data && msg_data->source.size > 0) ? msg_data->source.data : "", NULL,
false,
AUGROUP_ALL, NULL, NULL, &DICT_OBJ(data));
kv_destroy(messages);
}

View File

@@ -13,6 +13,7 @@ typedef kvec_t(HlMessageChunk) HlMessage;
#define MsgID Union(Integer, String)
typedef struct msg_data {
String source; ///< Source of progress message
Integer percent; ///< Progress percentage
String title; ///< Title for progress message
String status; ///< Status for progress message

View File

@@ -3345,7 +3345,7 @@ describe('progress-message', function()
local id = api.nvim_echo(
{ { 'test-message' } },
true,
{ kind = 'progress', title = 'testsuit', percent = 10, status = 'running' }
{ kind = 'progress', title = 'testsuit', percent = 10, status = 'running', source = 'tests' }
)
screen:expect({
@@ -3370,6 +3370,7 @@ describe('progress-message', function()
assert_progress_autocmd({
text = { 'test-message' },
percent = 10,
source = 'tests',
status = 'running',
title = 'testsuit',
id = 1,
@@ -3405,6 +3406,7 @@ describe('progress-message', function()
assert_progress_autocmd({
text = { 'test-message-updated' },
percent = 50,
source = '',
status = 'running',
title = 'TestSuit',
id = 1,
@@ -3510,25 +3512,29 @@ describe('progress-message', function()
},
})
-- progress event can filter by title
setup_autocmd('Special Title')
-- progress event can filter by source
setup_autocmd('Tests')
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')
assert_progress_autocmd(nil, 'No progress message with Tests source yet')
api.nvim_echo(
{ { 'test-message-updated' } },
true,
{ id = id, kind = 'progress', title = 'Special Title', percent = 100, status = 'success' }
)
api.nvim_echo({ { 'test-message-updated' } }, true, {
id = id,
kind = 'progress',
title = 'Title',
percent = 100,
status = 'success',
source = 'Tests',
})
assert_progress_autocmd({
text = { 'test-message-updated' },
percent = 100,
source = 'Tests',
status = 'success',
title = 'Special Title',
title = 'Title',
id = 1,
data = {},
}, 'Progress autocmd receives progress update')
@@ -3539,6 +3545,7 @@ describe('progress-message', function()
kind = 'progress',
title = 'TestSuit',
percent = 10,
source = '',
status = 'running',
data = { test_attribute = 1 },
})
@@ -3565,6 +3572,7 @@ describe('progress-message', function()
assert_progress_autocmd({
text = { 'test-message' },
percent = 10,
source = '',
status = 'running',
title = 'TestSuit',
id = 1,
@@ -3575,25 +3583,30 @@ describe('progress-message', function()
it('validation', function()
-- throws error if title, status, percent, data is used in non progress message
eq(
"Conflict: title/status/percent/data not allowed with kind='echo'",
"Conflict: title/source/status/percent/data not allowed with kind='echo'",
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { title = 'TestSuit' })
)
eq(
"Conflict: title/status/percent/data not allowed with kind='echo'",
"Conflict: title/source/status/percent/data not allowed with kind='echo'",
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { status = 'running' })
)
eq(
"Conflict: title/status/percent/data not allowed with kind='echo'",
"Conflict: title/source/status/percent/data not allowed with kind='echo'",
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { percent = 10 })
)
eq(
"Conflict: title/status/percent/data not allowed with kind='echo'",
"Conflict: title/source/status/percent/data not allowed with kind='echo'",
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { data = { tag = 'test' } })
)
eq(
"Conflict: title/source/status/percent/data not allowed with kind='echo'",
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { source = 'tests' })
)
-- throws error if anything other then running/success/failed/cancel is used in status
eq(
"Invalid 'status': expected success|failed|running|cancel, got live",
@@ -3777,6 +3790,7 @@ describe('progress-message', function()
assert_progress_autocmd({
text = { 'supports str-id updated' },
percent = 40,
source = '',
status = 'running',
title = 'testsuit',
id = 'str-id',
@@ -3804,6 +3818,7 @@ describe('progress-message', function()
kind = 'progress',
title = 'TestSuit',
percent = 10,
source = '',
status = 'running',
})
@@ -3829,6 +3844,7 @@ describe('progress-message', function()
assert_progress_autocmd({
text = { 'test-message' },
percent = 10,
source = '',
status = 'running',
title = 'TestSuit',
id = 1,
@@ -3851,6 +3867,7 @@ describe('progress-message', function()
assert_progress_autocmd({
text = { 'test-message: not shown in cmdline' },
percent = 10,
source = '',
status = 'running',
title = 'TestSuite',
id = 1,
@@ -3886,6 +3903,7 @@ describe('progress-message', function()
assert_progress_autocmd({
text = { 'test-message: shown in cmdline' },
percent = 10,
source = '',
status = 'running',
title = 'TestSuite',
id = 2,