mirror of
https://github.com/neovim/neovim.git
synced 2025-10-15 14:26:07 +00:00
feat(tui): allow grid and host to disagree on ambiguous-width chars (#19686)
Note: This only applies to ambiguous-width characters.
This commit is contained in:
@@ -875,6 +875,53 @@ safe_move:
|
|||||||
ugrid_goto(grid, row, col);
|
ugrid_goto(grid, row, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_spaces(UI *ui, int width)
|
||||||
|
{
|
||||||
|
TUIData *data = ui->data;
|
||||||
|
UGrid *grid = &data->grid;
|
||||||
|
|
||||||
|
out(ui, data->space_buf, (size_t)width);
|
||||||
|
grid->col += width;
|
||||||
|
if (data->immediate_wrap_after_last_column) {
|
||||||
|
// Printing at the right margin immediately advances the cursor.
|
||||||
|
final_column_wrap(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move cursor to the position given by `row` and `col` and print the character in `cell`.
|
||||||
|
/// This allows the grid and the host terminal to assume different widths of ambiguous-width chars.
|
||||||
|
///
|
||||||
|
/// @param is_doublewidth whether the character is double-width on the grid.
|
||||||
|
/// If true and the character is ambiguous-width, clear two cells.
|
||||||
|
static void print_cell_at_pos(UI *ui, int row, int col, UCell *cell, bool is_doublewidth)
|
||||||
|
{
|
||||||
|
TUIData *data = ui->data;
|
||||||
|
UGrid *grid = &data->grid;
|
||||||
|
|
||||||
|
if (grid->row == -1 && cell->data[0] == NUL) {
|
||||||
|
// If cursor needs to repositioned and there is nothing to print, don't move cursor.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor_goto(ui, row, col);
|
||||||
|
|
||||||
|
bool is_ambiwidth = utf_ambiguous_width(utf_ptr2char(cell->data));
|
||||||
|
if (is_ambiwidth && is_doublewidth) {
|
||||||
|
// Clear the two screen cells.
|
||||||
|
// If the character is single-width in the host terminal it won't change the second cell.
|
||||||
|
update_attrs(ui, cell->attr);
|
||||||
|
print_spaces(ui, 2);
|
||||||
|
cursor_goto(ui, row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_cell(ui, cell);
|
||||||
|
|
||||||
|
if (is_ambiwidth) {
|
||||||
|
// Force repositioning cursor after printing an ambiguous-width character.
|
||||||
|
grid->row = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void clear_region(UI *ui, int top, int bot, int left, int right, int attr_id)
|
static void clear_region(UI *ui, int top, int bot, int left, int right, int attr_id)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
@@ -888,7 +935,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, int attr
|
|||||||
&& left == 0 && right == ui->width && bot == ui->height) {
|
&& left == 0 && right == ui->width && bot == ui->height) {
|
||||||
if (top == 0) {
|
if (top == 0) {
|
||||||
unibi_out(ui, unibi_clear_screen);
|
unibi_out(ui, unibi_clear_screen);
|
||||||
ugrid_goto(&data->grid, top, left);
|
ugrid_goto(grid, top, left);
|
||||||
} else {
|
} else {
|
||||||
cursor_goto(ui, top, 0);
|
cursor_goto(ui, top, 0);
|
||||||
unibi_out(ui, unibi_clr_eos);
|
unibi_out(ui, unibi_clr_eos);
|
||||||
@@ -905,12 +952,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, int attr
|
|||||||
UNIBI_SET_NUM_VAR(data->params[0], width);
|
UNIBI_SET_NUM_VAR(data->params[0], width);
|
||||||
unibi_out(ui, unibi_erase_chars);
|
unibi_out(ui, unibi_erase_chars);
|
||||||
} else {
|
} else {
|
||||||
out(ui, data->space_buf, (size_t)width);
|
print_spaces(ui, width);
|
||||||
grid->col += width;
|
|
||||||
if (data->immediate_wrap_after_last_column) {
|
|
||||||
// Printing at the right margin immediately advances the cursor.
|
|
||||||
final_column_wrap(ui);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1302,8 +1344,8 @@ static void tui_flush(UI *ui)
|
|||||||
}
|
}
|
||||||
|
|
||||||
UGRID_FOREACH_CELL(grid, row, r.left, clear_col, {
|
UGRID_FOREACH_CELL(grid, row, r.left, clear_col, {
|
||||||
cursor_goto(ui, row, curcol);
|
print_cell_at_pos(ui, row, curcol, cell,
|
||||||
print_cell(ui, cell);
|
curcol < clear_col - 1 && (cell + 1)->data[0] == NUL);
|
||||||
});
|
});
|
||||||
if (clear_col < r.right) {
|
if (clear_col < r.right) {
|
||||||
clear_region(ui, row, row + 1, clear_col, r.right, clear_attr);
|
clear_region(ui, row, row + 1, clear_col, r.right, clear_attr);
|
||||||
@@ -1439,8 +1481,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I
|
|||||||
grid->cells[linerow][c].attr = attrs[c - startcol];
|
grid->cells[linerow][c].attr = attrs[c - startcol];
|
||||||
}
|
}
|
||||||
UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, {
|
UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, {
|
||||||
cursor_goto(ui, (int)linerow, curcol);
|
print_cell_at_pos(ui, (int)linerow, curcol, cell,
|
||||||
print_cell(ui, cell);
|
curcol < endcol - 1 && (cell + 1)->data[0] == NUL);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (clearcol > endcol) {
|
if (clearcol > endcol) {
|
||||||
@@ -1458,8 +1500,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I
|
|||||||
if (endcol != grid->width) {
|
if (endcol != grid->width) {
|
||||||
// Print the last char of the row, if we haven't already done so.
|
// Print the last char of the row, if we haven't already done so.
|
||||||
int size = grid->cells[linerow][grid->width - 1].data[0] == NUL ? 2 : 1;
|
int size = grid->cells[linerow][grid->width - 1].data[0] == NUL ? 2 : 1;
|
||||||
cursor_goto(ui, (int)linerow, grid->width - size);
|
print_cell_at_pos(ui, (int)linerow, grid->width - size,
|
||||||
print_cell(ui, &grid->cells[linerow][grid->width - size]);
|
&grid->cells[linerow][grid->width - size], size == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the cursor over to the next line. The next line will be
|
// Wrap the cursor over to the next line. The next line will be
|
||||||
|
@@ -1145,6 +1145,47 @@ describe('TUI', function()
|
|||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]=])
|
]=])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('allows grid to assume wider ambiguous-width characters than host terminal #19686', function()
|
||||||
|
child_session:request('nvim_buf_set_lines', 0, 0, 0, true, { ('℃'):rep(60), ('℃'):rep(60) })
|
||||||
|
child_session:request('nvim_win_set_option', 0, 'cursorline', true)
|
||||||
|
child_session:request('nvim_win_set_option', 0, 'list', true)
|
||||||
|
child_session:request('nvim_win_set_option', 0, 'listchars', 'eol:$')
|
||||||
|
local attrs = screen:get_default_attr_ids()
|
||||||
|
attrs[11] = {underline = true} -- CursorLine
|
||||||
|
attrs[12] = {underline = true, reverse = true} -- CursorLine and TermCursor
|
||||||
|
attrs[13] = {underline = true, foreground = 12} -- CursorLine and NonText
|
||||||
|
feed_data('gg')
|
||||||
|
local singlewidth_screen = [[
|
||||||
|
{12:℃}{11:℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃}|
|
||||||
|
{11:℃℃℃℃℃℃℃℃℃℃}{13:$}{11: }|
|
||||||
|
℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃|
|
||||||
|
℃℃℃℃℃℃℃℃℃℃{4:$} |
|
||||||
|
{5:[No Name] [+] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]
|
||||||
|
-- When grid assumes "℃" to be double-width but host terminal assumes it to be single-width, the
|
||||||
|
-- second cell of "℃" is a space and the attributes of the "℃" are applied to it.
|
||||||
|
local doublewidth_screen = [[
|
||||||
|
{12:℃}{11: ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
|
||||||
|
{11:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
|
||||||
|
{11:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }{13:$}{11: }|
|
||||||
|
℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ >{4:@@@}|
|
||||||
|
{5:[No Name] [+] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]
|
||||||
|
screen:expect(singlewidth_screen, attrs)
|
||||||
|
child_session:request('nvim_set_option', 'ambiwidth', 'double')
|
||||||
|
screen:expect(doublewidth_screen, attrs)
|
||||||
|
child_session:request('nvim_set_option', 'ambiwidth', 'single')
|
||||||
|
screen:expect(singlewidth_screen, attrs)
|
||||||
|
child_session:request('nvim_call_function', 'setcellwidths', {{{0x2103, 0x2103, 2}}})
|
||||||
|
screen:expect(doublewidth_screen, attrs)
|
||||||
|
child_session:request('nvim_call_function', 'setcellwidths', {{{0x2103, 0x2103, 1}}})
|
||||||
|
screen:expect(singlewidth_screen, attrs)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('TUI', function()
|
describe('TUI', function()
|
||||||
|
Reference in New Issue
Block a user