From 196200d03f04c9fe6794755b16a15a0e2f5a2736 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 14 Jul 2021 17:15:53 +0530 Subject: [PATCH] Dont reflow the current prompt when resizing This ensures that there is no leftover line when the shell redraws the prompt, by making the prompt a simple single character prompt before rewrapping. --- kitty/line-buf.c | 6 +++--- kitty/lineops.h | 2 +- kitty/rewrap.h | 2 +- kitty/screen.c | 38 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 35bb9aae2..1b9c6d140 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -266,11 +266,11 @@ clear_line_(Line *l, index_type xnum) { } void -linebuf_clear_line(LineBuf *self, index_type y) { +linebuf_clear_line(LineBuf *self, index_type y, bool clear_attrs) { Line l; init_line(self, &l, self->line_map[y]); clear_line_(&l, self->xnum); - self->line_attrs[y] = 0; + if (clear_attrs) self->line_attrs[y] = 0; } static PyObject* @@ -278,7 +278,7 @@ clear_line(LineBuf *self, PyObject *val) { #define clear_line_doc "clear_line(y) -> Clear the specified line" index_type y = (index_type)PyLong_AsUnsignedLong(val); if (y >= self->ynum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } - linebuf_clear_line(self, y); + linebuf_clear_line(self, y, true); Py_RETURN_NONE; } diff --git a/kitty/lineops.h b/kitty/lineops.h index a06553a19..c2932e247 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -106,7 +106,7 @@ void linebuf_init_line(LineBuf *, index_type); void linebuf_clear(LineBuf *, char_type ch); void linebuf_index(LineBuf* self, index_type top, index_type bottom); void linebuf_reverse_index(LineBuf *self, index_type top, index_type bottom); -void linebuf_clear_line(LineBuf *self, index_type y); +void linebuf_clear_line(LineBuf *self, index_type y, bool clear_attrs); void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom); void linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom); void linebuf_copy_line_to(LineBuf *, Line *, index_type); diff --git a/kitty/rewrap.h b/kitty/rewrap.h index 424002b09..3f00b8ffa 100644 --- a/kitty/rewrap.h +++ b/kitty/rewrap.h @@ -34,7 +34,7 @@ dest->line->has_dirty_text = true; \ historybuf_add_line(historybuf, dest->line, as_ansi_buf); \ }\ - linebuf_clear_line(dest, dest->ynum - 1); \ + linebuf_clear_line(dest, dest->ynum - 1, true); \ } else dest_y++; \ init_dest_line(dest_y); \ set_dest_line_attrs(dest_y, continued); diff --git a/kitty/screen.c b/kitty/screen.c index 67987d292..da5fd8274 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -271,11 +271,44 @@ index_selection(const Screen *self, Selections *selections, bool up) { #define INDEX_DOWN \ if (self->overlay_line.is_active) deactivate_overlay_line(self); \ linebuf_reverse_index(self->linebuf, top, bottom); \ - linebuf_clear_line(self->linebuf, top); \ + linebuf_clear_line(self->linebuf, top, true); \ INDEX_GRAPHICS(1) \ self->is_dirty = true; \ index_selection(self, &self->selections, false); + +static void +prevent_current_prompt_from_rewrapping(Screen *self, index_type columns) { + int y = self->cursor->y; + while (y >= 0) { + linebuf_init_line(self->main_linebuf, y); + Line *line = self->linebuf->line; + if (line->is_output_start) return; + if (line->is_prompt_start) break; + y--; + } + if (y < 0) return; + // we have identified a prompt at which the cursor is present, the shell + // will redraw this prompt. However when doing so it gets confused if the + // cursor vertical position relative to the first prompt line changes. This + // can easily be seen for instance in zsh when a right side prompt is used + // so when resizing to smaller sizes, simply blank all lines after the current + // prompt and trust the shell to redraw them + for (; y < (int)self->main_linebuf->ynum; y++) { + linebuf_mark_line_as_not_continued(self->main_linebuf, y); + if (columns < self->columns) { + linebuf_clear_line(self->main_linebuf, y, false); + linebuf_init_line(self->main_linebuf, y); + if (y <= (int)self->cursor->y) { + // this is needed because screen_resize() checks to see if the cursor is beyond the content, + // so insert some fake content + Line *line = self->linebuf->line; + line->cpu_cells[0].ch = '>'; + } + } + } +} + static bool screen_resize(Screen *self, unsigned int lines, unsigned int columns) { if (self->overlay_line.is_active) deactivate_overlay_line(self); @@ -299,6 +332,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) { HistoryBuf *nh = realloc_hb(self->historybuf, self->historybuf->ynum, columns, &self->as_ansi_buf); if (nh == NULL) return false; Py_CLEAR(self->historybuf); self->historybuf = nh; + if (is_main) prevent_current_prompt_from_rewrapping(self, columns); LineBuf *n = realloc_lb(self->main_linebuf, lines, columns, &num_content_lines_before, &num_content_lines_after, self->historybuf, &cursor, &main_saved_cursor, &self->as_ansi_buf); if (n == NULL) return false; Py_CLEAR(self->main_linebuf); self->main_linebuf = n; @@ -1135,7 +1169,7 @@ screen_cursor_to_column(Screen *self, unsigned int column) { historybuf_add_line(self->historybuf, self->linebuf->line, &self->as_ansi_buf); \ self->history_line_added_count++; \ } \ - linebuf_clear_line(self->linebuf, bottom); \ + linebuf_clear_line(self->linebuf, bottom, true); \ self->is_dirty = true; \ index_selection(self, &self->selections, true);