mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-12 04:27:51 -05:00
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to the specification. However, many terminal emulators try to keep tabs in the grid, to be able to e.g. copy them. That is, copying a text chunk containing tabs should result in tabs being pasted, not spaces. In order to do that, we need to print a tab character to the grid. To improve text reflow of tabs, we also print spaces to the subsequent cells, up until (but not including) the next tab stop. However, we can only do this if all the cells between the cursor and the next tab stop are empty, since (obviously), we cannot overwrite pre-existing characters. Finally, while some fonts render tabs as spaces (i.e. an empty glyph), some use a glyph representing “unprintable” characters, or similar. Thus, we need to exclude cells with tab characters when rendering.
This commit is contained in:
parent
e77b7d7111
commit
94b549f93e
2 changed files with 41 additions and 4 deletions
2
render.c
2
render.c
|
|
@ -620,7 +620,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
if (has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus)
|
||||
draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols);
|
||||
|
||||
if (cell->wc == 0 || cell->wc >= CELL_SPACER ||
|
||||
if (cell->wc == 0 || cell->wc >= CELL_SPACER || cell->wc == L'\t' ||
|
||||
(unlikely(cell->attrs.conceal) && !is_selected))
|
||||
{
|
||||
goto draw_cursor;
|
||||
|
|
|
|||
43
vt.c
43
vt.c
|
|
@ -162,20 +162,57 @@ action_execute(struct terminal *term, uint8_t c)
|
|||
|
||||
case '\t': {
|
||||
/* HT - horizontal tab */
|
||||
int start_col = term->grid->cursor.point.col;
|
||||
int new_col = term->cols - 1;
|
||||
|
||||
tll_foreach(term->tab_stops, it) {
|
||||
if (it->item > term->grid->cursor.point.col) {
|
||||
if (it->item > start_col) {
|
||||
new_col = it->item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xassert(new_col >= term->grid->cursor.point.col);
|
||||
xassert(new_col >= start_col);
|
||||
xassert(new_col < term->cols);
|
||||
|
||||
|
||||
bool emit_tab_char = true;
|
||||
struct row *row = term->grid->cur_row;
|
||||
|
||||
/* Check if all cells from here until the next tab stop are empty */
|
||||
for (const struct cell *cell = &row->cells[start_col];
|
||||
cell < &row->cells[new_col];
|
||||
cell++)
|
||||
{
|
||||
if (!(cell->wc == 0 || cell->wc == L' ')) {
|
||||
emit_tab_char = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit a tab in current cell, and write spaces to the
|
||||
* subsequent cells, all the way until the next tab stop.
|
||||
*/
|
||||
if (emit_tab_char) {
|
||||
row->dirty = true;
|
||||
|
||||
row->cells[start_col].wc = '\t';
|
||||
row->cells[start_col].attrs.clean = 0;
|
||||
|
||||
for (struct cell *cell = &row->cells[start_col + 1];
|
||||
cell < &row->cells[new_col];
|
||||
cell++)
|
||||
{
|
||||
cell->wc = L' ';
|
||||
cell->attrs.clean = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* According to the specification, HT _should_ cancel LCF. But
|
||||
* XTerm, and nearly all other emulators, don't. So we follow
|
||||
* suit */
|
||||
bool lcf = term->grid->cursor.lcf;
|
||||
term_cursor_right(term, new_col - term->grid->cursor.point.col);
|
||||
term_cursor_right(term, new_col - start_col);
|
||||
term->grid->cursor.lcf = lcf;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue