diff --git a/kitty/history.c b/kitty/history.c index 34a462f0bf7..f5b93f4d03a 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -8,6 +8,7 @@ #include "wcswidth.h" #include "lineops.h" #include "charsets.h" +#include "rewrap.h" #include #include "../3rdparty/ringbuf/ringbuf.h" @@ -278,9 +279,9 @@ pagerhist_push(HistoryBuf *self, ANSIBuf *as_ansi_buf) { } static index_type -historybuf_push(HistoryBuf *self, ANSIBuf *as_ansi_buf) { +historybuf_push(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *line) { index_type idx = (self->start_of_data + self->count) % self->ynum; - init_line(self, idx, self->line); + init_line(self, idx, line); if (self->count == self->ynum) { pagerhist_push(self, as_ansi_buf); self->start_of_data = (self->start_of_data + 1) % self->ynum; @@ -290,7 +291,7 @@ historybuf_push(HistoryBuf *self, ANSIBuf *as_ansi_buf) { void historybuf_add_line(HistoryBuf *self, const Line *line, ANSIBuf *as_ansi_buf) { - index_type idx = historybuf_push(self, as_ansi_buf); + index_type idx = historybuf_push(self, as_ansi_buf, self->line); copy_line(line, self->line); *attrptr(self, idx) = line->attrs; } @@ -607,17 +608,12 @@ HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns, unsigned } // }}} -#define BufType HistoryBuf - -#define map_src_index(y) ((src->start_of_data + y) % src->ynum) - -#define init_src_line(src_y) init_line(src, map_src_index(src_y), src->line); - -#define next_dest_line(cont) { history_buf_set_last_char_as_continuation(dest, 0, cont); LineAttrs *lap = attrptr(dest, historybuf_push(dest, as_ansi_buf)); *lap = src->line->attrs; } - -#define first_dest_line next_dest_line(false); - -#include "rewrap.h" +index_type +historybuf_next_dest_line(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued) { + history_buf_set_last_char_as_continuation(self, dest_y, continued); + *attrptr(self, historybuf_push(self, as_ansi_buf, dest_line)) = src_line->attrs; + return dest_y + 1; +} void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other, ANSIBuf *as_ansi_buf) { @@ -636,7 +632,7 @@ historybuf_rewrap(HistoryBuf *self, HistoryBuf *other, ANSIBuf *as_ansi_buf) { other->pagerhist->rewrap_needed = true; other->count = 0; other->start_of_data = 0; if (self->count > 0) { - rewrap_inner(self, other, self->count, NULL, NULL, as_ansi_buf); + 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; } } diff --git a/kitty/history.h b/kitty/history.h index c0a63d1f3ce..19f738883e1 100644 --- a/kitty/history.h +++ b/kitty/history.h @@ -35,3 +35,4 @@ typedef struct { HistoryBuf* alloc_historybuf(unsigned int, unsigned int, unsigned int, TextCache *tc); +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 b0c02b8ce8f..d5037f7554b 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -7,6 +7,8 @@ #include "data-types.h" #include "lineops.h" +#include "rewrap.h" + #include extern PyTypeObject Line_Type; @@ -143,13 +145,18 @@ init_line(LineBuf *lb, Line *l, index_type ynum) { l->gpu_cells = gpu_lineptr(lb, ynum); } +void +linebuf_init_line_at(LineBuf *self, index_type idx, Line *line) { + line->ynum = idx; + line->xnum = self->xnum; + line->attrs = self->line_attrs[idx]; + line->attrs.is_continued = idx > 0 ? cpu_lineptr(self, self->line_map[idx - 1])[self->xnum - 1].next_char_was_wrapped : false; + init_line(self, line, self->line_map[idx]); +} + void linebuf_init_line(LineBuf *self, index_type idx) { - self->line->ynum = idx; - self->line->xnum = self->xnum; - self->line->attrs = self->line_attrs[idx]; - self->line->attrs.is_continued = idx > 0 ? cpu_lineptr(self, self->line_map[idx - 1])[self->xnum - 1].next_char_was_wrapped : false; - init_line(self, self->line, self->line_map[idx]); + linebuf_init_line_at(self, idx, self->line); } void @@ -584,8 +591,6 @@ copy_old(LineBuf *self, PyObject *y) { Py_RETURN_NONE; } -#include "rewrap.h" - static index_type restitch(Line *dest, index_type at, LineBuf *src, const index_type src_y, TrackCursor *cursors) { index_type y = src_y, num_of_lines_removed = 0, num_cells_removed_on_last_line = 0; @@ -697,10 +702,9 @@ linebuf_rewrap( } *num_content_lines_before = first + 1; TrackCursor tcarr[3] = {{.x = *track_x, .y = *track_y }, {.x = *track_x2, .y = *track_y2}, {.is_sentinel = true}}; - rewrap_inner(self, other, *num_content_lines_before, historybuf, (TrackCursor*)tcarr, as_ansi_buf); + *num_content_lines_after = 1 + linebuf_rewrap_inner(self, other, *num_content_lines_before, historybuf, (TrackCursor*)tcarr, as_ansi_buf); *track_x = tcarr[0].x; *track_y = tcarr[0].y; *track_x2 = tcarr[1].x; *track_y2 = tcarr[1].y; - *num_content_lines_after = other->line->ynum + 1; if (history_buf_last_line_is_split && historybuf) { historybuf_init_line(historybuf, 0, historybuf->line); index_type xlimit = xlimit_for_line(historybuf->line); diff --git a/kitty/lineops.h b/kitty/lineops.h index f354868e0ea..9afbb3aa658 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -80,6 +80,7 @@ PyObject* unicode_in_range(const Line *self, const index_type start, const index PyObject* line_as_unicode(Line *, bool); void linebuf_init_line(LineBuf *, index_type); +void linebuf_init_line_at(LineBuf *, index_type, Line*); void linebuf_init_cells(LineBuf *lb, index_type ynum, CPUCell **c, GPUCell **g); CPUCell* linebuf_cpu_cells_for_line(LineBuf *lb, index_type idx); void linebuf_clear(LineBuf *, char_type ch); diff --git a/kitty/rewrap.c b/kitty/rewrap.c new file mode 100644 index 00000000000..b9107f17a1b --- /dev/null +++ b/kitty/rewrap.c @@ -0,0 +1,173 @@ +/* + * 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; + index_type src_y, src_x, dest_x, dest_y, num, src_x_limit; + init_line_func_t init_line; + first_dest_line_func_t first_dest_line; + next_dest_line_func_t next_dest_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 +init_line(TextCache *tc, index_type xnum, Line *l) { + l->text_cache = tc; + l->xnum = xnum; + +} + +static index_type +rewrap_inner(Rewrap r) { + init_line(r.text_cache, r.src_xnum, &r.src); init_line(r.text_cache, r.dest_xnum, &r.dest); + static TrackCursor tc_end = {.is_sentinel = true }; + if (!r.cursors) r.cursors = &tc_end; + bool is_first_line = true, src_line_is_continued = false; + while (r.src_y < r.src_limit) { + for (TrackCursor *t = r.cursors; !t->is_sentinel; t++) t->is_tracked_line = r.src_y == t->y; + r.init_line(r.src_buf, r.src_y, &r.src); + r.src_x_limit = r.src.xnum; + if (!src_line_is_continued) { + r.dest_x = 0; + if (is_first_line) { + is_first_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, false); + } + } + src_line_is_continued = r.src.cpu_cells[r.src.xnum-1].next_char_was_wrapped; + if (!src_line_is_continued) { + // Trim trailing blanks since there is a hard line break at the end of this line + while(r.src_x_limit && r.src.cpu_cells[r.src_x_limit - 1].ch_and_idx == BLANK_CHAR) r.src_x_limit--; + } else { + r.src.cpu_cells[r.src.xnum-1].next_char_was_wrapped = false; + } + if (r.src_x_limit) { + for (TrackCursor *t = r.cursors; !t->is_sentinel; t++) { + if (t->is_tracked_line && t->x >= r.src_x_limit) t->x = MAX(1u, r.src_x_limit) - 1; + } + while (r.src_x < r.src_x_limit) { + if (r.dest_x >= r.dest.xnum) { + r.dest_x = 0; + r.dest_y = r.next_dest_line(r.dest_buf, r.historybuf, r.as_ansi_buf, &r.src, r.dest_y, &r.dest, true); + } + index_type num = MIN(r.src.xnum - r.src_x, r.dest.xnum - r.dest_x); + copy_range(&r.src, r.src_x, &r.dest, r.dest_x, num); + for (TrackCursor *t = r.cursors; !t->is_sentinel; t++) { + if (t->is_tracked_line && r.src_x <= t->x && t->x < r.src_x + num) { + t->y = r.dest_y; + t->x = r.dest_x + (t->x - r.src_x + (t->x > 0)); + } + } + r.src_x += num; r.dest_x += num; + } + } + r.src_y++; r.src_x = 0; + } + 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) { + 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, + + .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) { + 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, + + .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 index 10c089f36d0..849ae490b77 100644 --- a/kitty/rewrap.h +++ b/kitty/rewrap.h @@ -6,46 +6,8 @@ */ #pragma once - -#ifndef BufType -#define BufType LineBuf -#endif - -#ifndef init_src_line -#define init_src_line(src_y) linebuf_init_line(src, src_y); -#endif - -#define set_dest_line_attrs(dest_y) dest->line_attrs[dest_y] = src->line->attrs; src->line->attrs.prompt_kind = UNKNOWN_PROMPT_KIND; - -#ifndef first_dest_line -#define first_dest_line linebuf_init_line(dest, 0); set_dest_line_attrs(0) -#endif - -#ifndef next_dest_line -#define next_dest_line(continued) \ - 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(dest, dest_y); \ - set_dest_line_attrs(dest_y); -#endif - -#ifndef is_src_line_continued -#define is_src_line_continued() (src->line->cpu_cells[src->xnum-1].next_char_was_wrapped) -#endif - -static inline 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)); -} +#include "line-buf.h" +#include "history.h" typedef struct TrackCursor { index_type x, y; @@ -53,44 +15,7 @@ typedef struct TrackCursor { } TrackCursor; -static void -rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf UNUSED *historybuf, TrackCursor *track, ANSIBuf *as_ansi_buf) { - bool is_first_line = true; - index_type src_y = 0, src_x = 0, dest_x = 0, dest_y = 0, num = 0, src_x_limit = 0; - TrackCursor tc_end = {.is_sentinel = true }; - if (!track) track = &tc_end; +index_type linebuf_rewrap_inner(LineBuf *src, LineBuf *dest, const index_type src_limit, HistoryBuf *historybuf, TrackCursor *track, ANSIBuf *as_ansi_buf); + +index_type historybuf_rewrap_inner(HistoryBuf *src, HistoryBuf *dest, const index_type src_limit, ANSIBuf *as_ansi_buf); - do { - for (TrackCursor *t = track; !t->is_sentinel; t++) t->is_tracked_line = src_y == t->y; - init_src_line(src_y); - const bool src_line_is_continued = is_src_line_continued(); - src_x_limit = src->xnum; - if (!src_line_is_continued) { - // Trim trailing blanks since there is a hard line break at the end of this line - while(src_x_limit && src->line->cpu_cells[src_x_limit - 1].ch_or_idx == BLANK_CHAR && !src->line->cpu_cells[src_x_limit - 1].ch_is_idx) src_x_limit--; - } else { - src->line->cpu_cells[src->xnum-1].next_char_was_wrapped = false; - } - for (TrackCursor *t = track; !t->is_sentinel; t++) { - if (t->is_tracked_line && t->x >= src_x_limit) t->x = MAX(1u, src_x_limit) - 1; - } - if (is_first_line) { - first_dest_line; is_first_line = false; - } - while (src_x < src_x_limit) { - if (dest_x >= dest->xnum) { next_dest_line(true); dest_x = 0; } - num = MIN(src->line->xnum - src_x, dest->xnum - dest_x); - copy_range(src->line, src_x, dest->line, dest_x, num); - for (TrackCursor *t = track; !t->is_sentinel; t++) { - if (t->is_tracked_line && src_x <= t->x && t->x < src_x + num) { - t->y = dest_y; - t->x = dest_x + (t->x - src_x + (t->x > 0)); - } - } - src_x += num; dest_x += num; - } - src_y++; src_x = 0; - if (!src_line_is_continued && src_y < src_limit) { init_src_line(src_y); next_dest_line(false); dest_x = 0; } - } while (src_y < src_limit); - dest->line->ynum = dest_y; -}