fix(messages): truncate warning messages only in display (#38901)

For now, add a private "_truncate" flag to nvim_echo, using a truncation
method similar to showmode().

(cherry picked from commit 9c5fba5df0)
This commit is contained in:
zeertzjq
2026-04-09 12:11:32 +08:00
committed by github-actions[bot]
parent ed47b27ad4
commit 1b36b75832
9 changed files with 117 additions and 22 deletions
+4 -2
View File
@@ -773,7 +773,8 @@ do
vim.v.swapchoice = 'e' -- Choose "(E)dit".
vim.notify(
('W325: Ignoring swapfile from Nvim process %d'):format(info.pid),
vim.log.levels.WARN
vim.log.levels.WARN,
{ _truncate = true }
)
end,
})
@@ -984,7 +985,8 @@ do
then
vim.notify(
'defaults.lua: Did not detect DSR response from terminal. This results in a slower startup time.',
vim.log.levels.WARN
vim.log.levels.WARN,
{ _truncate = true }
)
end
end
+6 -4
View File
@@ -547,7 +547,11 @@ end
---@diagnostic disable-next-line: unused-local
function vim.notify(msg, level, opts) -- luacheck: no unused args
local chunks = { { msg, level == vim.log.levels.WARN and 'WarningMsg' or nil } }
vim.api.nvim_echo(chunks, true, { err = level == vim.log.levels.ERROR })
vim.api.nvim_echo(
chunks,
true,
{ err = level == vim.log.levels.ERROR, _truncate = opts and opts._truncate }
)
end
do
@@ -1213,9 +1217,7 @@ end
do
local function truncated_echo(msg)
-- Truncate message to avoid hit-enter-prompt
local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace
local msg_truncated = string.sub(msg, 1, max_width)
vim.api.nvim_echo({ { msg_truncated, 'WarningMsg' } }, true, {})
vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, { _truncate = true })
end
local notified = false
+1
View File
@@ -239,6 +239,7 @@ error('Cannot require a meta file')
--- @class vim.api.keyset.echo_opts
--- @field err? boolean
--- @field verbose? boolean
--- @field _truncate? boolean
--- @field kind? string
--- @field id? integer|string
--- @field title? string
+1
View File
@@ -357,6 +357,7 @@ typedef struct {
OptionalKeys is_set__echo_opts_;
Boolean err;
Boolean verbose;
Boolean _truncate;
String kind;
Union(Integer, String) id;
String title;
+17
View File
@@ -884,7 +884,24 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
.percent = opts->percent, .data = opts->data,
.source = opts->source };
const bool save_nwr = need_wait_return;
const int save_lines_left = lines_left;
const bool save_msg_didany = msg_didany;
// Similar truncation method to showmode().
if (opts->_truncate) {
no_wait_return++;
lines_left = 0;
msg_didany = true;
msg_no_more = true;
}
id = msg_multihl(opts->id, hl_msg, kind, history, opts->err, &msg_data, &needs_clear);
if (opts->_truncate) {
msg_no_more = false;
msg_didany = save_msg_didany;
lines_left = save_lines_left;
no_wait_return--;
need_wait_return = save_nwr;
}
if (opts->verbose) {
verbose_leave();
+1 -1
View File
@@ -118,7 +118,7 @@ static int progress_msg_target = PROGRESS_TARGET_CMD;
static FILE *verbose_fd = NULL;
static bool verbose_did_open = false;
bool keep_msg_more = false; // keep_msg was set by msgmore()
static bool keep_msg_more = false; // keep_msg was set by msgmore()
// When writing messages to the screen, there are many different situations.
// A number of variables is used to remember the current state:
@@ -360,9 +360,36 @@ pcall(vim.cmd.edit, 'Xtest_swapredraw.lua')
set_session(nvim1)
local screen = Screen.new(75, 18)
exec(init)
feed(':edit Xfile1\n')
screen:expect({ any = ('W325: Ignoring swapfile from Nvim process %d'):format(nvimpid) })
feed(':edit Xfile1\n')
local msg_expected = ('W325: Ignoring swapfile from Nvim process %d'):format(nvimpid)
screen:expect({ any = vim.pesc(msg_expected) })
eq(msg_expected, n.exec_capture('messages'))
command('bwipe!')
-- With smaller screen, the message should be truncated, but only in display.
screen:try_resize(40, 18)
feed(':edit Xfile1\n')
screen:expect([[
^ |
{1:~ }|*16
{19:W325: Ignoring swapfile from Nvim proces}|
]])
eq(('\n' .. msg_expected):rep(2):sub(2), n.exec_capture('messages'))
command('bwipe!')
-- Also test with 'ruler'.
screen:try_resize(60, 18)
command('set ruler')
feed(':edit Xfile1\n')
screen:expect([[
^ |
{1:~ }|*16
{19:W325: Ignoring swapfile from Nvim process }0,0-1 All |
]])
eq(('\n' .. msg_expected):rep(3):sub(2), n.exec_capture('messages'))
command('bwipe!')
nvim1:close()
end)
+22 -13
View File
@@ -134,18 +134,20 @@ describe('lua stdlib', function()
describe(('vim.deprecate prerel=%s,'):format(prerel or 'nil'), function()
local curver --- @type {major:number, minor:number}
local curstr --- @type string
local nextver --- @type string
before_each(function()
curver = exec_lua('return vim.version()')
-- "0.10" or "0.10-dev+xxx"
curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '')
-- "0.10" or "0.11"
nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1))
end)
it('plugin=nil, same message skipped', function()
-- "0.10" or "0.10-dev+xxx"
local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '')
eq(
([[foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]]):format(
curstr
),
[[foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]],
exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr)
)
-- Same message as above; skipped this time.
@@ -160,19 +162,26 @@ describe('lua stdlib', function()
end)
it('plugin=nil, show error if hard-deprecated', function()
-- "0.10" or "0.11"
local nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1))
local was_removed = prerel and 'was removed' or 'will be removed'
eq(
dedent(
[[
foo.hard_dep() is deprecated. Run ":checkhealth vim.deprecated" for more information]]
):format(was_removed, nextver),
[[foo.hard_dep() is deprecated. Run ":checkhealth vim.deprecated" for more information]],
exec_lua('return vim.deprecate(...)', 'foo.hard_dep()', 'vim.new_api()', nextver)
)
end)
it('plugin=nil, message is only truncated in display #38841', function()
local screen = Screen.new(50, 10)
exec_lua('vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr)
screen:expect([[
^ |
{1:~ }|*8
{19:foo.bar() is deprecated. Run ":checkhealth vim.dep}|
]])
eq(
[[foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]],
n.exec_capture('messages')
)
end)
it('plugin specified', function()
-- When `plugin` is specified, don't show ":help deprecated". #22235
eq(
+36
View File
@@ -3213,6 +3213,42 @@ describe('TUI', function()
feed_data(':set columns=99|set stl=redrawn%m\n')
screen:expect({ any = 'redrawn%[%+%]' })
end)
it('missing DSR response does not lead to hit-enter prompt #38877', function()
local child_server = n.new_pipename()
exec_lua('vim.uv.os_unsetenv("NVIM_TEST")')
local job = fn.jobstart({ nvim_prog, '--clean', '--listen', child_server }, {
-- Use pty = true for a PTY without a terminal, so that there is no DSR response.
pty = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
COLORTERM = 'xterm-256color',
NVIM_LOG_FILE = testlog,
},
})
finally(function()
exec_lua(function()
vim.fn.jobstop(job)
vim.fn.jobwait({ job }, 5000)
end)
os.remove(testlog)
end)
retry(nil, nil, function()
t.neq(nil, vim.uv.fs_stat(child_server))
end)
local child_session = n.connect(child_server)
local expected_msg =
'defaults.lua: Did not detect DSR response from terminal. This results in a slower startup time.'
retry(nil, 4000, function()
eq({ true, { mode = 'n', blocking = false } }, { child_session:request('nvim_get_mode') })
if not is_os('win') then -- ConPTY provides DSR response on Windows?
eq(
{ true, { output = expected_msg } },
{ child_session:request('nvim_exec2', 'messages', { output = true }) }
)
end
end)
end)
end)
describe('TUI UIEnter/UILeave', function()