From 7b6efcf19a10794dd7b41e30ce706eae3a290388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 4 Mar 2025 08:34:18 +0100 Subject: [PATCH] grid: change default value of linebreak to true This way, all lines are treated as having a hard linebreak, until it's cleared when we do an auto-wrap. This change alone causes issues when reflowing text, as now all trailing lines in an otherwise empty window are treated as hard linebreaks, causing the new grid to insert lots of unwanted, empty lines. Fix by doing two things: * *clear* the linebreak flag when we pull in new lines for the new grid. We only want to set it explicitly, when an old row has its linebreak flag set. * Coalesce empty lines with linebreak=true, and only "emit" them as new liens in the new grid if they are followed by non-empty lines. --- grid.c | 87 +++++++++++++++++++++++++++++++++++++----------------- terminal.c | 2 +- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/grid.c b/grid.c index 2deb9111..4b9a7fb6 100644 --- a/grid.c +++ b/grid.c @@ -439,7 +439,7 @@ grid_row_alloc(int cols, bool initialize) { struct row *row = xmalloc(sizeof(*row)); row->dirty = false; - row->linebreak = false; + row->linebreak = true; row->extra = NULL; row->shell_integration.prompt_marker = false; row->shell_integration.cmd_start = -1; @@ -709,14 +709,21 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row, /* Scrollback not yet full, allocate a completely new row */ new_row = grid_row_alloc(col_count, false); new_grid[*row_idx] = new_row; + + /* *clear* linebreak, since we only want to set it when we + reach the end of an old row, with linebreak=true */ + new_row->linebreak = false; } else { /* Scrollback is full, need to reuse a row */ grid_row_reset_extra(new_row); - new_row->linebreak = false; new_row->shell_integration.prompt_marker = false; new_row->shell_integration.cmd_start = -1; new_row->shell_integration.cmd_end = -1; + /* *clear* linebreak, since we only want to set it when we + reach the end of an old row, with linebreak=true */ + new_row->linebreak = false; + tll_foreach(old_grid->sixel_images, it) { if (it->item.pos.row == *row_idx) { sixel_destroy(&it->item); @@ -894,6 +901,8 @@ grid_resize_and_reflow( i, tracking_points[i]->row, tracking_points[i]->col); } + int coalesced_linebreaks = 0; + /* * Walk the old grid */ @@ -984,6 +993,20 @@ grid_resize_and_reflow( } else underline_range = underline_range_terminator = NULL; + if (unlikely(col_count > 0 && coalesced_linebreaks > 0)) { + for (size_t apa = 0; apa < coalesced_linebreaks; apa++) { + /* Erase the remaining cells */ + memset(&new_row->cells[new_col_idx], 0, + (new_cols - new_col_idx) * sizeof(new_row->cells[0])); + new_row->linebreak = true; + + if (r + 1 < old_rows) + line_wrap(); + } + + coalesced_linebreaks = 0; + } + for (int c = 0; c < col_count;) { const struct cell *old = &old_row->cells[c]; @@ -1095,33 +1118,43 @@ grid_resize_and_reflow( } if (old_row->linebreak) { - /* Erase the remaining cells */ - memset(&new_row->cells[new_col_idx], 0, - (new_cols - new_col_idx) * sizeof(new_row->cells[0])); - new_row->linebreak = true; - - if (r + 1 < old_rows) - line_wrap(); - else if (new_row->extra != NULL) { - if (new_row->extra->uri_ranges.count > 0) { - /* - * line_wrap() "closes" still-open URIs. Since - * this is the *last* row, and since we're - * line-breaking due to a hard line-break (rather - * than running out of cells in the "new_row"), - * there shouldn't be an open URI (it would have - * been closed when we reached the end of the URI - * while reflowing the last "old" row). - */ - int last_idx = new_row->extra->uri_ranges.count - 1; - xassert(new_row->extra->uri_ranges.v[last_idx].end >= 0); - } - - if (new_row->extra->underline_ranges.count > 0) { - int last_idx = new_row->extra->underline_ranges.count - 1; - xassert(new_row->extra->underline_ranges.v[last_idx].end >= 0); + if (col_count > 0) { + /* Erase the remaining cells */ + memset(&new_row->cells[new_col_idx], 0, + (new_cols - new_col_idx) * sizeof(new_row->cells[0])); + new_row->linebreak = true; + if (r + 1 < old_rows) { + /* Not the last (old) row */ + line_wrap(); + } else if (new_row->extra != NULL) { + if (new_row->extra->uri_ranges.count > 0) { + /* + * line_wrap() "closes" still-open URIs. Since + * this is the *last* row, and since we're + * line-breaking due to a hard line-break (rather + * than running out of cells in the "new_row"), + * there shouldn't be an open URI (it would have + * been closed when we reached the end of the URI + * while reflowing the last "old" row). + */ + int last_idx = new_row->extra->uri_ranges.count - 1; + xassert(new_row->extra->uri_ranges.v[last_idx].end >= 0); + } + + if (new_row->extra->underline_ranges.count > 0) { + int last_idx = new_row->extra->underline_ranges.count - 1; + xassert(new_row->extra->underline_ranges.v[last_idx].end >= 0); + } } + } else { + /* + * rows have linebreak=true by default. But we don't + * want trailing empty lines to result in actual lines + * in the new grid (think: empty window with prompt at + * the top) + */ + coalesced_linebreaks++; } } diff --git a/terminal.c b/terminal.c index 988716f4..2ba85276 100644 --- a/terminal.c +++ b/terminal.c @@ -2057,7 +2057,7 @@ static inline void erase_line(struct terminal *term, struct row *row) { erase_cell_range(term, row, 0, term->cols - 1); - row->linebreak = false; + row->linebreak = true; row->shell_integration.prompt_marker = false; row->shell_integration.cmd_start = -1; row->shell_integration.cmd_end = -1;