From b0a3677969721dc541fea2245ad43568d8ab3cef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 31 Dec 2024 11:28:11 +0530 Subject: [PATCH] Refactor rewrap code, again Now we do the rewrap of history and line buffers together. This is faster and deals with multiline chars split between the two buffers correctly. Also, considerably simpler code. --- kitty/history.c | 60 +++++--- kitty/history.h | 3 + kitty/line-buf.c | 63 +------- kitty/lineops.h | 2 - kitty/resize.c | 317 +++++++++++++++++++++++++++++++++++++++ kitty/resize.h | 25 +++ kitty/rewrap.c | 305 ------------------------------------- kitty/rewrap.h | 22 --- kitty/screen.c | 67 ++++----- kitty_tests/datatypes.py | 46 ++---- 10 files changed, 435 insertions(+), 475 deletions(-) create mode 100644 kitty/resize.c create mode 100644 kitty/resize.h delete mode 100644 kitty/rewrap.c delete mode 100644 kitty/rewrap.h diff --git a/kitty/history.c b/kitty/history.c index e98df12b1e2..3de94527d3a 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -8,7 +8,7 @@ #include "wcswidth.h" #include "lineops.h" #include "charsets.h" -#include "rewrap.h" +#include "resize.h" #include #include "../3rdparty/ringbuf/ringbuf.h" @@ -599,6 +599,7 @@ INIT_TYPE(HistoryBuf) HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns, unsigned int pagerhist_sz, TextCache *tc) { return create_historybuf(&HistoryBuf_Type, columns, lines, pagerhist_sz, tc); } + // }}} static void @@ -617,34 +618,47 @@ historybuf_next_dest_line(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *src_line return dest_y + 1; } -void -historybuf_rewrap(HistoryBuf *self, HistoryBuf *other, ANSIBuf *as_ansi_buf) { - while(other->num_segments < self->num_segments) add_segment(other); - if (other->xnum == self->xnum && other->ynum == self->ynum) { - // Fast path - for (index_type i = 0; i < self->num_segments; i++) { - memcpy(other->segments[i].cpu_cells, self->segments[i].cpu_cells, SEGMENT_SIZE * self->xnum * sizeof(CPUCell)); - memcpy(other->segments[i].gpu_cells, self->segments[i].gpu_cells, SEGMENT_SIZE * self->xnum * sizeof(GPUCell)); - memcpy(other->segments[i].line_attrs, self->segments[i].line_attrs, SEGMENT_SIZE * sizeof(LineAttrs)); - } - other->count = self->count; other->start_of_data = self->start_of_data; - return; +HistoryBuf* +historybuf_alloc_for_rewrap(unsigned int columns, HistoryBuf *self) { + if (!self) return NULL; + HistoryBuf *ans = alloc_historybuf(self->ynum, columns, 0, self->text_cache); + if (ans) { + while(ans->num_segments < self->num_segments) add_segment(ans); + ans->count = 0; ans->start_of_data = 0; } - if (other->pagerhist && other->xnum != self->xnum && ringbuf_bytes_used(other->pagerhist->ringbuf)) - other->pagerhist->rewrap_needed = true; - other->count = 0; other->start_of_data = 0; - if (self->count > 0) { - historybuf_rewrap_inner(self, other, self->count, as_ansi_buf); - for (index_type i = 0; i < other->count; i++) attrptr(other, (other->start_of_data + i) % other->ynum)->has_dirty_text = true; + return ans; +} + +void +historybuf_finish_rewrap(HistoryBuf *dest, HistoryBuf *src) { + for (index_type i = 0; i < dest->count; i++) attrptr(dest, (dest->start_of_data + i) % dest->ynum)->has_dirty_text = true; + dest->pagerhist = src->pagerhist; src->pagerhist = NULL; + if (dest->pagerhist && dest->xnum != src->xnum && ringbuf_bytes_used(dest->pagerhist->ringbuf)) dest->pagerhist->rewrap_needed = true; +} + +void +historybuf_fast_rewrap(HistoryBuf *dest, HistoryBuf *src) { + for (index_type i = 0; i < src->num_segments; i++) { + memcpy(dest->segments[i].cpu_cells, src->segments[i].cpu_cells, SEGMENT_SIZE * src->xnum * sizeof(CPUCell)); + memcpy(dest->segments[i].gpu_cells, src->segments[i].gpu_cells, SEGMENT_SIZE * src->xnum * sizeof(GPUCell)); + memcpy(dest->segments[i].line_attrs, src->segments[i].line_attrs, SEGMENT_SIZE * sizeof(LineAttrs)); } + dest->count = src->count; dest->start_of_data = src->start_of_data; } + static PyObject* rewrap(HistoryBuf *self, PyObject *args) { - HistoryBuf *other; - if (!PyArg_ParseTuple(args, "O!", &HistoryBuf_Type, &other)) return NULL; + unsigned xnum; + if (!PyArg_ParseTuple(args, "I", &xnum)) return NULL; ANSIBuf as_ansi_buf = {0}; - historybuf_rewrap(self, other, &as_ansi_buf); + LineBuf *dummy = alloc_linebuf(4, self->xnum, self->text_cache); + if (!dummy) return PyErr_NoMemory(); + RAII_PyObject(cleanup, (PyObject*)dummy); (void)cleanup; + TrackCursor cursors[1] = {{.is_sentinel=true}}; + ResizeResult r = resize_screen_buffers(dummy, self, 8, xnum, &as_ansi_buf, cursors); free(as_ansi_buf.buf); - Py_RETURN_NONE; + if (!r.ok) return PyErr_NoMemory(); + Py_CLEAR(r.lb); + return (PyObject*)r.hb; } diff --git a/kitty/history.h b/kitty/history.h index 19f738883e1..4d397f9fcb2 100644 --- a/kitty/history.h +++ b/kitty/history.h @@ -35,4 +35,7 @@ typedef struct { HistoryBuf* alloc_historybuf(unsigned int, unsigned int, unsigned int, TextCache *tc); +HistoryBuf *historybuf_alloc_for_rewrap(unsigned int columns, HistoryBuf *self); +void historybuf_finish_rewrap(HistoryBuf *dest, HistoryBuf *src); +void historybuf_fast_rewrap(HistoryBuf *dest, HistoryBuf *src); index_type historybuf_next_dest_line(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued); diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 01ebead6151..83373f8df1e 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -7,7 +7,7 @@ #include "data-types.h" #include "lineops.h" -#include "rewrap.h" +#include "resize.h" #include @@ -596,65 +596,16 @@ copy_old(LineBuf *self, PyObject *y) { Py_RETURN_NONE; } -void -linebuf_rewrap( - LineBuf *self, LineBuf *other, index_type *num_content_lines_before, index_type *num_content_lines_after, - HistoryBuf *historybuf, index_type *track_x, index_type *track_y, index_type *track_x2, index_type *track_y2, - ANSIBuf *as_ansi_buf, bool history_buf_last_line_is_split -) { - index_type first, i; - bool is_empty = true; - - // Fast path - if (other->xnum == self->xnum && other->ynum == self->ynum) { - memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum); - memcpy(other->line_attrs, self->line_attrs, sizeof(LineAttrs) * self->ynum); - memcpy(other->cpu_cell_buf, self->cpu_cell_buf, (size_t)self->xnum * self->ynum * sizeof(CPUCell)); - memcpy(other->gpu_cell_buf, self->gpu_cell_buf, (size_t)self->xnum * self->ynum * sizeof(GPUCell)); - *num_content_lines_before = self->ynum; *num_content_lines_after = self->ynum; - return; - } - - // Find the first line that contains some content - first = self->ynum; - do { - first--; - CPUCell *cells = cpu_lineptr(self, self->line_map[first]); - for(i = 0; i < self->xnum; i++) { - if (cells[i].ch_or_idx || cells[i].ch_is_idx) { is_empty = false; break; } - } - } while(is_empty && first > 0); - - if (is_empty) { // All lines are empty - *num_content_lines_after = 0; - *num_content_lines_before = 0; - return; - } - *num_content_lines_before = first + 1; - TrackCursor tcarr[3] = { - {.x = *track_x, .y = *track_y, .dest_x=*track_x, .dest_y = *track_y }, - {.x = *track_x2, .y = *track_y2, .dest_x=*track_x2, .dest_y = *track_y2 }, - {.is_sentinel = true} - }; - *num_content_lines_after = 1 + linebuf_rewrap_inner(self, other, *num_content_lines_before, historybuf, (TrackCursor*)tcarr, as_ansi_buf, history_buf_last_line_is_split && historybuf != NULL); - *track_x = tcarr[0].dest_x; *track_y = tcarr[0].dest_y; - *track_x2 = tcarr[1].dest_x; *track_y2 = tcarr[1].dest_y; - for (i = 0; i < *num_content_lines_after; i++) other->line_attrs[i].has_dirty_text = true; -} - static PyObject* rewrap(LineBuf *self, PyObject *args) { - LineBuf* other; - HistoryBuf *historybuf; - unsigned int nclb, ncla; - - if (!PyArg_ParseTuple(args, "O!O!", &LineBuf_Type, &other, &HistoryBuf_Type, &historybuf)) return NULL; - index_type x = 0, y = 0, x2 = 0, y2 = 0; + unsigned int lines, columns; + if (!PyArg_ParseTuple(args, "II", &lines, &columns)) return NULL; + TrackCursor cursors[1] = {{.is_sentinel=true}}; ANSIBuf as_ansi_buf = {0}; - linebuf_rewrap(self, other, &nclb, &ncla, historybuf, &x, &y, &x2, &y2, &as_ansi_buf, false); + ResizeResult r = resize_screen_buffers(self, NULL, lines, columns, &as_ansi_buf, cursors); free(as_ansi_buf.buf); - - return Py_BuildValue("II", nclb, ncla); + if (!r.ok) return PyErr_NoMemory(); + return Py_BuildValue("NII", r.lb, r.num_content_lines_before, r.num_content_lines_after); } diff --git a/kitty/lineops.h b/kitty/lineops.h index 9afbb3aa658..a8bb7de4715 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -91,7 +91,6 @@ 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); -void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, HistoryBuf *, index_type *, index_type *, index_type *, index_type *, ANSIBuf*, bool); void linebuf_mark_line_dirty(LineBuf *self, index_type y); void linebuf_clear_attrs_and_dirty(LineBuf *self, index_type y); void linebuf_mark_line_clean(LineBuf *self, index_type y); @@ -102,7 +101,6 @@ bool linebuf_line_ends_with_continuation(LineBuf *self, index_type y); void linebuf_refresh_sprite_positions(LineBuf *self); void historybuf_add_line(HistoryBuf *self, const Line *line, ANSIBuf*); bool historybuf_pop_line(HistoryBuf *, Line *); -void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other, ANSIBuf*); void historybuf_init_line(HistoryBuf *self, index_type num, Line *l); bool history_buf_endswith_wrap(HistoryBuf *self); CPUCell* historybuf_cpu_cells(HistoryBuf *self, index_type num); diff --git a/kitty/resize.c b/kitty/resize.c new file mode 100644 index 00000000000..480142f2dd5 --- /dev/null +++ b/kitty/resize.c @@ -0,0 +1,317 @@ +/* + * resize.c + * Copyright (C) 2024 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include "resize.h" +#include "lineops.h" + +typedef struct Rewrap { + struct { + LineBuf *lb; + HistoryBuf *hb; + index_type x, y, hb_count; + Line line, scratch_line; + } src, dest; + ANSIBuf *as_ansi_buf; + TrackCursor *cursors; + LineBuf *sb; + + index_type num_content_lines_before, src_x_limit; + bool prev_src_line_ended_with_wrap, current_src_line_has_multline_cells, current_dest_line_has_multiline_cells; + bool dest_line_from_linebuf, src_is_in_linebuf; + +} Rewrap; + +static void +setup_line(TextCache *tc, index_type xnum, Line *l) { + l->text_cache = tc; + l->xnum = xnum; +} + +#define src_xnum (r->src.lb->xnum) +#define dest_xnum (r->dest.lb->xnum) + +static void +exclude_empty_lines_at_bottom(Rewrap *r) { + index_type first, i; + bool is_empty = true; + // Find the first line that contains some content +#define self (r->src.lb) + first = self->ynum; + do { + first--; + CPUCell *cells = linebuf_cpu_cells_for_line(self, first); + for(i = 0; i < self->xnum; i++) { + if (cells[i].ch_or_idx || cells[i].ch_is_idx) { is_empty = false; break; } + } + } while(is_empty && first > 0); + if (!is_empty) r->num_content_lines_before = first + 1; +#undef self +} + +static void +init_src_line_basic(Rewrap *r, index_type y, Line *dest, bool update_state) { + if (r->src_is_in_linebuf) { + linebuf_init_line_at(r->src.lb, y - r->src.hb_count, dest); + } else if (r->src.y >= r->src.hb_count) { + if (update_state) r->src_is_in_linebuf = true; + linebuf_init_line_at(r->src.lb, y - r->src.hb_count, dest); + } else { + // historybuf_init_line uses reverse indexing + historybuf_init_line(r->src.hb, r->src.hb->count - y - 1, dest); + } +} + +static bool +init_src_line(Rewrap *r) { + bool newline_needed = !r->prev_src_line_ended_with_wrap; + init_src_line_basic(r, r->src.y, &r->src.line, true); + r->src_x_limit = src_xnum; + r->prev_src_line_ended_with_wrap = r->src.line.cpu_cells[src_xnum - 1].next_char_was_wrapped; + r->src.line.cpu_cells[src_xnum - 1].next_char_was_wrapped = false; + // Trim trailing blanks + while (r->src_x_limit && r->src.line.cpu_cells[r->src_x_limit - 1].ch_and_idx == BLANK_CHAR) r->src_x_limit--; + r->src.x = 0; + r->current_src_line_has_multline_cells = false; + for (index_type i = 0; i < r->src_x_limit; i++) if (r->src.line.cpu_cells[i].is_multicell && r->src.line.cpu_cells[i].scale > 1) { + r->current_src_line_has_multline_cells = true; + break; + } + return newline_needed; +} + +#define set_dest_line_attrs(dest_y) r->dest.lb->line_attrs[dest_y] = r->src.line.attrs; r->src.line.attrs.prompt_kind = UNKNOWN_PROMPT_KIND; + +static void +first_dest_line(Rewrap *r) { + if (r->src.hb_count) historybuf_next_dest_line(r->dest.hb, r->as_ansi_buf, &r->src.line, 0, &r->dest.line, false); + else { + r->dest_line_from_linebuf = true; + linebuf_init_line_at(r->dest.lb, 0, &r->dest.line); + set_dest_line_attrs(0); + } +} + +static index_type +linebuf_next_dest_line(Rewrap *r, bool continued) { +#define dest_y r->dest.y + LineBuf *dest = r->dest.lb; + linebuf_set_last_char_as_continuation(dest, dest_y, continued); + if (dest_y >= dest->ynum - 1) { + linebuf_index(dest, 0, dest->ynum - 1); + if (r->dest.hb != NULL) { + linebuf_init_line(dest, dest->ynum - 1); + dest->line->attrs.has_dirty_text = true; + historybuf_add_line(r->dest.hb, dest->line, r->as_ansi_buf); + } + linebuf_clear_line(dest, dest->ynum - 1, true); + } else dest_y++; + linebuf_init_line_at(dest, dest_y, &r->dest.line); + set_dest_line_attrs(dest_y); + return dest_y; +#undef dest_y +} + + +static void +next_dest_line(Rewrap *r, bool continued) { + r->dest.x = 0; + r->current_dest_line_has_multiline_cells = false; + if (r->dest_line_from_linebuf) { + r->dest.y = linebuf_next_dest_line(r, continued); + } else if (r->src_is_in_linebuf) { + r->dest_line_from_linebuf = true; + r->dest.y = 0; + linebuf_init_line_at(r->dest.lb, 0, &r->dest.line); + set_dest_line_attrs(0); + if (continued && r->dest.hb && r->dest.hb->count) { + historybuf_init_line(r->dest.hb, 0, r->dest.hb->line); + r->dest.hb->line->cpu_cells[dest_xnum-1].next_char_was_wrapped = true; + } + } else { + r->dest.y = historybuf_next_dest_line(r->dest.hb, r->as_ansi_buf, &r->src.line, r->dest.y, &r->dest.line, continued); + } + if (r->sb->line_attrs[0].has_dirty_text) { + CPUCell *cpu_cells; GPUCell *gpu_cells; + linebuf_init_cells(r->sb, 0, &cpu_cells, &gpu_cells); + memcpy(r->dest.line.cpu_cells, cpu_cells, dest_xnum * sizeof(cpu_cells[0])); + memcpy(r->dest.line.gpu_cells, gpu_cells, dest_xnum * sizeof(gpu_cells[0])); + r->current_dest_line_has_multiline_cells = true; + } + linebuf_index(r->sb, 0, r->sb->ynum - 1); + if (r->sb->line_attrs[r->sb->ynum - 1].has_dirty_text) { + linebuf_clear_line(r->sb, r->sb->ynum - 1, true); + } +} + +static void +update_tracked_cursors(Rewrap *r, index_type num_cells, index_type src_y, index_type dest_y, index_type x_limit) { + if (!r->src_is_in_linebuf) return; + src_y -= r->src.hb_count; + for (TrackCursor *t = r->cursors; !t->is_sentinel; t++) { + if (t->y == src_y && r->src.x <= t->x && (t->x < r->src.x + num_cells || t->x >= x_limit)) { + t->dest_y = dest_y; + t->dest_x = r->dest.x + (t->x - r->src.x); + if (t->dest_x > dest_xnum) t->dest_x = dest_xnum; + } + } +} + +static bool +find_space_in_dest_line(Rewrap *r, index_type num_cells) { + while (r->dest.x + num_cells <= dest_xnum) { + index_type before = r->dest.x; + for (index_type x = r->dest.x; x < r->dest.x + num_cells; x++) { + if (r->dest.line.cpu_cells[x].is_multicell) { + r->dest.x = x + mcd_x_limit(r->dest.line.cpu_cells + x); + break; + } + } + if (before == r->dest.x) return true; + } + return false; +} + +static void +find_space_in_dest(Rewrap *r, index_type num_cells) { + while (!find_space_in_dest_line(r, num_cells)) next_dest_line(r, true); +} + +static void +copy_range(Line *src, index_type src_at, Line* dest, index_type dest_at, index_type num) { + memcpy(dest->cpu_cells + dest_at, src->cpu_cells + src_at, num * sizeof(CPUCell)); + memcpy(dest->gpu_cells + dest_at, src->gpu_cells + src_at, num * sizeof(GPUCell)); +} + +static void +copy_multiline_extra_lines(Rewrap *r, CPUCell *src_cell, index_type mc_width) { + for (index_type i = 1; i < src_cell->scale; i++) { + init_src_line_basic(r, r->src.y + i, &r->src.scratch_line, false); + linebuf_init_line_at(r->sb, i - 1, &r->dest.scratch_line); + linebuf_mark_line_dirty(r->sb, i - 1); + copy_range(&r->src.scratch_line, r->src.x, &r->dest.scratch_line, r->dest.x, mc_width); + update_tracked_cursors(r, mc_width, r->src.y + i, r->dest.y + i, src_xnum + 10000 /* ensure cursor is moved only if in region being copied */); + } +} + + +static void +multiline_copy_src_to_dest(Rewrap *r) { + CPUCell *c; index_type mc_width; + while (r->src.x < r->src_x_limit) { + c = &r->src.line.cpu_cells[r->src.x]; + if (c->is_multicell) { + mc_width = mcd_x_limit(c); + if (mc_width > dest_xnum) { + update_tracked_cursors(r, mc_width, r->src.y, r->dest.y, r->src_x_limit); + r->src.x += mc_width; + continue; + } else if (c->y) { + r->src.x += mc_width; + continue; + } + } else mc_width = 1; + find_space_in_dest(r, mc_width); + copy_range(&r->src.line, r->src.x, &r->dest.line, r->dest.x, mc_width); + update_tracked_cursors(r, mc_width, r->src.y, r->dest.y, r->src_x_limit); + if (c->scale > 1) copy_multiline_extra_lines(r, c, mc_width); + r->src.x += mc_width; r->dest.x += mc_width; + } +} + + +static void +fast_copy_src_to_dest(Rewrap *r) { + CPUCell *c; index_type mc_width; + while (r->src.x < r->src_x_limit) { + if (r->dest.x >= dest_xnum) { + next_dest_line(r, true); + if (r->current_dest_line_has_multiline_cells) { + multiline_copy_src_to_dest(r); + return; + } + } + index_type num = MIN(r->src_x_limit - r->src.x, dest_xnum - r->dest.x); + if (num && (c = &r->src.line.cpu_cells[r->src.x + num - 1])->is_multicell && c->x != (mc_width = mcd_x_limit(c)) - 1) { + // we have a split multicell at the right edge of the copy region + if (num > mc_width) num = MIN(r->src_x_limit - r->src.x - mc_width, num); + else { + if (mc_width > dest_xnum) { + multiline_copy_src_to_dest(r); + return; + } + r->dest.x = dest_xnum; + continue; + } + } + copy_range(&r->src.line, r->src.x, &r->dest.line, r->dest.x, num); + update_tracked_cursors(r, num, r->src.y, r->dest.y, r->src_x_limit); + r->src.x += num; r->dest.x += num; + } +} + + +static void +rewrap(Rewrap *r) { + r->src.hb_count = r->src.hb ? r->src.hb->count : 0; + // Fast path + if (r->dest.lb->xnum == r->src.lb->xnum && r->dest.lb->ynum == r->src.lb->ynum) { + memcpy(r->dest.lb->line_map, r->src.lb->line_map, sizeof(index_type) * r->src.lb->ynum); + memcpy(r->dest.lb->line_attrs, r->src.lb->line_attrs, sizeof(LineAttrs) * r->src.lb->ynum); + memcpy(r->dest.lb->cpu_cell_buf, r->src.lb->cpu_cell_buf, (size_t)r->src.lb->xnum * r->src.lb->ynum * sizeof(CPUCell)); + memcpy(r->dest.lb->gpu_cell_buf, r->src.lb->gpu_cell_buf, (size_t)r->src.lb->xnum * r->src.lb->ynum * sizeof(GPUCell)); + r->num_content_lines_before = r->src.lb->ynum; + if (r->dest.hb && r->src.hb) historybuf_fast_rewrap(r->dest.hb, r->src.hb); + r->dest.y = r->src.lb->ynum - 1; + return; + } + + setup_line(r->src.lb->text_cache, src_xnum, &r->src.line); + setup_line(r->src.lb->text_cache, dest_xnum, &r->dest.line); + setup_line(r->src.lb->text_cache, src_xnum, &r->src.scratch_line); + setup_line(r->src.lb->text_cache, dest_xnum, &r->dest.scratch_line); + + exclude_empty_lines_at_bottom(r); + + for (; r->src.y < r->num_content_lines_before + r->src.hb_count; r->src.y++) { + if (init_src_line(r)) { + if (r->src.y) next_dest_line(r, false); + else first_dest_line(r); + } + if (r->current_src_line_has_multline_cells || r->current_dest_line_has_multiline_cells) multiline_copy_src_to_dest(r); + else fast_copy_src_to_dest(r); + } +} + +ResizeResult +resize_screen_buffers(LineBuf *lb, HistoryBuf *hb, index_type lines, index_type columns, ANSIBuf *as_ansi_buf, TrackCursor *cursors) { + ResizeResult ans = {0}; + ans.lb = alloc_linebuf(lines, columns, lb->text_cache); + if (!ans.lb) return ans; + RAII_PyObject(raii_nlb, (PyObject*)ans.lb); (void) raii_nlb; + if (hb) { + ans.hb = historybuf_alloc_for_rewrap(columns, hb); + if (!ans.hb) return ans; + } + RAII_PyObject(raii_nhb, (PyObject*)ans.hb); (void) raii_nhb; + Rewrap r = { + .src = {.lb=lb, .hb=hb}, .dest = {.lb=ans.lb, .hb=ans.hb}, + .as_ansi_buf = as_ansi_buf, .cursors = cursors, + }; + r.sb = alloc_linebuf(SCALE_BITS << 1, columns, lb->text_cache); + if (!r.sb) return ans; + RAII_PyObject(scratch, (PyObject*)r.sb); (void)scratch; + for (TrackCursor *t = cursors; !t->is_sentinel; t++) { t->dest_x = t->x; t->dest_y = t->y; } + rewrap(&r); + ans.num_content_lines_before = r.num_content_lines_before; + ans.num_content_lines_after = MIN(r.dest.y + 1, ans.lb->ynum); + if (hb) historybuf_finish_rewrap(ans.hb, hb); + for (unsigned i = 0; i < ans.num_content_lines_after; i++) linebuf_mark_line_dirty(ans.lb, i); + for (TrackCursor *t = cursors; !t->is_sentinel; t++) { t->dest_x = MIN(t->dest_x, columns); t->dest_y = MIN(t->dest_y, lines); } + Py_INCREF(raii_nlb); Py_XINCREF(raii_nhb); + ans.ok = true; + return ans; +} diff --git a/kitty/resize.h b/kitty/resize.h new file mode 100644 index 00000000000..a5bdbd02f6a --- /dev/null +++ b/kitty/resize.h @@ -0,0 +1,25 @@ +/* + * resize.h + * Copyright (C) 2024 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#pragma once +#include "line-buf.h" +#include "history.h" + +typedef struct TrackCursor { + index_type x, y; + index_type dest_x, dest_y; + bool is_sentinel; +} TrackCursor; + +typedef struct ResizeResult { + LineBuf *lb; HistoryBuf *hb; + bool ok; + index_type num_content_lines_before, num_content_lines_after; +} ResizeResult; + +ResizeResult +resize_screen_buffers(LineBuf *lb, HistoryBuf *hb, index_type lines, index_type columns, ANSIBuf *as_ansi_buf, TrackCursor *cursors); diff --git a/kitty/rewrap.c b/kitty/rewrap.c deleted file mode 100644 index 94b7705654f..00000000000 --- a/kitty/rewrap.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * rewrap.c - * Copyright (C) 2024 Kovid Goyal - * - * Distributed under terms of the GPL3 license. - */ - -#include "rewrap.h" -#include "lineops.h" -#include "text-cache.h" - - -typedef void (*init_line_func_t)(void *buf, index_type y, Line *line); -typedef index_type (*first_dest_line_func_t)(void *buf, ANSIBuf *as_ansi_buf, Line *src_line, Line *dest_line); -typedef index_type (*next_dest_line_func_t)(void *buf, HistoryBuf *historybuf, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued); - -static void -LineBuf_init_line(void *buf, index_type y, Line *line) { - linebuf_init_line_at(buf, y, line); -} - -static void -HistoryBuf_init_line(void *buf, index_type y, Line *line) { - HistoryBuf *dest = buf; - // historybuf_init_line uses reverse indexing - historybuf_init_line(dest, dest->count ? dest->count - y - 1 : 0, line); -} - - -#define set_dest_line_attrs(dest_y) dest->line_attrs[dest_y] = src_line->attrs; src_line->attrs.prompt_kind = UNKNOWN_PROMPT_KIND; - -static index_type -LineBuf_first_dest_line(void *buf, ANSIBuf *as_ansi_buf, Line *src_line, Line *dest_line) { - (void)as_ansi_buf; - LineBuf *dest = buf; - linebuf_init_line_at(dest, 0, dest_line); - set_dest_line_attrs(0); - return 0; -} - -static index_type -HistoryBuf_first_dest_line(void *buf, ANSIBuf *as_ansi_buf, Line *src_line, Line *dest_line) { - HistoryBuf *dest = buf; - historybuf_next_dest_line(dest, as_ansi_buf, src_line, 0, dest_line, false); - return 0; -} - -static index_type -LineBuf_next_dest_line(void *buf, HistoryBuf *historybuf, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued) { - LineBuf *dest = buf; - linebuf_set_last_char_as_continuation(dest, dest_y, continued); - if (dest_y >= dest->ynum - 1) { - linebuf_index(dest, 0, dest->ynum - 1); - if (historybuf != NULL) { - linebuf_init_line(dest, dest->ynum - 1); - dest->line->attrs.has_dirty_text = true; - historybuf_add_line(historybuf, dest->line, as_ansi_buf); - } - linebuf_clear_line(dest, dest->ynum - 1, true); - } else dest_y++; - linebuf_init_line_at(dest, dest_y, dest_line); - set_dest_line_attrs(dest_y); - return dest_y; -} - -static index_type -HistoryBuf_next_dest_line(void *buf, HistoryBuf *historybuf, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued) { - (void)historybuf; - HistoryBuf *dest = buf; - return historybuf_next_dest_line(dest, as_ansi_buf, src_line, dest_y, dest_line, continued); -} - -typedef struct Rewrap { - void *src_buf, *dest_buf; - index_type src_xnum, dest_xnum; - ANSIBuf *as_ansi_buf; - TextCache *text_cache; - HistoryBuf *historybuf; - TrackCursor *cursors; - index_type src_limit; - - Line src, dest, src_scratch, dest_scratch; - index_type src_y, src_x, dest_x, dest_y, num, src_x_limit, history_buf_last_line_xlimit; - init_line_func_t init_line; - first_dest_line_func_t first_dest_line; - next_dest_line_func_t next_dest_line; - LineBuf *scratch; - bool current_dest_line_has_multiline_cells, current_src_line_has_multline_cells, prev_src_line_ended_with_wrap; - bool current_dest_line_is_last_history_line; -} Rewrap; - -static void -copy_range(Line *src, index_type src_at, Line* dest, index_type dest_at, index_type num) { - memcpy(dest->cpu_cells + dest_at, src->cpu_cells + src_at, num * sizeof(CPUCell)); - memcpy(dest->gpu_cells + dest_at, src->gpu_cells + src_at, num * sizeof(GPUCell)); -} - -static void -setup_line(TextCache *tc, index_type xnum, Line *l) { - l->text_cache = tc; - l->xnum = xnum; -} - -static void -next_dest_line(Rewrap *r, bool continued) { - r->dest_x = 0; - r->current_dest_line_has_multiline_cells = false; - if (r->current_dest_line_is_last_history_line) { - r->dest.cpu_cells[r->dest_xnum-1].next_char_was_wrapped = continued; - r->current_dest_line_is_last_history_line = false; - r->dest_y = r->first_dest_line(r->dest_buf, r->as_ansi_buf, &r->src, &r->dest); - } else r->dest_y = r->next_dest_line(r->dest_buf, r->historybuf, r->as_ansi_buf, &r->src, r->dest_y, &r->dest, continued); - if (r->scratch->line_attrs[0].has_dirty_text) { - CPUCell *cpu_cells; GPUCell *gpu_cells; - linebuf_init_cells(r->scratch, 0, &cpu_cells, &gpu_cells); - memcpy(r->dest.cpu_cells, cpu_cells, r->dest_xnum * sizeof(cpu_cells[0])); - memcpy(r->dest.gpu_cells, gpu_cells, r->dest_xnum * sizeof(gpu_cells[0])); - r->current_dest_line_has_multiline_cells = true; - } - linebuf_index(r->scratch, 0, r->scratch->ynum - 1); - if (r->scratch->line_attrs[r->scratch->ynum - 1].has_dirty_text) { - linebuf_clear_line(r->scratch, r->scratch->ynum - 1, true); - } -} - -static void -first_dest_line(Rewrap *r) { - if (r->history_buf_last_line_xlimit < r->dest_xnum && r->historybuf) { - r->dest_y = 0; - r->dest_x = r->history_buf_last_line_xlimit; - r->current_dest_line_has_multiline_cells = true; - r->current_dest_line_is_last_history_line = true; - historybuf_init_line(r->historybuf, 0, &r->dest); - } else { - r->dest_y = r->first_dest_line(r->dest_buf, r->as_ansi_buf, &r->src, &r->dest); - } -} - -static bool -init_src_line(Rewrap *r) { - bool newline_needed = !r->prev_src_line_ended_with_wrap; - r->init_line(r->src_buf, r->src_y, &r->src); - r->src_x_limit = r->src_xnum; - // Trim trailing blanks - while(r->src_x_limit && r->src.cpu_cells[r->src_x_limit - 1].ch_and_idx == BLANK_CHAR) r->src_x_limit--; - r->prev_src_line_ended_with_wrap = r->src.cpu_cells[r->src_xnum - 1].next_char_was_wrapped; - r->src.cpu_cells[r->src_xnum - 1].next_char_was_wrapped = false; - r->src_x = 0; - r->current_src_line_has_multline_cells = false; - for (index_type i = 0; i < r->src_x_limit; i++) if (r->src.cpu_cells[i].is_multicell && r->src.cpu_cells[i].scale > 1) { - r->current_src_line_has_multline_cells = true; - break; - } - return newline_needed; -} - -static void -update_tracked_cursors(Rewrap *r, index_type num_cells, index_type src_y, index_type dest_y, index_type x_limit) { - for (TrackCursor *t = r->cursors; !t->is_sentinel; t++) { - if (t->y == src_y && r->src_x <= t->x && (t->x < r->src_x + num_cells || t->x >= x_limit)) { - if (r->current_dest_line_is_last_history_line) { - t->dest_x = 0; t->dest_y = 0; - } else { - t->dest_y = dest_y; - t->dest_x = r->dest_x + (t->x - r->src_x); - if (t->dest_x > r->dest_xnum) t->dest_x = r->dest_xnum; - } - } - } -} - -static bool -find_space_in_dest_line(Rewrap *r, index_type num_cells) { - while (r->dest_x + num_cells <= r->dest_xnum) { - index_type before = r->dest_x; - for (index_type x = r->dest_x; x < r->dest_x + num_cells; x++) { - if (r->dest.cpu_cells[x].is_multicell) { - r->dest_x = x + mcd_x_limit(r->dest.cpu_cells + x); - break; - } - } - if (before == r->dest_x) return true; - } - return false; -} - -static void -find_space_in_dest(Rewrap *r, index_type num_cells) { - while (!find_space_in_dest_line(r, num_cells)) next_dest_line(r, true); -} - -static void -copy_multiline_extra_lines(Rewrap *r, CPUCell *src_cell, index_type mc_width) { - for (index_type i = 1; i < src_cell->scale; i++) { - r->init_line(r->src_buf, r->src_y + i, &r->src_scratch); - linebuf_init_line_at(r->scratch, i - 1, &r->dest_scratch); - linebuf_mark_line_dirty(r->scratch, i - 1); - copy_range(&r->src_scratch, r->src_x, &r->dest_scratch, r->dest_x, mc_width); - update_tracked_cursors(r, mc_width, r->src_y + i, r->dest_y + i, r->src_xnum + 10000 /* ensure cursor is moved only if in region being copied */); - } -} - - -static void -multiline_copy_src_to_dest(Rewrap *r) { - CPUCell *c; index_type mc_width; - while (r->src_x < r->src_x_limit) { - c = &r->src.cpu_cells[r->src_x]; - if (c->is_multicell) { - mc_width = mcd_x_limit(c); - if (mc_width > r->dest_xnum) { - update_tracked_cursors(r, mc_width, r->src_y, r->dest_y, r->src_x_limit); - r->src_x += mc_width; - continue; - } else if (c->y) { - r->src_x += mc_width; - continue; - } - } else mc_width = 1; - find_space_in_dest(r, mc_width); - copy_range(&r->src, r->src_x, &r->dest, r->dest_x, mc_width); - update_tracked_cursors(r, mc_width, r->src_y, r->dest_y, r->src_x_limit); - if (c->scale > 1) copy_multiline_extra_lines(r, c, mc_width); - r->src_x += mc_width; r->dest_x += mc_width; - } -} - - -static void -fast_copy_src_to_dest(Rewrap *r) { - CPUCell *c; index_type mc_width; - while (r->src_x < r->src_x_limit) { - if (r->dest_x >= r->dest_xnum) { - next_dest_line(r, true); - if (r->current_dest_line_has_multiline_cells) { - multiline_copy_src_to_dest(r); - return; - } - } - index_type num = MIN(r->src_x_limit - r->src_x, r->dest_xnum - r->dest_x); - if (num && (c = &r->src.cpu_cells[r->src_x + num - 1])->is_multicell && c->x != (mc_width = mcd_x_limit(c)) - 1) { - // we have a split multicell at the right edge of the copy region - if (num > mc_width) num = MIN(r->src_x_limit - r->src_x - mc_width, num); - else { - if (mc_width > r->dest_xnum) { - multiline_copy_src_to_dest(r); - return; - } - r->dest_x = r->dest_xnum; - continue; - } - } - copy_range(&r->src, r->src_x, &r->dest, r->dest_x, num); - update_tracked_cursors(r, num, r->src_y, r->dest_y, r->src_x_limit); - r->src_x += num; r->dest_x += num; - } -} - -static index_type -rewrap_inner(Rewrap *r) { - setup_line(r->text_cache, r->src_xnum, &r->src); setup_line(r->text_cache, r->dest_xnum, &r->dest); - setup_line(r->text_cache, r->src_xnum, &r->src_scratch); setup_line(r->text_cache, r->dest_xnum, &r->dest_scratch); - - r->scratch = alloc_linebuf(SCALE_BITS << 1, r->dest_xnum, r->text_cache); - if (!r->scratch) fatal("Out of memory"); - RAII_PyObject(scratch, (PyObject*)r->scratch); (void)scratch; - for (; r->src_y < r->src_limit; r->src_y++) { - if (init_src_line(r)) { - if (r->src_y) next_dest_line(r, false); - else first_dest_line(r); - } - if (r->current_src_line_has_multline_cells || r->current_dest_line_has_multiline_cells) multiline_copy_src_to_dest(r); - else fast_copy_src_to_dest(r); - } - return r->dest_y; -} - -index_type -linebuf_rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, HistoryBuf *historybuf, TrackCursor *track, ANSIBuf *as_ansi_buf, bool history_buf_last_line_is_split) { - index_type xlimit = dest->xnum; - if (history_buf_last_line_is_split && historybuf) { - historybuf_init_line(historybuf, 0, historybuf->line); - xlimit = xlimit_for_line(historybuf->line); - } - Rewrap r = { - .src_buf = src, .dest_buf = dest, .as_ansi_buf = as_ansi_buf, .text_cache = src->text_cache, .cursors = track, - .src_limit = src_limit, .historybuf=historybuf, .src_xnum = src->xnum, .dest_xnum = dest->xnum, - .history_buf_last_line_xlimit = xlimit, - - .init_line = LineBuf_init_line, .next_dest_line = LineBuf_next_dest_line, .first_dest_line = LineBuf_first_dest_line, - }; - return rewrap_inner(&r); -} - -index_type -historybuf_rewrap_inner(HistoryBuf *src, HistoryBuf *dest, const index_type src_limit, ANSIBuf *as_ansi_buf) { - static TrackCursor t = {.is_sentinel = true }; - Rewrap r = { - .src_buf = src, .dest_buf = dest, .as_ansi_buf = as_ansi_buf, .text_cache = src->text_cache, - .src_limit = src_limit, .src_xnum = src->xnum, .dest_xnum = dest->xnum, .cursors=&t, .history_buf_last_line_xlimit = dest->xnum, - - .init_line = HistoryBuf_init_line, .next_dest_line = HistoryBuf_next_dest_line, .first_dest_line = HistoryBuf_first_dest_line, - }; - return rewrap_inner(&r); -} diff --git a/kitty/rewrap.h b/kitty/rewrap.h deleted file mode 100644 index b008ec35a37..00000000000 --- a/kitty/rewrap.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * rewrap.h - * Copyright (C) 2016 Kovid Goyal - * - * Distributed under terms of the GPL3 license. - */ - -#pragma once -#include "line-buf.h" -#include "history.h" - -typedef struct TrackCursor { - const index_type x, y; - index_type dest_x, dest_y; - bool is_sentinel; -} TrackCursor; - - -index_type linebuf_rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, HistoryBuf *historybuf, TrackCursor *track, ANSIBuf *as_ansi_buf, bool history_buf_last_line_is_split); - -index_type historybuf_rewrap_inner(HistoryBuf *src, HistoryBuf *dest, const index_type src_limit, ANSIBuf *as_ansi_buf); - diff --git a/kitty/screen.c b/kitty/screen.c index 3e5cb3683c2..b937df9f4a3 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -30,6 +30,7 @@ #include #include "keys.h" #include "vt-parser.h" +#include "resize.h" static const ScreenModes empty_modes = {0, .mDECAWM=true, .mDECTCEM=true, .mDECARM=true}; @@ -216,16 +217,6 @@ screen_dirty_sprite_positions(Screen *self) { for (index_type i = 0; i < self->historybuf->count; i++) historybuf_mark_line_dirty(self->historybuf, i); } -static HistoryBuf* -realloc_hb(HistoryBuf *old, unsigned int columns, ANSIBuf *as_ansi_buf) { - HistoryBuf *ans = alloc_historybuf(old->ynum, columns, 0, old->text_cache); - if (ans == NULL) { PyErr_NoMemory(); return NULL; } - ans->pagerhist = old->pagerhist; old->pagerhist = NULL; - historybuf_rewrap(old, ans, as_ansi_buf); - return ans; -} - - typedef struct CursorTrack { index_type num_content_lines; bool is_beyond_content; @@ -234,14 +225,36 @@ typedef struct CursorTrack { struct { index_type x, y; } temp; } CursorTrack; -static LineBuf* -realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, index_type *nclb, index_type *ncla, HistoryBuf *hb, CursorTrack *a, CursorTrack *b, ANSIBuf *as_ansi_buf, bool history_buf_last_line_is_split) { - LineBuf *ans = alloc_linebuf(lines, columns, old->text_cache); - if (ans == NULL) { PyErr_NoMemory(); return NULL; } - a->temp.x = a->before.x; a->temp.y = a->before.y; - b->temp.x = b->before.x; b->temp.y = b->before.y; - linebuf_rewrap(old, ans, nclb, ncla, hb, &a->temp.x, &a->temp.y, &b->temp.x, &b->temp.y, as_ansi_buf, history_buf_last_line_is_split); - return ans; +static bool +rewrap(Screen *screen, unsigned int lines, unsigned int columns, index_type *nclb, index_type *ncla, CursorTrack *cursor, CursorTrack *main_saved_cursor, CursorTrack *alt_saved_cursor, bool main_is_active) { + TrackCursor cursors[3]; + cursors[0] = (TrackCursor){.x=main_saved_cursor->before.x, .y=main_saved_cursor->before.y}; + if (main_is_active) cursors[1] = (TrackCursor){.x=cursor->before.x, .y=cursor->before.y}; + else cursors[1].is_sentinel = true; + ResizeResult mr = resize_screen_buffers(screen->main_linebuf, screen->historybuf, lines, columns, &screen->as_ansi_buf, cursors); + if (!mr.ok) { PyErr_NoMemory(); return false; } + main_saved_cursor->temp.x = cursors[0].dest_x; main_saved_cursor->temp.y = cursors[0].dest_y; + if (main_is_active) { cursor->temp.x = cursors[1].dest_x; cursor->temp.y = cursors[1].dest_y; } + + cursors[0] = (TrackCursor){.x=alt_saved_cursor->before.x, .y=alt_saved_cursor->before.y}; + if (!main_is_active) cursors[1] = (TrackCursor){.x=cursor->before.x, .y=cursor->before.y}; + else cursors[1].is_sentinel = true; + ResizeResult ar = resize_screen_buffers(screen->alt_linebuf, NULL, lines, columns, &screen->as_ansi_buf, cursors); + if (!ar.ok) { + Py_DecRef((PyObject*)mr.lb); Py_DecRef((PyObject*)mr.hb); + PyErr_NoMemory(); return false; + } + alt_saved_cursor->temp.x = cursors[0].dest_x; alt_saved_cursor->temp.y = cursors[0].dest_y; + if (!main_is_active) { cursor->temp.x = cursors[1].dest_x; cursor->temp.y = cursors[1].dest_y; } + Py_CLEAR(screen->main_linebuf); Py_CLEAR(screen->alt_linebuf); Py_CLEAR(screen->historybuf); + screen->main_linebuf = mr.lb; screen->historybuf = mr.hb; screen->alt_linebuf = ar.lb; + screen->linebuf = main_is_active ? screen->main_linebuf : screen->alt_linebuf; + if (main_is_active) { + *nclb = mr.num_content_lines_before; *ncla = mr.num_content_lines_after; + } else { + *nclb = ar.num_content_lines_before; *ncla = ar.num_content_lines_after; + } + return true; } static bool @@ -396,36 +409,22 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) { if (!init_overlay_line(self, columns, true)) return false; // Resize main linebuf - bool history_buf_last_line_is_split = history_buf_endswith_wrap(self->historybuf); - HistoryBuf *nh = realloc_hb(self->historybuf, columns, &self->as_ansi_buf); - if (nh == NULL) return false; - Py_CLEAR(self->historybuf); self->historybuf = nh; RAII_PyObject(prompt_copy, NULL); index_type num_of_prompt_lines = 0, num_of_prompt_lines_above_cursor = 0; if (is_main) { prompt_copy = (PyObject*)alloc_linebuf(self->lines, self->columns, self->text_cache); num_of_prompt_lines = prevent_current_prompt_from_rewrapping(self, (LineBuf*)prompt_copy, &num_of_prompt_lines_above_cursor); } - 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, history_buf_last_line_is_split); - if (n == NULL) return false; - Py_CLEAR(self->main_linebuf); self->main_linebuf = n; - if (is_main) setup_cursor(cursor); + if (!rewrap(self, lines, columns, &num_content_lines_before, &num_content_lines_after, &cursor, &main_saved_cursor, &alt_saved_cursor, is_main)) return false; + setup_cursor(cursor); /* printf("old_cursor: (%u, %u) new_cursor: (%u, %u) beyond_content: %d\n", self->cursor->x, self->cursor->y, cursor.after.x, cursor.after.y, cursor.is_beyond_content); */ setup_cursor(main_saved_cursor); grman_remove_all_cell_images(self->main_grman); grman_resize(self->main_grman, self->lines, lines, self->columns, columns, num_content_lines_before, num_content_lines_after); - - // Resize alt linebuf - n = realloc_lb(self->alt_linebuf, lines, columns, &num_content_lines_before, &num_content_lines_after, NULL, &cursor, &alt_saved_cursor, &self->as_ansi_buf, false); - if (n == NULL) return false; - Py_CLEAR(self->alt_linebuf); self->alt_linebuf = n; - if (!is_main) setup_cursor(cursor); setup_cursor(alt_saved_cursor); grman_remove_all_cell_images(self->alt_grman); grman_resize(self->alt_grman, self->lines, lines, self->columns, columns, num_content_lines_before, num_content_lines_after); #undef setup_cursor - - self->linebuf = is_main ? self->main_linebuf : self->alt_linebuf; /* printf("\nold_size: (%u, %u) new_size: (%u, %u)\n", self->columns, self->lines, columns, lines); */ self->lines = lines; self->columns = columns; self->margin_top = 0; self->margin_bottom = self->lines - 1; diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index 1ff997a0d25..50c61850d38 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -314,28 +314,25 @@ def no_url(t): self.ae(l4.url_end_at(0), len(l4) - 2) self.ae(l4.url_end_at(0, 0, True), len(l4) - 1) - def rewrap(self, lb, lb2): - hb = HistoryBuf(lb2.ynum, lb2.xnum) - cy = lb.rewrap(lb2, hb) - return hb, cy[1] + def rewrap(self, lb, lines, columns): + return lb.rewrap(lines, columns) def test_rewrap_simple(self): ' Same width buffers ' lb = filled_line_buf(5, 5) lb2 = LineBuf(lb.ynum, lb.xnum) - self.rewrap(lb, lb2) + lb2 = self.rewrap(lb, lb.ynum, lb.xnum)[0] for i in range(lb.ynum): self.ae(lb2.line(i), lb.line(i)) - lb2 = LineBuf(8, 5) - cy = self.rewrap(lb, lb2)[1] + lb2, _, cy = self.rewrap(lb, 8, 5) self.ae(cy, 5) for i in range(lb.ynum): - self.ae(lb2.line(i), lb.line(i)) + self.ae(lb2.line(i), lb.line(i), i) empty = LineBuf(1, lb2.xnum) for i in range(lb.ynum, lb2.ynum): self.ae(str(lb2.line(i)), str(empty.line(0))) lb2 = LineBuf(3, 5) - cy = self.rewrap(lb, lb2)[1] + lb2, _, cy = self.rewrap(lb, 3, 5) self.ae(cy, 3) for i in range(lb2.ynum): self.ae(lb2.line(i), lb.line(i + 2)) @@ -348,8 +345,7 @@ def line_comparison(self, buf, *lines): self.ae(l0, str(l2)) def line_comparison_rewrap(self, lb, *lines): - lb2 = LineBuf(len(lines), max(map(len, lines))) - self.rewrap(lb, lb2) + lb2 = self.rewrap(lb, len(lines), max(map(len, lines)))[0] self.line_comparison(lb2, *lines) return lb2 @@ -493,40 +489,24 @@ def as_ansi(hb): hb = filled_history_buf(5, 5) for i in range(hb.ynum): hb.line(i).set_wrapped_flag(True) - hb2 = HistoryBuf(3, 10) before = as_ansi(hb) - hb.rewrap(hb2) + hb2 = hb.rewrap(10) self.ae(before, as_ansi(hb2).rstrip()) hb = filled_history_buf(5, 5) - hb2 = HistoryBuf(hb.ynum, hb.xnum) - hb.rewrap(hb2) + hb2 = hb.rewrap(hb.xnum) for i in range(hb.ynum): self.ae(hb2.line(i), hb.line(i)) - hb2 = HistoryBuf(8, 5) - hb.rewrap(hb2) - for i in range(hb.ynum): - self.ae(hb2.line(i), hb.line(i)) - for i in range(hb.ynum, hb2.ynum): - with self.assertRaises(IndexError): - hb2.line(i) - hb2 = HistoryBuf(3, 5) - hb.rewrap(hb2) - for i in range(hb2.ynum): - self.ae(hb2.line(i), hb.line(i)) - self.ae(hb2.dirty_lines(), list(range(hb2.ynum))) hb = filled_history_buf(5, 5) - hb2 = HistoryBuf(hb.ynum, hb.xnum * 2) - hb.rewrap(hb2) + hb2 = hb.rewrap(hb.xnum * 2) hb3 = HistoryBuf(hb.ynum, hb.xnum) - hb2.rewrap(hb3) + hb3 = hb2.rewrap(hb.xnum) for i in range(hb.ynum): self.ae(hb.line(i), hb3.line(i)) hb2 = HistoryBuf(hb.ynum, hb.xnum) - large_hb.rewrap(hb2) - hb2 = HistoryBuf(large_hb.ynum, large_hb.xnum) - large_hb.rewrap(hb2) + hb2 = large_hb.rewrap(hb.xnum) + hb2.rewrap(large_hb.xnum) def test_ansi_repr(self): lb = filled_line_buf()