Skip to content

Commit

Permalink
Move rewrap code into its own module
Browse files Browse the repository at this point in the history
Makes it easier to modify in the future when adding multicell support
  • Loading branch information
kovidgoyal committed Dec 27, 2024
1 parent 7400c10 commit ba833d0
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 104 deletions.
26 changes: 11 additions & 15 deletions kitty/history.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "wcswidth.h"
#include "lineops.h"
#include "charsets.h"
#include "rewrap.h"
#include <structmember.h>
#include "../3rdparty/ringbuf/ringbuf.h"

Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
}
Expand Down
1 change: 1 addition & 0 deletions kitty/history.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
22 changes: 13 additions & 9 deletions kitty/line-buf.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "data-types.h"
#include "lineops.h"
#include "rewrap.h"

#include <structmember.h>

extern PyTypeObject Line_Type;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions kitty/lineops.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
173 changes: 173 additions & 0 deletions kitty/rewrap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* 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;
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);
}
85 changes: 5 additions & 80 deletions kitty/rewrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,91 +6,16 @@
*/

#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;
bool is_tracked_line, is_sentinel;
} 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;
}

0 comments on commit ba833d0

Please sign in to comment.