From 5a6532168443e479143c84aa42a5ac17d4c5db63 Mon Sep 17 00:00:00 2001 From: glepnir Date: Sun, 18 Jan 2026 16:55:30 +0800 Subject: [PATCH] fix(pum): text overflows into border on truncation (#37438) Problem: When text is too long, it overflows into the border. This happens because grid_line_maxcol includes border columns, so grid_line_puts can write there. Solution: Truncate string to available width when need_fcs_trunc is set. --- src/nvim/popupmenu.c | 17 ++++++++++++ test/functional/ui/popupmenu_spec.lua | 39 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index bf620c24b4..3abb80f5e4 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -817,6 +817,23 @@ void pum_redraw(void) if (width_limit - totwidth < cells + pad) { need_fcs_trunc = true; } + if (need_fcs_trunc) { + int available_cells = width_limit - totwidth; + // Find the truncation point by counting display cells + char *p_end = st; + int displayed = 0; + while (*p_end != NUL) { + int char_cells = utf_ptr2cells(p_end); + if (displayed + char_cells > available_cells) { + break; + } + displayed += char_cells; + MB_PTR_ADV(p_end); + } + *p_end = NUL; + cells = displayed; + width = displayed; + } if (attrs == NULL) { grid_line_puts(grid_col, st, -1, attr); diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index d41596002c..3ee073c506 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -9405,6 +9405,45 @@ describe('builtin popupmenu', function() assert_alive() eq({ 4, 1 }, { #fn.complete_info({ 'items' }).items, fn.pumvisible() }) end) + + it("works with 'pummaxwidth' #test", function() + exec([[ + set pummaxwidth=10 + set cot+=menuone + let g:list = [#{word: repeat('fo', 10)}] + ]]) + feed('S') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:------------------------------]|*10 + [3:------------------------------]| + ## grid 2 + fofofofofofofofofofo^ | + {1:~ }|*9 + ## grid 3 + {5:-- The only match} | + ## grid 4 + ╭──────────╮| + │{12:fofofofof>}│| + ╰──────────╯| + ]], + float_pos = { + [4] = { -1, 'NW', 2, 1, 0, false, 100, 1, 1, 0 }, + }, + }) + else + screen:expect([[ + fofofofofofofofofofo^ | + ╭──────────╮{1: }| + │{12:fofofofof>}│{1: }| + ╰──────────╯{1: }| + {1:~ }|*6 + {5:-- The only match} | + ]]) + end + end) end) end