mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-08-31 06:18:17 +00:00
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.
This commit is contained in:
parent
97449dfddb
commit
561686c9d4
10 changed files with 437 additions and 476 deletions
|
|
@ -8,7 +8,7 @@
|
|||
#include "wcswidth.h"
|
||||
#include "lineops.h"
|
||||
#include "charsets.h"
|
||||
#include "rewrap.h"
|
||||
#include "resize.h"
|
||||
#include <structmember.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include "data-types.h"
|
||||
#include "lineops.h"
|
||||
#include "rewrap.h"
|
||||
#include "resize.h"
|
||||
|
||||
#include <structmember.h>
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
317
kitty/resize.c
Normal file
317
kitty/resize.c
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* resize.c
|
||||
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
25
kitty/resize.h
Normal file
25
kitty/resize.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* resize.h
|
||||
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* 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);
|
||||
305
kitty/rewrap.c
305
kitty/rewrap.c
|
|
@ -1,305 +0,0 @@
|
|||
/*
|
||||
* rewrap.c
|
||||
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* rewrap.h
|
||||
* Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* 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);
|
||||
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
#include <stdalign.h>
|
||||
#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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue