From 4b643d7068e871e4bee8ab74f3f0a054c242fa33 Mon Sep 17 00:00:00 2001 From: Shadman Date: Fri, 27 Mar 2026 16:24:14 +0600 Subject: [PATCH] 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. --- runtime/doc/api.txt | 1 + runtime/doc/autocmd.txt | 11 ++++--- runtime/lua/vim/_meta/api.lua | 1 + runtime/lua/vim/_meta/api_keysets.lua | 1 + src/nvim/api/keysets_defs.h | 1 + src/nvim/api/vim.c | 8 +++-- src/nvim/autocmd.c | 1 + src/nvim/message.c | 7 ++-- src/nvim/message_defs.h | 1 + test/functional/ui/messages_spec.lua | 46 +++++++++++++++++++-------- 10 files changed, 55 insertions(+), 23 deletions(-) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 0d4db20af4..5fb51ed7fa 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -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. diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index f5c6ccd83f..9b8cc654ac 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -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 diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 380058a287..65d05dac1e 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -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. diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 7f4feba87d..273d25a285 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -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 --- @class vim.api.keyset.empty diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index c54c8f712d..96ba98230c 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -358,6 +358,7 @@ typedef struct { String title; String status; Integer percent; + String source; DictOf(Object) data; } Dict(echo_opts); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 2e9bc89204..facf7cecc7 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -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); diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index bb3b8c6c5a..074ae9f372 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -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 diff --git a/src/nvim/message.c b/src/nvim/message.c index dd95a2764c..f48bad2425 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -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); } diff --git a/src/nvim/message_defs.h b/src/nvim/message_defs.h index 6229c7f1af..90b6756026 100644 --- a/src/nvim/message_defs.h +++ b/src/nvim/message_defs.h @@ -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 diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 374cd20e25..089af5756e 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -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,