Start work on multicell support for selection_as_text

This commit is contained in:
Kovid Goyal
2025-01-10 14:35:32 +05:30
parent ac32f91a2e
commit 8156a7520f
7 changed files with 121 additions and 74 deletions

View File

@@ -19,7 +19,6 @@ typedef enum {
FREETYPE_CLEANUP_FUNC,
SYSTEMD_CLEANUP_FUNC,
SHADERS_CLEANUP_FUNC,
LINE_CLEANUP_FUNC,
NUM_CLEANUP_FUNCS
} AtExitCleanupFunc;

View File

@@ -268,7 +268,7 @@ pagerhist_push(HistoryBuf *self, ANSIBuf *as_ansi_buf) {
Line l = {.xnum=self->xnum, .text_cache=self->text_cache};
init_line(self, self->start_of_data, &l);
ANSILineState s = {.output_buf=as_ansi_buf};
line_as_ansi(&l, &s, 0, l.xnum, 0);
line_as_ansi(&l, &s, 0, l.xnum, 0, true);
pagerhist_write_bytes(ph, (const uint8_t*)"\x1b[m", 3);
if (pagerhist_write_ucs4(ph, as_ansi_buf->buf, as_ansi_buf->len)) {
char line_end[2]; size_t num = 0;
@@ -320,9 +320,10 @@ static PyObject*
__str__(HistoryBuf *self) {
PyObject *lines = PyTuple_New(self->count);
if (lines == NULL) return PyErr_NoMemory();
RAII_ANSIBuf(buf);
for (index_type i = 0; i < self->count; i++) {
init_line(self, index_of(self, i), self->line);
PyObject *t = line_as_unicode(self->line, false);
PyObject *t = line_as_unicode(self->line, false, &buf);
if (t == NULL) { Py_CLEAR(lines); return NULL; }
PyTuple_SET_ITEM(lines, i, t);
}
@@ -350,7 +351,7 @@ as_ansi(HistoryBuf *self, PyObject *callback) {
ANSIBuf output = {0}; ANSILineState s = {.output_buf=&output};
for(unsigned int i = 0; i < self->count; i++) {
init_line(self, i, &l);
line_as_ansi(&l, &s, 0, l.xnum, 0);
line_as_ansi(&l, &s, 0, l.xnum, 0, true);
if (!l.cpu_cells[l.xnum - 1].next_char_was_wrapped) {
ensure_space_for(&output, buf, Py_UCS4, output.len + 1, capacity, 2048, false);
output.buf[output.len++] = '\n';

View File

@@ -470,7 +470,7 @@ as_ansi(LineBuf *self, PyObject *callback) {
ANSIBuf output = {0}; ANSILineState s = {.output_buf=&output};
do {
init_line(self, &l, self->line_map[ylimit]);
line_as_ansi(&l, &s, 0, l.xnum, 0);
line_as_ansi(&l, &s, 0, l.xnum, 0, true);
if (output.len) break;
ylimit--;
} while(ylimit > 0);
@@ -478,7 +478,7 @@ as_ansi(LineBuf *self, PyObject *callback) {
for(index_type i = 0; i <= ylimit; i++) {
bool output_newline = !linebuf_line_ends_with_continuation(self, i);
init_line(self, &l, self->line_map[i]);
line_as_ansi(&l, &s, 0, l.xnum, 0);
line_as_ansi(&l, &s, 0, l.xnum, 0, true);
if (output_newline) {
ensure_space_for(&output, buf, Py_UCS4, output.len + 1, capacity, 2048, false);
output.buf[output.len++] = 10; // 10 = \n
@@ -515,10 +515,11 @@ as_text(LineBuf *self, PyObject *args) {
static PyObject*
__str__(LineBuf *self) {
RAII_PyObject(lines, PyTuple_New(self->ynum));
RAII_ANSIBuf(buf);
if (lines == NULL) return PyErr_NoMemory();
for (index_type i = 0; i < self->ynum; i++) {
init_line(self, self->line, self->line_map[i]);
PyObject *t = line_as_unicode(self->line, false);
PyObject *t = line_as_unicode(self->line, false, &buf);
if (t == NULL) return NULL;
PyTuple_SET_ITEM(lines, i, t);
}

View File

@@ -5,8 +5,6 @@
* Distributed under terms of the GPL3 license.
*/
#include "cleanup.h"
#define EXTRA_INIT register_at_exit_cleanup_func(LINE_CLEANUP_FUNC, cleanup_module);
#include "state.h"
#include "unicode-data.h"
@@ -109,9 +107,9 @@ multicell_is_continuation_of_previous(const CPUCell *prev, const CPUCell *curr)
}
static void
text_in_cell_ansi(ANSILineState *s, const CPUCell *c, TextCache *tc) {
text_in_cell_ansi(ANSILineState *s, const CPUCell *c, TextCache *tc, bool skip_multiline_non_zero_lines) {
if (c->is_multicell) {
if (c->x || c->y) return;
if (c->x || (skip_multiline_non_zero_lines && c->y)) return;
if (s->current_multicell_state) {
if (!multicell_is_continuation_of_previous(s->current_multicell_state, c)) {
close_multicell(s);
@@ -364,42 +362,43 @@ cell_as_utf8_for_fallback(const ListOfChars *lc, char *buf) {
return n;
}
static ListOfChars global_unicode_in_range_buf = {0};
PyObject*
unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const bool add_trailing_newline, const bool skip_zero_cells) {
size_t n = 0;
bool
unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const bool add_trailing_newline, const bool skip_zero_cells, bool skip_multiline_non_zero_lines, ANSIBuf *buf) {
ListOfChars lc;
for (index_type i = start; i < limit; i++) {
lc.chars = global_unicode_in_range_buf.chars + n; lc.capacity = global_unicode_in_range_buf.capacity - n;
lc.chars = buf->buf + buf->len; lc.capacity = buf->capacity - buf->len;
while (!text_in_cell_without_alloc(self->cpu_cells + i, self->text_cache, &lc)) {
size_t ns = MAX(4096u, 2 * global_unicode_in_range_buf.capacity);
char_type *np = realloc(global_unicode_in_range_buf.chars, ns);
if (!np) return PyErr_NoMemory();
global_unicode_in_range_buf.capacity = ns; global_unicode_in_range_buf.chars = np;
lc.chars = global_unicode_in_range_buf.chars + n; lc.capacity = global_unicode_in_range_buf.capacity - n;
size_t ns = MAX(4096u, 2 * buf->capacity);
char_type *np = realloc(buf->buf, ns);
if (!np) return false;
buf->capacity = ns; buf->buf = np;
lc.chars = buf->buf + buf->len; lc.capacity = buf->capacity - buf->len;
}
if (self->cpu_cells[i].is_multicell && (self->cpu_cells[i].x || self->cpu_cells[i].y)) continue;
if (self->cpu_cells[i].is_multicell && (self->cpu_cells[i].x || (skip_multiline_non_zero_lines && self->cpu_cells[i].y))) continue;
if (!lc.chars[0]) {
if (skip_zero_cells) continue;
lc.chars[0] = ' ';
}
if (lc.chars[0] == '\t') {
n++;
buf->len++;
unsigned num_cells_to_skip_for_tab = lc.count > 1 ? lc.chars[1] : 0;
while (num_cells_to_skip_for_tab && i + 1 < limit && cell_is_char(self->cpu_cells+i+1, ' ')) {
i++;
num_cells_to_skip_for_tab--;
}
} else n += include_cc ? lc.count : 1;
} else buf->len += include_cc ? lc.count : 1;
}
if (add_trailing_newline && !self->cpu_cells[self->xnum-1].next_char_was_wrapped && n < global_unicode_in_range_buf.capacity) global_unicode_in_range_buf.chars[n++] = '\n';
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, global_unicode_in_range_buf.chars, n);
if (add_trailing_newline && !self->cpu_cells[self->xnum-1].next_char_was_wrapped && buf->len < buf->capacity) buf->buf[buf->len++] = '\n';
return true;
}
PyObject *
line_as_unicode(Line* self, bool skip_zero_cells) {
return unicode_in_range(self, 0, xlimit_for_line(self), true, false, skip_zero_cells);
line_as_unicode(Line* self, bool skip_zero_cells, ANSIBuf *buf) {
size_t before = buf->len;
if (!unicode_in_range(self, 0, xlimit_for_line(self), true, false, skip_zero_cells, true, buf)) return PyErr_NoMemory();
PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf->buf + before, buf->len - before);
buf->len = before;
return ans;
}
static PyObject*
@@ -485,7 +484,7 @@ write_mark_to_ansi_buf(ANSILineState *s, const char *m) {
}
bool
line_as_ansi(Line *self, ANSILineState *s, index_type start_at, index_type stop_before, char_type prefix_char) {
line_as_ansi(Line *self, ANSILineState *s, index_type start_at, index_type stop_before, char_type prefix_char, bool skip_multiline_non_zero_lines) {
s->output_buf->len = 0;
s->limit = MIN(stop_before, xlimit_for_line(self));
s->current_multicell_state = NULL;
@@ -520,7 +519,7 @@ line_as_ansi(Line *self, ANSILineState *s, index_type start_at, index_type stop_
if (*sgr) write_sgr_to_ansi_buf(s, sgr);
}
text_in_cell_ansi(s, self->cpu_cells + s->pos, self->text_cache);
text_in_cell_ansi(s, self->cpu_cells + s->pos, self->text_cache, skip_multiline_non_zero_lines);
s->prev_gpu_cell = cell;
}
close_multicell(s);
@@ -533,7 +532,7 @@ static PyObject*
as_ansi(Line* self, PyObject *a UNUSED) {
#define as_ansi_doc "Return the line's contents with ANSI (SGR) escape codes for formatting"
ANSIBuf output = {0}; ANSILineState s = {.output_buf=&output};
line_as_ansi(self, &s, 0, self->xnum, 0);
line_as_ansi(self, &s, 0, self->xnum, 0, true);
PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output.buf, output.len);
free(output.buf);
return ans;
@@ -554,7 +553,8 @@ set_wrapped_flag(Line* self, PyObject *is_wrapped) {
static PyObject*
__repr__(Line* self) {
PyObject *s = line_as_unicode(self, false);
RAII_ANSIBuf(buf);
PyObject *s = line_as_unicode(self, false, &buf);
if (s == NULL) return NULL;
PyObject *ans = PyObject_Repr(s);
Py_CLEAR(s);
@@ -563,7 +563,8 @@ __repr__(Line* self) {
static PyObject*
__str__(Line* self) {
return line_as_unicode(self, false);
RAII_ANSIBuf(buf);
return line_as_unicode(self, false, &buf);
}
@@ -938,12 +939,12 @@ apply_marker(PyObject *marker, Line *line, const PyObject *text) {
}
void
mark_text_in_line(PyObject *marker, Line *line) {
mark_text_in_line(PyObject *marker, Line *line, ANSIBuf *buf) {
if (!marker) {
for (index_type i = 0; i < line->xnum; i++) line->gpu_cells[i].attrs.mark = 0;
return;
}
PyObject *text = line_as_unicode(line, false);
PyObject *text = line_as_unicode(line, false, buf);
if (PyUnicode_GET_LENGTH(text) > 0) {
apply_marker(marker, line, text);
} else {
@@ -978,11 +979,11 @@ as_text_generic(PyObject *args, void *container, get_line_func get_line, index_t
// makes writing pagers easier.
// see https://github.com/kovidgoyal/kitty/issues/2381
s.prev_gpu_cell = NULL;
line_as_ansi(line, &s, 0, line->xnum, 0);
line_as_ansi(line, &s, 0, line->xnum, 0, true);
t = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, ansibuf->buf, ansibuf->len);
if (t && ansibuf->len > 0) APPEND(sgr_reset);
} else {
t = line_as_unicode(line, false);
t = line_as_unicode(line, false, ansibuf);
}
APPEND_AND_DECREF(t);
if (insert_wrap_markers) APPEND(cr);
@@ -1064,12 +1065,6 @@ Line *alloc_line(TextCache *tc) {
return ans;
}
static void
cleanup_module(void) {
free(global_unicode_in_range_buf.chars);
global_unicode_in_range_buf = (ListOfChars){0};
}
RICHCMP(Line)
INIT_TYPE(Line)
// }}}

View File

@@ -108,6 +108,8 @@ typedef struct ANSILineOutput {
bool escape_code_written;
} ANSILineState;
static inline void cleanup_ansibuf(ANSIBuf *b) { free(b->buf); zero_at_ptr(b); }
#define RAII_ANSIBuf(name) __attribute__((cleanup(cleanup_ansibuf))) ANSIBuf name = {0}
Line* alloc_line(TextCache *text_cache);
void apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, int *params, unsigned int count, bool is_group);

View File

@@ -72,12 +72,12 @@ char_type line_get_char(Line *self, index_type at);
index_type line_url_start_at(Line *self, index_type x);
index_type line_url_end_at(Line *self, index_type x, bool, char_type, bool, bool, index_type);
bool line_startswith_url_chars(Line*, bool);
bool line_as_ansi(Line *self, ANSILineState *s, index_type start_at, index_type stop_before, char_type prefix_char) __attribute__((nonnull));
bool line_as_ansi(Line *self, ANSILineState *s, index_type start_at, index_type stop_before, char_type prefix_char, bool skip_multiline_non_zero_lines) __attribute__((nonnull));
unsigned int line_length(Line *self);
size_t cell_as_unicode_for_fallback(const ListOfChars *lc, Py_UCS4 *buf);
size_t cell_as_utf8_for_fallback(const ListOfChars *lc, char *buf);
PyObject* unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const bool add_trailing_newline, const bool skip_zero_cells);
PyObject* line_as_unicode(Line *, bool);
bool unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const bool add_trailing_newline, const bool skip_zero_cells, bool skip_multiline_non_zero_lines, ANSIBuf*);
PyObject* line_as_unicode(Line *, bool, ANSIBuf*);
void linebuf_init_line(LineBuf *, index_type);
void linebuf_init_line_at(LineBuf *, index_type, Line*);
@@ -109,7 +109,7 @@ void historybuf_mark_line_dirty(HistoryBuf *self, index_type y);
void historybuf_set_line_has_image_placeholders(HistoryBuf *self, index_type y, bool val);
void historybuf_refresh_sprite_positions(HistoryBuf *self);
void historybuf_clear(HistoryBuf *self);
void mark_text_in_line(PyObject *marker, Line *line);
void mark_text_in_line(PyObject *marker, Line *line, ANSIBuf *buf);
bool line_has_mark(Line *, uint16_t mark);
PyObject* as_text_generic(PyObject *args, void *container, get_line_func get_line, index_type lines, ANSIBuf *ansibuf, bool add_trailing_newline);
bool colors_for_cell(Line *self, const ColorProfile *cp, index_type *x, color_type *fg, color_type *bg, bool *reversed);

View File

@@ -601,12 +601,18 @@ zero_cells(text_loop_state *s, CPUCell *c, GPUCell *g) { *c = s->cc; *g = s->g;
typedef Line*(linefunc_t)(Screen*, int);
static void
init_line_(Screen *self, index_type y, Line *line) {
linebuf_init_line_at(self->linebuf, y, line);
if (y == 0 && self->linebuf == self->main_linebuf) {
if (history_buf_endswith_wrap(self->historybuf)) line->attrs.is_continued = true;
}
}
static Line*
init_line(Screen *self, index_type y) {
linebuf_init_line(self->linebuf, y);
if (y == 0 && self->linebuf == self->main_linebuf) {
if (history_buf_endswith_wrap(self->historybuf)) self->linebuf->line->attrs.is_continued = true;
}
init_line_(self, y, self->linebuf->line);
return self->linebuf->line;
}
@@ -632,6 +638,13 @@ range_line_(Screen *self, int y) {
return init_line(self, y);
}
static void
range_line(Screen *self, int y, Line *line) {
if (y < 0) historybuf_init_line(self->historybuf, -(y + 1), line);
else init_line_(self, y, line);
}
static Line*
checked_range_line(Screen *self, int y) {
if (
@@ -3189,7 +3202,8 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
if (linebuf->line->attrs.has_dirty_text) {
render_line(fonts_data, linebuf->line, y, &self->paused_rendering.cursor, self->disable_ligatures, self->lc);
screen_render_line_graphics(self, linebuf->line, y);
if (linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(self->marker, linebuf->line);
if (linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(
self->marker, linebuf->line, &self->as_ansi_buf);
linebuf_mark_line_clean(linebuf, y);
}
update_line_data(linebuf->line, y, address);
@@ -3213,7 +3227,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
screen_render_line_graphics(self, self->historybuf->line, y - self->scrolled_by);
if (self->historybuf->line->attrs.has_dirty_text) {
render_line(fonts_data, self->historybuf->line, lnum, self->cursor, self->disable_ligatures, self->lc);
if (screen_has_marker(self)) mark_text_in_line(self->marker, self->historybuf->line);
if (screen_has_marker(self)) mark_text_in_line(self->marker, self->historybuf->line, &self->as_ansi_buf);
historybuf_mark_line_clean(self->historybuf, lnum);
}
update_line_data(self->historybuf->line, y, address);
@@ -3225,7 +3239,8 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
(cursor_has_moved && (self->cursor->y == lnum || self->last_rendered.cursor_y == lnum))) {
render_line(fonts_data, self->linebuf->line, lnum, self->cursor, self->disable_ligatures, self->lc);
screen_render_line_graphics(self, self->linebuf->line, y - self->scrolled_by);
if (self->linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(self->marker, self->linebuf->line);
if (self->linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(
self->marker, self->linebuf->line, &self->as_ansi_buf);
if (is_overlay_active && lnum == self->overlay_line.ynum) render_overlay_line(self, self->linebuf->line, fonts_data);
linebuf_mark_line_clean(self->linebuf, lnum);
}
@@ -3459,30 +3474,61 @@ static PyObject*
text_for_range(Screen *self, const Selection *sel, bool insert_newlines, bool strip_trailing_whitespace) {
IterationData idata;
iteration_data(sel, &idata, self->columns, -self->historybuf->count, 0);
int limit = MIN((int)self->lines, idata.y_limit);
PyObject *ans = PyTuple_New(limit - idata.y);
Line line = {.xnum=self->columns, .text_cache=self->text_cache}, next_line = line; XRange xr, xrn = {0}; CPUCell *c;
size_t before = self->as_ansi_buf.len;
const int limit = MIN((int)self->lines, idata.y_limit);
if (idata.y >= limit) return PyTuple_New(0);
RAII_PyObject(ans, PyTuple_New(limit - idata.y));
if (!ans) return NULL;
range_line(self, idata.y, &next_line);
xrn = xrange_for_iteration_with_multicells(&idata, idata.y, &next_line);
for (int i = 0, y = idata.y; y < limit; y++, i++) {
Line *line = range_line_(self, y);
XRange xr = xrange_for_iteration(&idata, y, line);
const bool is_first_line = y == idata.y, is_last_line = y + 1 >= limit;
xr = xrn;
line = next_line;
index_type x_limit = xr.x_limit;
bool is_only_whitespace_line = false;
if (strip_trailing_whitespace) {
index_type new_limit = limit_without_trailing_whitespace(line, x_limit);
index_type new_limit = limit_without_trailing_whitespace(&line, x_limit);
if (new_limit != x_limit) {
x_limit = new_limit;
if (!x_limit) {
PyObject *text = PyUnicode_FromString("\n");
if (text == NULL) { Py_DECREF(ans); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(ans, i, text);
continue;
is_only_whitespace_line = true;
}
}
const bool add_trailing_newline = insert_newlines && !is_last_line;
self->as_ansi_buf.len = before;
if (!is_last_line) {
range_line(self, y + 1, &next_line);
xrn = xrange_for_iteration_with_multicells(&idata, y+1, &next_line);
for (index_type x = 0; x < xr.x; x++) {
if ((c = &line.cpu_cells[x])->is_multicell && c->scale > 1 && c->y + 1 < c->scale && x <= xrn.x) {
index_type mc_limit = x + mcd_x_limit(c);
if (!unicode_in_range(&line, x, mc_limit, true, false, false, false, &self->as_ansi_buf)) return PyErr_NoMemory();
x = mc_limit - 1;
}
}
}
PyObject *text = unicode_in_range(line, xr.x, x_limit, true, insert_newlines && y != limit-1, false);
if (text == NULL) { Py_DECREF(ans); return PyErr_NoMemory(); }
PyObject *text = NULL;
if (x_limit <= xr.x && is_only_whitespace_line) { // we want a newline on only whitespace lines even if they are continued
text = add_trailing_newline ? PyUnicode_FromString("\n") : PyUnicode_FromString("");
} else {
if (!unicode_in_range(&line, xr.x, x_limit, true, add_trailing_newline, false, !is_first_line, &self->as_ansi_buf)) return PyErr_NoMemory();
if (!is_last_line) {
for (index_type x = x_limit; x < MIN(line.xnum, xrn.x_limit); x++) {
if ((c = &line.cpu_cells[x])->is_multicell && c->scale > 1 && c->y + 1 < c->scale) {
index_type mc_limit = x + mcd_x_limit(c);
if (!unicode_in_range(&line, x, mc_limit, true, false, false, false, &self->as_ansi_buf)) return PyErr_NoMemory();
x = mc_limit - 1;
}
}
}
text = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, self->as_ansi_buf.buf + before, self->as_ansi_buf.len - before);
}
if (!text) return NULL;
PyTuple_SET_ITEM(ans, i, text);
}
return ans;
self->as_ansi_buf.len = before;
return Py_NewRef(ans);
}
static PyObject*
@@ -3499,7 +3545,7 @@ ansi_for_range(Screen *self, const Selection *sel, bool insert_newlines, bool st
ANSILineState s = {.output_buf=&output};
for (int i = 0, y = idata.y; y < limit; y++, i++) {
Line *line = range_line_(self, y);
XRange xr = xrange_for_iteration(&idata, y, line);
XRange xr = xrange_for_iteration_with_multicells(&idata, y, line);
output.len = 0;
char_type prefix_char = need_newline ? '\n' : 0;
index_type x_limit = xr.x_limit;
@@ -3513,7 +3559,10 @@ ansi_for_range(Screen *self, const Selection *sel, bool insert_newlines, bool st
}
}
}
if (line_as_ansi(line, &s, xr.x, x_limit, prefix_char)) has_escape_codes = true;
const bool is_first_line = y == idata.y;
if (is_first_line) {
}
if (line_as_ansi(line, &s, xr.x, x_limit, prefix_char, !is_first_line)) has_escape_codes = true;
need_newline = insert_newlines && !line->cpu_cells[line->xnum-1].next_char_was_wrapped;
PyObject *t = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output.buf, output.len);
if (!t) return NULL;
@@ -4928,15 +4977,15 @@ static void
screen_mark_all(Screen *self) {
for (index_type y = 0; y < self->main_linebuf->ynum; y++) {
linebuf_init_line(self->main_linebuf, y);
mark_text_in_line(self->marker, self->main_linebuf->line);
mark_text_in_line(self->marker, self->main_linebuf->line, &self->as_ansi_buf);
}
for (index_type y = 0; y < self->alt_linebuf->ynum; y++) {
linebuf_init_line(self->alt_linebuf, y);
mark_text_in_line(self->marker, self->alt_linebuf->line);
mark_text_in_line(self->marker, self->alt_linebuf->line, &self->as_ansi_buf);
}
for (index_type y = 0; y < self->historybuf->count; y++) {
historybuf_init_line(self->historybuf, y, self->historybuf->line);
mark_text_in_line(self->marker, self->historybuf->line);
mark_text_in_line(self->marker, self->historybuf->line, &self->as_ansi_buf);
}
self->is_dirty = true;
}
@@ -5164,7 +5213,7 @@ dump_line_with_attrs(Screen *self, int y, PyObject *accum) {
if (line->attrs.is_continued) call_string("continued ");
if (line->attrs.has_dirty_text) call_string("dirty ");
call_string("\n");
RAII_PyObject(t, line_as_unicode(line, false)); if (!t) return;
RAII_PyObject(t, line_as_unicode(line, false, &self->as_ansi_buf)); if (!t) return;
RAII_PyObject(r2, PyObject_CallOneArg(accum, t)); if (!r2) return;
call_string("\n");
#undef call_string