mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-06-28 11:51:48 +00:00
Move rewrap code into its own module
Makes it easier to modify in the future when adding multicell support
This commit is contained in:
parent
5b0e1b5b5c
commit
b450e71b92
6 changed files with 204 additions and 104 deletions
|
|
@ -8,6 +8,7 @@
|
|||
#include "wcswidth.h"
|
||||
#include "lineops.h"
|
||||
#include "charsets.h"
|
||||
#include "rewrap.h"
|
||||
#include <structmember.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "data-types.h"
|
||||
#include "lineops.h"
|
||||
#include "rewrap.h"
|
||||
|
||||
#include <structmember.h>
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
173
kitty/rewrap.c
Normal file
173
kitty/rewrap.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue