fix(terminal): handle split composing chars at right edge (#37694)

Problem:
Recombining composing chars in terminal doesn't work at right edge.

Solution:
Check for the case where printing the previous char didn't advance the
cursor. Reset at_phantom when returning to combine_pos.
This commit is contained in:
zeertzjq
2026-02-05 08:19:11 +08:00
committed by GitHub
parent a124439559
commit 814f2629cb
3 changed files with 49 additions and 3 deletions

View File

@@ -342,13 +342,15 @@ static int on_text(const char bytes[], size_t len, void *user)
// See if the cursor has moved since
if (state->pos.row == state->combine_pos.row
&& state->pos.col == state->combine_pos.col + state->combine_width) {
&& state->pos.col >= state->combine_pos.col
&& state->pos.col <= state->combine_pos.col + state->combine_width) {
// This is a combining char. that needs to be merged with the previous glyph output
if (utf_iscomposing((int)state->grapheme_last, (int)codepoints[i], &state->grapheme_state)) {
// Find where we need to append these combining chars
grapheme_len = state->grapheme_len;
grapheme_state = state->grapheme_state;
state->pos.col = state->combine_pos.col;
state->at_phantom = 0;
recombine = true;
} else {
DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");

View File

@@ -758,7 +758,7 @@ describe(':terminal buffer', function()
end)
it('handles split UTF-8 sequences #16245', function()
local screen = Screen.new(50, 7)
local screen = Screen.new(50, 10)
fn.jobstart({ testprg('shell-test'), 'UTF-8' }, { term = true })
screen:expect([[
^å |
@@ -766,7 +766,24 @@ describe(':terminal buffer', function()
1: å̲ |
2: å̲ |
3: å̲ |
|*2
|
[Process exited 0] |
|*3
]])
-- Test with Unicode char at right edge using a 4-wide terminal
command('bwipe! | set laststatus=0 | 4vnew')
fn.jobstart({ testprg('shell-test'), 'UTF-8' }, { term = true })
screen:expect([[
^å │ |
ref:│{1:~ }|
å̲ │{1:~ }|
1: å̲│{1:~ }|
2: å̲│{1:~ }|
3: å̲│{1:~ }|
│{1:~ }|
[Pro│{1:~ }|
cess│{1:~ }|
|
]])
end)

View File

@@ -992,6 +992,7 @@ describe('vterm', function()
push('e' .. string.rep('\xCC\x81', 20), vt)
expect('putglyph 65,301,301,301,301,301,301,301,301,301,301,301,301,301,301 1 0,0') -- and nothing more
-- Combining across buffers multiple times
reset(state, nil)
push('e', vt)
expect('putglyph 65 1 0,0')
@@ -1000,6 +1001,32 @@ describe('vterm', function()
push('\xCC\x82', vt)
expect('putglyph 65,301,302 1 0,0')
-- Combining across buffers at right edge
reset(state, nil)
push('\x1b[5;80H', vt)
push('e', vt)
expect('putglyph 65 1 4,79')
push('\xCC\x81', vt)
expect('putglyph 65,301 1 4,79')
push('\xCC\x82Z', vt)
expect('putglyph 65,301,302 1 4,79\nputglyph 5a 1 5,0')
-- Combining regional indicators
reset(state, nil)
push('\x1b[5;77H', vt)
push('🇦', vt)
expect('putglyph 1f1e6 2 4,76')
push('🇩', vt)
expect('putglyph 1f1e6,1f1e9 2 4,76')
push('🇱', vt)
expect('putglyph 1f1f1 2 4,78')
push('🇮', vt)
expect('putglyph 1f1f1,1f1ee 2 4,78')
push('🇲', vt)
expect('putglyph 1f1f2 2 5,0')
push('🇨', vt)
expect('putglyph 1f1f2,1f1e8 2 5,0')
-- emoji with ZWJ and variant selectors, as one chunk
reset(state, nil)
push('🏳️‍🌈🏳️‍⚧️🏴‍☠️', vt)