diff --git a/kitty/history.c b/kitty/history.c index e98df12b1..3de94527d 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; - } - 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; +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; } + 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 19f738883..4d397f9fc 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 01ebead61..83373f8df 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 9afbb3aa6..a8bb7de47 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 000000000..480142f2d --- /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 000000000..a5bdbd02f --- /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 94b770565..000000000 --- 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 b008ec35a..000000000 --- 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 8d485b841..3fcdeadd2 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,37 @@ 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[2].is_sentinel = true; + 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 +410,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 1ff997a0d..50c61850d 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -314,28 +314,25 @@ class TestDataTypes(BaseTest): 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 @@ class TestDataTypes(BaseTest): 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 @@ class TestDataTypes(BaseTest): 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()