feat(ui2): highlight the [+x] spill indicator with MoreMsg #41679

Problem:
The "spill" indicator that ui2 appends when messages overflow the
available height is drawn with whatever highlight the message tail
happens to have, so it is indistinguishable from the message text.

Solution:
Give the [+x] chunks an explicit `MoreMsg` highlight, and only fall back
to the message tail highlight for chunks that don't carry one of their
own.

AI-assisted
This commit is contained in:
Volodymyr Chernetskyi authored and GitHub committed 2026-09-04 14:28:22 -04:00
1 parent b7a0c71a54
commit 06bff27929
3 files changed
+31 -28

No files matched your search

+1
View File
@@ -496,6 +496,7 @@ UI
• |z=| (spell suggest)
• The |ui2| dialog window scrolls with the mouse wheel, by 'mousescroll'
lines.
• The |ui2| `[+x]` "spill" indicator is highlighted with |hl-MoreMsg|.
VIMSCRIPT
+9 -7
View File
@@ -1,6 +1,7 @@
local api, fn, o = vim.api, vim.fn, vim.o
local nvim_on = require('vim._core.util').nvim_on
local ui = require('vim._core.ui2')
local more_msg = api.nvim_get_hl_id_by_name('MoreMsg') -- Highlight for the [+x] indicators.
---@alias Msg { extid: integer, timer: uv.uv_timer_t? }
---@class vim._core.ui2.messages
@@ -162,11 +163,11 @@ local function set_virttext(type, tgt)
col = fn.virtcol2col(win, row + 1, texth.end_vcol - (scol - offset + width - mwidth))
end
-- Give virt_text the same highlight as the message tail.
-- Give virt_text without its own highlight the same highlight as the message tail.
local pos, opts = { row, col }, { details = true, overlap = true, type = 'highlight' }
local hl = api.nvim_buf_get_extmarks(ui.bufs[tgt], ui.ns, pos, pos, opts)
for _, chunk in ipairs(hl[1] and chunks or {}) do
chunk[2] = hl[1][4].hl_group
chunk[2] = chunk[2] or hl[1][4].hl_group
end
else
local mode = #M.virt.last[M.virt.idx.mode]
@@ -340,7 +341,7 @@ function M.show_msg(tgt, kind, content, replace_last, append, id)
local texth = api.nvim_win_text_height(ui.wins.cmd, { max_height = lines })
texth.all = math.max(texth.all, api.nvim_buf_line_count(ui.bufs.cmd))
local spill = texth.all > ui.cmdheight and (' [+%d]'):format(texth.all - ui.cmdheight)
M.virt.cmd[M.virt.idx.spill][1] = spill and { 0, spill } or nil
M.virt.cmd[M.virt.idx.spill][1] = spill and { 0, spill, more_msg } or nil
M.cmd.msg_row = texth.end_row
-- Expand the cmdline for a non-error message that doesn't fit.
@@ -636,9 +637,9 @@ end
local function set_top_bot_spill()
local topspill = fn.line('w0', ui.wins.dialog) - 1
local botspill = api.nvim_buf_line_count(ui.bufs.dialog) - fn.line('w$', ui.wins.dialog)
M.virt.top[1][1] = topspill > 0 and { 0, (' [+%d]'):format(topspill) } or nil
M.virt.top[1][1] = topspill > 0 and { 0, (' [+%d]'):format(topspill), more_msg } or nil
set_virttext('top', 'dialog')
M.virt.bot[1][1] = botspill > 0 and { 0, (' [+%d]'):format(botspill) } or nil
M.virt.bot[1][1] = botspill > 0 and { 0, (' [+%d]'):format(botspill), more_msg } or nil
set_virttext('bot', 'dialog')
api.nvim__redraw({ flush = true })
return topspill > 0 or botspill > 0
@@ -753,9 +754,10 @@ function M.set_pos(tgt, focus)
-- Dismiss temporarily expanded cmdline on next keypress and update spill indicator.
texth.all = math.max(texth.all, api.nvim_buf_line_count(ui.bufs.cmd))
local spill = texth.all > cfg.height and (' [+%d]'):format(texth.all - cfg.height)
M.virt.cmd[M.virt.idx.spill][1] = spill and { 0, spill } or nil
M.virt.cmd[M.virt.idx.spill][1] = spill and { 0, spill, more_msg } or nil
set_virttext('cmd', 'cmd')
M.virt.cmd[M.virt.idx.spill][1] = { 0, (' [+%d]'):format(texth.all - ui.cmdheight) }
M.virt.cmd[M.virt.idx.spill][1] =
{ 0, (' [+%d]'):format(texth.all - ui.cmdheight), more_msg }
M.cmd_on_key = vim.on_key(cmd_on_key, ui.ns)
elseif tgt == 'dialog' and set_top_bot_spill() then
M.dialog_on_key = vim.on_key(dialog_on_key, M.dialog_on_key)
+21 -21
View File
@@ -79,7 +79,7 @@ describe('messages2', function()
bar |
baz |
bar |
baz [+23] |
baz{6: [+23]} |
]])
-- Any key press resizes the cmdline and updates the spill indicator.
feed('j')
@@ -161,7 +161,7 @@ describe('messages2', function()
{5: + [No Name] }{24: [No Name] }{2: }{24:X}|
^x |
{1:~ }|*11
foo [+1] 1,1 All|
foo{6: [+1]} 1,1 All|
]])
-- Don't enter the pager in insert mode.
command('tabonly | call nvim_echo([["foo\n"]]->repeat(&lines), 1, {}) | startinsert')
@@ -170,14 +170,14 @@ describe('messages2', function()
{1:~ }|*5
{3: }|
foo |*6
foo [+8] |
foo{6: [+8]} |
]])
feed('<CR>')
screen:expect([[
|
^x |
{1:~ }|*11
foo [+14] 2,1 All|
foo{6: [+14]} 2,1 All|
]])
feed('<BS><Esc>')
-- First multiline message expands cmdline, additional message updates spill indicator.
@@ -187,7 +187,7 @@ describe('messages2', function()
{1:~ }|*5
{3: }|
foo |*6
foo [+9] |
foo{6: [+9]} |
]])
-- Do enter the pager in normal mode (with keybinding setup).
-- Also checks that "messagesopt=pager:…" is normalized to the keytrans() form.
@@ -404,7 +404,7 @@ describe('messages2', function()
{1:~ }|*5
foo |
bar |*5
bar [+8] |
bar{6: [+8]} |
]])
end)
@@ -501,7 +501,7 @@ describe('messages2', function()
screen:expect([[
^ |
{1:~ }|*12
foo [+1] |
foo{6: [+1]} |
]])
end)
@@ -512,7 +512,7 @@ describe('messages2', function()
{1:~ }|*5
{3: }|
foo |*6
foo [+8] |
foo{6: [+8]} |
]])
-- Place cmdline below expanded messages: #37653, without "more" title #38481.
feed(':call setline(1, "foo")')
@@ -521,7 +521,7 @@ describe('messages2', function()
{1:~ }|*4
{3: }|
foo |*6
foo [+8] |
foo{6: [+8]} |
{16::}{15:call} {25:setline}{16:(}{26:1}{16:,} {26:"foo"}{16:)}^ |
]])
-- No message closes expanded cmdline and keeps the entered command.
@@ -676,7 +676,7 @@ describe('messages2', function()
3 |
4 |
5 |
6 [+93] |
6{6: [+93]} |
Type number and <Enter> (q or empty cancels): ^ |
]]
command('set mousescroll=ver:2')
@@ -687,13 +687,13 @@ describe('messages2', function()
|
{1:~ }|*4
{3: }|
1 [+1] |
1{6: [+1]} |
2 |
3 |
4 |
5 |
6 |
7 [+92] |
7{6: [+92]} |
Type number and <Enter> (q or empty cancels): ^ |
]])
feed('<Up>')
@@ -703,13 +703,13 @@ describe('messages2', function()
|
{1:~ }|*4
{3: }|
5 [+5] |
5{6: [+5]} |
6 |
7 |
8 |
9 |
10 |
11 [+88] |
11{6: [+88]} |
Type number and <Enter> (q or empty cancels): ^ |
]])
feed('<PageUp>')
@@ -719,7 +719,7 @@ describe('messages2', function()
|
{1:~ }|*4
{3: }|
93 [+93] |
93{6: [+93]} |
94 |
95 |
96 |
@@ -739,13 +739,13 @@ describe('messages2', function()
|
{1:~ }|*4
{3: }|
2 [+2] |
2{6: [+2]} |
3 |
4 |
5 |
6 |
7 |
8 [+91] |
8{6: [+91]} |
Type number and <Enter> (q or empty cancels): ^ |
]])
feed('<Up>')
@@ -753,13 +753,13 @@ describe('messages2', function()
|
{1:~ }|*4
{3: }|
1 [+1] |
1{6: [+1]} |
2 |
3 |
4 |
5 |
6 |
7 [+92] |
7{6: [+92]} |
Type number and <Enter> (q or empty cancels): ^ |
]])
feed('<ScrollWheelUp><0,0>')
@@ -1032,7 +1032,7 @@ describe('messages2', function()
^ |
{1:~ }|*2
foo |*2
{14:f}oo [+6] |
{14:f}oo{6: [+6]} |
]])
feed('<Esc>')
screen:expect([[
@@ -1247,7 +1247,7 @@ describe('messages2', function()
screen:expect([[
^ |
{1:~ }|*12
foo [+1] |
foo{6: [+1]} |
]])
end)