From ce8005545d5bc94775f616d0caacd55e691e400f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 14 Feb 2020 22:39:26 +0100 Subject: [PATCH] term: convert cell 'linefeed' attribute to a row 'linebreak' property To do text reflow, we only need to know if a line has been explicitly linebreaked or not. If not, that means it wrapped, and that we should *not* insert a linebreak when reflowing text. When reflowing text, when reaching the end of a row in the old grid, only insert a linebreak in the new grid if the old row had been explicitly linebreaked. Furthermore, when reflowing text and wrapping a row in the new grid, mark the previous row as linebreaked if either the last cell was (the last column in the last row) empty, or the current cell (the first column in the new row) is empty. If both are non-empty, then we assume a linewrap. --- grid.c | 1 + render.c | 58 ++++++++++++++++++++++------------------------------- selection.c | 2 +- terminal.c | 7 ++----- terminal.h | 4 ++-- 5 files changed, 30 insertions(+), 42 deletions(-) diff --git a/grid.c b/grid.c index 744dd175..a9d155e0 100644 --- a/grid.c +++ b/grid.c @@ -28,6 +28,7 @@ grid_row_alloc(int cols, bool initialize) { struct row *row = malloc(sizeof(*row)); row->dirty = false; + row->linebreak = false; if (initialize) { row->cells = calloc(cols, sizeof(row->cells[0])); diff --git a/render.c b/render.c index 2c48629b..14c534d0 100644 --- a/render.c +++ b/render.c @@ -1009,17 +1009,16 @@ reflow(struct terminal *term, struct row **new_grid, int new_cols, int new_rows, /* Walk current line of the old grid */ for (int c = 0; c < old_cols; c++) { - const struct cell *old_cell = &old_row->cells[c]; - - if (old_cell->wc == 0) { + if (old_row->cells[c].wc == 0) { empty_count++; continue; } - assert(old_cell->wc != 0); - - /* Non-empty cell. Emit preceeding string of empty cells, - * and possibly line break for current cell */ + int old_cols_left = old_cols - c; + int cols_needed = empty_count + old_cols_left; + int new_cols_left = new_cols - new_col_idx; + if (new_cols_left < cols_needed && new_cols_left >= old_cols_left) + empty_count = max(0, empty_count - (cols_needed - new_cols_left)); for (int i = 0; i < empty_count + 1; i++) { if (new_col_idx >= new_cols) { @@ -1036,32 +1035,30 @@ reflow(struct terminal *term, struct row **new_grid, int new_cols, int new_rows, new_row->dirty = true; } + assert(new_row != NULL); + assert(new_col_idx >= 0); + assert(new_col_idx < new_cols); + + const struct cell *old_cell = &old_row->cells[c - empty_count + i]; + + if (new_col_idx == 0 && new_row_idx > 0 && + (new_grid[new_row_idx - 1]->cells[new_cols - 1].wc == 0 || + old_cell->wc == 0)) + { + new_grid[new_row_idx - 1]->linebreak = true; + } + + new_row->cells[new_col_idx] = *old_cell; + new_row->cells[new_col_idx].attrs.clean = 1; new_col_idx++; } empty_count = 0; - new_col_idx--; - - assert(new_row != NULL); - assert(new_col_idx >= 0); - assert(new_col_idx < new_cols); - - /* Copy current cell */ - new_row->cells[new_col_idx].attrs.clean = 1; - new_row->cells[new_col_idx++] = *old_cell; } - /* - * If last cell of the old grid's line is empty, then we - * insert a linebreak in the new grid's line too. Unless, the - * *entire* old line was empty. - */ + if (old_row->linebreak) { + new_row->linebreak = true; - if (empty_count < old_cols && - //r < old_rows - 1 && - (old_row->cells[old_cols - 1].wc == 0 || - old_row->cells[old_cols - 1].attrs.linefeed)) - { new_col_idx = 0; new_row_idx = (new_row_idx + 1) & (new_rows - 1); @@ -1237,19 +1234,12 @@ maybe_resize(struct terminal *term, int width, int height, bool force) while (cursor_row < 0) cursor_row += term->grid->num_rows; - /* Heuristic to prevent a new prompt from being printed a new line */ - bool do_linefeed = false; - if (term->cursor.point.col > 0) - cursor_row--; - else if (cursor_row >= term->rows) - do_linefeed = true; - term_cursor_to( term, min(max(cursor_row, 0), term->rows - 1), min(term->cursor.point.col, term->cols - 1)); - if (do_linefeed) + if (cursor_row >= term->rows) term_linefeed(term); term->render.last_cursor.cell = NULL; diff --git a/selection.c b/selection.c index f1485f45..189119d9 100644 --- a/selection.c +++ b/selection.c @@ -213,7 +213,7 @@ extract_one(struct terminal *term, struct row *row, struct cell *cell, if (ctx->last_row != NULL && row != ctx->last_row && ((term->selection.kind == SELECTION_NORMAL && - (ctx->last_cell->wc == 0 || ctx->last_cell->attrs.linefeed)) || + ctx->last_row->linebreak) || term->selection.kind == SELECTION_BLOCK)) { /* Last cell was the last column in the selection */ diff --git a/terminal.c b/terminal.c index cb52a416..b2fb9233 100644 --- a/terminal.c +++ b/terminal.c @@ -1253,6 +1253,7 @@ static inline void erase_line(struct terminal *term, struct row *row) { erase_cell_range(term, row, 0, term->cols - 1); + row->linebreak = false; } void @@ -1498,17 +1499,13 @@ term_scroll_reverse(struct terminal *term, int rows) void term_formfeed(struct terminal *term) { - int col = term->cursor.point.col; - if (!term->cursor.lcf) - col--; - if (col >= 0) - term->grid->cur_row->cells[col].attrs.linefeed = 1; term_cursor_left(term, term->cursor.point.col); } void term_linefeed(struct terminal *term) { + term->grid->cur_row->linebreak = true; if (term->cursor.point.row == term->scroll_region.end - 1) term_scroll(term, 1); else diff --git a/terminal.h b/terminal.h index 449a1329..1d19c9d7 100644 --- a/terminal.h +++ b/terminal.h @@ -42,8 +42,7 @@ struct attributes { uint32_t have_fg:1; uint32_t have_bg:1; uint32_t selected:2; - uint32_t linefeed:1; - uint32_t reserved:2; + uint32_t reserved:3; uint32_t bg:24; }; static_assert(sizeof(struct attributes) == 8, "bad size"); @@ -84,6 +83,7 @@ struct damage { struct row { struct cell *cells; bool dirty; + bool linebreak; }; struct grid {