fix(progress): require "source" for progress-message #38514

Problem:
- Progress-events are filtered by "source". But "source" is not required by nvim_echo.
- Without "++nested" (force=false), nvim_echo in an event-handler does not trigger Progress events.
- vim.health does not declare a "source".

Solution:
- Make source mandatory for progress-messages
- Enable ++nested (force=true) by default when firing Progress event.
- Set "source" in vim.health module.
This commit is contained in:
Shadman
2026-03-28 19:22:22 +06:00
committed by GitHub
parent 5a7df03b42
commit 7bf83cc2a6
6 changed files with 128 additions and 80 deletions

View File

@@ -717,8 +717,9 @@ LspProgress *LspProgress*
vim.api.nvim_create_autocmd('LspProgress', { buffer = buf, callback = function(ev) vim.api.nvim_create_autocmd('LspProgress', { buffer = buf, callback = function(ev)
local value = ev.data.params.value local value = ev.data.params.value
vim.api.nvim_echo({ { value.message or 'done' } }, false, { vim.api.nvim_echo({ { value.message or 'done' } }, false, {
id = 'lsp', id = ev.data.id,
kind = 'progress', kind = 'progress',
source = 'vim.lsp',
title = value.title, title = value.title,
status = value.kind ~= 'end' and 'running' or 'success', status = value.kind ~= 'end' and 'running' or 'success',
percent = value.percentage, percent = value.percentage,

View File

@@ -375,7 +375,7 @@ end
---@param len integer ---@param len integer
---@return fun(status: 'success'|'running', idx: integer, fmt: string, ...: any): nil ---@return fun(status: 'success'|'running', idx: integer, fmt: string, ...: any): nil
local function progress_report(len) local function progress_report(len)
local progress = { kind = 'progress', title = 'checkhealth' } local progress = { kind = 'progress', source = 'vim.health', title = 'checkhealth' }
return function(status, idx, fmt, ...) return function(status, idx, fmt, ...)
progress.status = status progress.status = status

View File

@@ -870,6 +870,10 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
goto error; goto error;
}); });
VALIDATE_R((!is_progress || opts->source.size != 0), "opts.source", {
goto error;
});
// Message-id may be user-defined only if String, not Integer. // Message-id may be user-defined only if String, not Integer.
VALIDATE(opts->id.type != kObjectTypeInteger || msg_id_exists(opts->id.data.integer), VALIDATE(opts->id.type != kObjectTypeInteger || msg_id_exists(opts->id.data.integer),
"Invalid 'id': %" PRId64, opts->id.data.integer, { "Invalid 'id': %" PRId64, opts->id.data.integer, {

View File

@@ -1153,7 +1153,7 @@ void do_autocmd_progress(MsgID msg_id, HlMessage msg, MessageData *msg_data)
apply_autocmds_group(EVENT_PROGRESS, apply_autocmds_group(EVENT_PROGRESS,
(msg_data && msg_data->source.size > 0) ? msg_data->source.data : "", NULL, (msg_data && msg_data->source.size > 0) ? msg_data->source.data : "", NULL,
false, true,
AUGROUP_ALL, NULL, NULL, &DICT_OBJ(data)); AUGROUP_ALL, NULL, NULL, &DICT_OBJ(data));
kv_destroy(messages); kv_destroy(messages);
} }

View File

@@ -3378,11 +3378,14 @@ describe('progress-message', function()
}, 'progress autocmd receives progress messages') }, 'progress autocmd receives progress messages')
-- can update progress messages -- can update progress messages
api.nvim_echo( api.nvim_echo({ { 'test-message-updated' } }, true, {
{ { 'test-message-updated' } }, id = id,
true, kind = 'progress',
{ id = id, kind = 'progress', title = 'TestSuit', percent = 50, status = 'running' } source = 'tests',
) title = 'TestSuit',
percent = 50,
status = 'running',
})
screen:expect({ screen:expect({
grid = [[ grid = [[
^ | ^ |
@@ -3406,7 +3409,7 @@ describe('progress-message', function()
assert_progress_autocmd({ assert_progress_autocmd({
text = { 'test-message-updated' }, text = { 'test-message-updated' },
percent = 50, percent = 50,
source = '', source = 'tests',
status = 'running', status = 'running',
title = 'TestSuit', title = 'TestSuit',
id = 1, id = 1,
@@ -3417,7 +3420,7 @@ describe('progress-message', function()
api.nvim_echo( api.nvim_echo(
{ { 'test-message (success)' } }, { { 'test-message (success)' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 100, status = 'success' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 100, status = 'success' }
) )
screen:expect({ screen:expect({
grid = [[ grid = [[
@@ -3443,7 +3446,7 @@ describe('progress-message', function()
api.nvim_echo( api.nvim_echo(
{ { 'test-message (fail)' } }, { { 'test-message (fail)' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 35, status = 'failed' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 35, status = 'failed' }
) )
screen:expect({ screen:expect({
grid = [[ grid = [[
@@ -3469,7 +3472,7 @@ describe('progress-message', function()
api.nvim_echo( api.nvim_echo(
{ { 'test-message (cancel)' } }, { { 'test-message (cancel)' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 30, status = 'cancel' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 30, status = 'cancel' }
) )
screen:expect({ screen:expect({
grid = [[ grid = [[
@@ -3495,7 +3498,7 @@ describe('progress-message', function()
api.nvim_echo( api.nvim_echo(
{ { 'test-message (no-tile or percent)' } }, { { 'test-message (no-tile or percent)' } },
true, true,
{ kind = 'progress', status = 'cancel' } { kind = 'progress', source = 'tests', status = 'cancel' }
) )
screen:expect({ screen:expect({
grid = [[ grid = [[
@@ -3517,7 +3520,7 @@ describe('progress-message', function()
api.nvim_echo( api.nvim_echo(
{ { 'test-message-updated' } }, { { 'test-message-updated' } },
true, true,
{ id = id, kind = 'progress', percent = 80, status = 'running' } { id = id, kind = 'progress', source = 'other_source', percent = 80, status = 'running' }
) )
assert_progress_autocmd(nil, 'No progress message with Tests source yet') assert_progress_autocmd(nil, 'No progress message with Tests source yet')
@@ -3545,7 +3548,7 @@ describe('progress-message', function()
kind = 'progress', kind = 'progress',
title = 'TestSuit', title = 'TestSuit',
percent = 10, percent = 10,
source = '', source = 'tests',
status = 'running', status = 'running',
data = { test_attribute = 1 }, data = { test_attribute = 1 },
}) })
@@ -3572,7 +3575,7 @@ describe('progress-message', function()
assert_progress_autocmd({ assert_progress_autocmd({
text = { 'test-message' }, text = { 'test-message' },
percent = 10, percent = 10,
source = '', source = 'tests',
status = 'running', status = 'running',
title = 'TestSuit', title = 'TestSuit',
id = 1, id = 1,
@@ -3614,7 +3617,7 @@ describe('progress-message', function()
api.nvim_echo, api.nvim_echo,
{ { 'test-message' } }, { { 'test-message' } },
false, false,
{ kind = 'progress', status = 'live' } { kind = 'progress', source = 'tests', status = 'live' }
) )
) )
@@ -3625,7 +3628,7 @@ describe('progress-message', function()
api.nvim_echo, api.nvim_echo,
{ { 'test-message' } }, { { 'test-message' } },
false, false,
{ kind = 'progress', status = 'running', percent = -1 } { kind = 'progress', source = 'tests', status = 'running', percent = -1 }
) )
) )
@@ -3635,18 +3638,31 @@ describe('progress-message', function()
api.nvim_echo, api.nvim_echo,
{ { 'test-message' } }, { { 'test-message' } },
false, false,
{ kind = 'progress', status = 'running', percent = 101 } { kind = 'progress', source = 'tests', status = 'running', percent = 101 }
) )
) )
-- throws error if data is not a dictionary -- throws error if data is not a dictionary
eq( eq(
"Invalid 'data': expected Dict, got String", "Invalid 'data': expected Dict, got String",
t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, {
kind = 'progress',
source = 'tests',
title = 'TestSuit',
percent = 10,
status = 'running',
data = 'test',
})
)
-- throws error if source is not given
eq(
"Required: 'opts.source'",
t.pcall_err( t.pcall_err(
api.nvim_echo, api.nvim_echo,
{ { 'test-message' } }, { { 'test-message' } },
false, false,
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running', data = 'test' } { kind = 'progress', status = 'running' }
) )
) )
end) end)
@@ -3655,15 +3671,18 @@ describe('progress-message', function()
local id = api.nvim_echo( local id = api.nvim_echo(
{ { 'test-message 10' } }, { { 'test-message 10' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 10, status = 'running' }
) )
eq('TestSuit: 10% test-message 10', exec_capture('messages')) eq('TestSuit: 10% test-message 10', exec_capture('messages'))
api.nvim_echo( api.nvim_echo({ { 'test-message 20' } }, true, {
{ { 'test-message 20' } }, id = id,
true, kind = 'progress',
{ id = id, kind = 'progress', title = 'TestSuit', percent = 20, status = 'running' } source = 'tests',
) title = 'TestSuit',
percent = 20,
status = 'running',
})
eq('TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20', exec_capture('messages')) eq('TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20', exec_capture('messages'))
api.nvim_echo({ { 'middle msg' } }, true, {}) api.nvim_echo({ { 'middle msg' } }, true, {})
@@ -3671,21 +3690,27 @@ describe('progress-message', function()
'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg', 'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg',
exec_capture('messages') exec_capture('messages')
) )
api.nvim_echo( api.nvim_echo({ { 'test-message 30' } }, true, {
{ { 'test-message 30' } }, id = id,
true, kind = 'progress',
{ id = id, kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } source = 'tests',
) title = 'TestSuit',
percent = 30,
status = 'running',
})
eq( eq(
'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg\nTestSuit: 30% test-message 30', 'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg\nTestSuit: 30% test-message 30',
exec_capture('messages') exec_capture('messages')
) )
api.nvim_echo( api.nvim_echo({ { 'test-message 50' } }, true, {
{ { 'test-message 50' } }, id = id,
true, kind = 'progress',
{ id = id, kind = 'progress', title = 'TestSuit', percent = 50, status = 'running' } source = 'tests',
) title = 'TestSuit',
percent = 50,
status = 'running',
})
eq( eq(
'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg\nTestSuit: 30% test-message 30\nTestSuit: 50% test-message 50', 'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg\nTestSuit: 30% test-message 30\nTestSuit: 50% test-message 50',
exec_capture('messages') exec_capture('messages')
@@ -3696,14 +3721,14 @@ describe('progress-message', function()
local id1 = api.nvim_echo( local id1 = api.nvim_echo(
{ { 'test-message 10' } }, { { 'test-message 10' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 10, status = 'running' }
) )
eq(1, id1) eq(1, id1)
local id2 = api.nvim_echo( local id2 = api.nvim_echo(
{ { 'test-message 20' } }, { { 'test-message 20' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 20, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 20, status = 'running' }
) )
eq(2, id2) eq(2, id2)
@@ -3716,30 +3741,36 @@ describe('progress-message', function()
local id5 = api.nvim_echo( local id5 = api.nvim_echo(
{ { 'test-message 30' } }, { { 'test-message 30' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 30, status = 'running' }
) )
eq(5, id5) eq(5, id5)
-- updating progress message does not create new msg-id -- updating progress message does not create new msg-id
local id5_update = api.nvim_echo( local id5_update = api.nvim_echo({ { 'test-message 40' } }, true, {
{ { 'test-message 40' } }, id = id5,
true, kind = 'progress',
{ id = id5, kind = 'progress', title = 'TestSuit', percent = 40, status = 'running' } source = 'tests',
) title = 'TestSuit',
percent = 40,
status = 'running',
})
eq(id5, id5_update) eq(id5, id5_update)
local id6 = api.nvim_echo( local id6 = api.nvim_echo(
{ { 'test-message 30' } }, { { 'test-message 30' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 30, status = 'running' }
) )
eq(6, id6) eq(6, id6)
local id7 = api.nvim_echo( local id7 = api.nvim_echo({ { 'supports str-id' } }, true, {
{ { 'supports str-id' } }, id = 'str-id',
true, kind = 'progress',
{ id = 'str-id', kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } source = 'tests',
) title = 'TestSuit',
percent = 30,
status = 'running',
})
eq('str-id', id7) eq('str-id', id7)
-- internal messages are also assigned an ID (and thus advance the next progress ID) -- internal messages are also assigned an ID (and thus advance the next progress ID)
@@ -3747,18 +3778,21 @@ describe('progress-message', function()
local id8 = api.nvim_echo( local id8 = api.nvim_echo(
{ { 'test-message 30' } }, { { 'test-message 30' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 30, status = 'running' }
) )
eq(8, id8) eq(8, id8)
end) end)
it('accepts caller-defined id (string)', function() it('accepts caller-defined id (string)', function()
-- string id works -- string id works
local id = api.nvim_echo( local id = api.nvim_echo({ { 'supports str-id' } }, true, {
{ { 'supports str-id' } }, id = 'str-id',
true, kind = 'progress',
{ id = 'str-id', kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } source = 'tests',
) title = 'TestSuit',
percent = 30,
status = 'running',
})
eq('str-id', id) eq('str-id', id)
screen:expect({ screen:expect({
@@ -3781,16 +3815,19 @@ describe('progress-message', function()
}, },
}) })
local id_update = api.nvim_echo( local id_update = api.nvim_echo({ { 'supports str-id updated' } }, true, {
{ { 'supports str-id updated' } }, id = id,
true, kind = 'progress',
{ id = id, kind = 'progress', title = 'testsuit', percent = 40, status = 'running' } source = 'tests',
) title = 'testsuit',
percent = 40,
status = 'running',
})
eq(id, id_update) eq(id, id_update)
assert_progress_autocmd({ assert_progress_autocmd({
text = { 'supports str-id updated' }, text = { 'supports str-id updated' },
percent = 40, percent = 40,
source = '', source = 'tests',
status = 'running', status = 'running',
title = 'testsuit', title = 'testsuit',
id = 'str-id', id = 'str-id',
@@ -3804,7 +3841,7 @@ describe('progress-message', function()
api.nvim_echo( api.nvim_echo(
{ { 'test-message' } }, { { 'test-message' } },
true, true,
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuit', percent = 10, status = 'running' }
) )
screen:expect([[ screen:expect([[
^ | ^ |
@@ -3818,7 +3855,7 @@ describe('progress-message', function()
kind = 'progress', kind = 'progress',
title = 'TestSuit', title = 'TestSuit',
percent = 10, percent = 10,
source = '', source = 'tests',
status = 'running', status = 'running',
}) })
@@ -3844,7 +3881,7 @@ describe('progress-message', function()
assert_progress_autocmd({ assert_progress_autocmd({
text = { 'test-message' }, text = { 'test-message' },
percent = 10, percent = 10,
source = '', source = 'tests',
status = 'running', status = 'running',
title = 'TestSuit', title = 'TestSuit',
id = 1, id = 1,
@@ -3857,7 +3894,7 @@ describe('progress-message', function()
api.nvim_echo( api.nvim_echo(
{ { 'test-message: not shown in cmdline' } }, { { 'test-message: not shown in cmdline' } },
true, true,
{ kind = 'progress', title = 'TestSuite', percent = 10, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuite', percent = 10, status = 'running' }
) )
screen:expect([[ screen:expect([[
^ | ^ |
@@ -3867,7 +3904,7 @@ describe('progress-message', function()
assert_progress_autocmd({ assert_progress_autocmd({
text = { 'test-message: not shown in cmdline' }, text = { 'test-message: not shown in cmdline' },
percent = 10, percent = 10,
source = '', source = 'tests',
status = 'running', status = 'running',
title = 'TestSuite', title = 'TestSuite',
id = 1, id = 1,
@@ -3878,7 +3915,7 @@ describe('progress-message', function()
api.nvim_echo( api.nvim_echo(
{ { 'test-message: shown in cmdline' } }, { { 'test-message: shown in cmdline' } },
true, true,
{ kind = 'progress', title = 'TestSuite', percent = 10, status = 'running' } { kind = 'progress', source = 'tests', title = 'TestSuite', percent = 10, status = 'running' }
) )
screen:expect({ screen:expect({
grid = [[ grid = [[
@@ -3903,7 +3940,7 @@ describe('progress-message', function()
assert_progress_autocmd({ assert_progress_autocmd({
text = { 'test-message: shown in cmdline' }, text = { 'test-message: shown in cmdline' },
percent = 10, percent = 10,
source = '', source = 'tests',
status = 'running', status = 'running',
title = 'TestSuite', title = 'TestSuite',
id = 2, id = 2,

View File

@@ -1057,27 +1057,33 @@ describe('default statusline', function()
local id1 = api.nvim_echo( local id1 = api.nvim_echo(
{ { 'searching...' } }, { { 'searching...' } },
true, true,
{ kind = 'progress', title = 'test', status = 'running', percent = 10 } { kind = 'progress', source = 'tests', title = 'test', status = 'running', percent = 10 }
) )
eq('10%(1) ', get_progress()) eq('10%(1) ', get_progress())
api.nvim_echo( api.nvim_echo({ { 'searching' } }, true, {
{ { 'searching' } }, id = id1,
true, kind = 'progress',
{ id = id1, kind = 'progress', percent = 50, status = 'running', title = 'terminal(ripgrep)' } source = 'tests',
) percent = 50,
status = 'running',
title = 'terminal(ripgrep)',
})
eq('50%(1) ', get_progress()) eq('50%(1) ', get_progress())
api.nvim_echo( api.nvim_echo({ { 'searching...' } }, true, {
{ { 'searching...' } }, kind = 'progress',
true, source = 'tests',
{ kind = 'progress', title = 'second-item', status = 'running', percent = 20 } title = 'second-item',
) status = 'running',
percent = 20,
})
eq('35%(2) ', get_progress()) eq('35%(2) ', get_progress())
api.nvim_echo({ { 'searching' } }, true, { api.nvim_echo({ { 'searching' } }, true, {
id = id1, id = id1,
kind = 'progress', kind = 'progress',
source = 'tests',
percent = 100, percent = 100,
status = 'success', status = 'success',
title = 'terminal(ripgrep)', title = 'terminal(ripgrep)',