Merge branch 'scrollback-fill-enlarged-window' of https://github.com/elebow/kitty

This commit is contained in:
Kovid Goyal
2021-03-17 12:22:12 +05:30
9 changed files with 121 additions and 11 deletions

View File

@@ -514,6 +514,10 @@ The current implementation stores the data in UTF-8, so approximatively
10000 lines per megabyte at 100 chars per line, for pure ASCII text, unformatted text.
A value of zero or less disables this feature. The maximum allowed size is 4GB.'''))
o('scrollback_fill_enlarged_window', False, long_text=_('''
Fill new space with lines from the scrollback buffer after enlarging a window.
'''))
o('wheel_scroll_multiplier', 5.0, long_text=_('''
Modify the amount scrolled by the mouse wheel. Note this is only used for low
precision scrolling devices, not for high precision scrolling on platforms such

View File

@@ -252,6 +252,19 @@ historybuf_add_line(HistoryBuf *self, const Line *line, ANSIBuf *as_ansi_buf) {
*attrptr(self, idx) = (line->continued & CONTINUED_MASK) | (line->has_dirty_text ? TEXT_DIRTY_MASK : 0);
}
bool
historybuf_pop_line(HistoryBuf *self, Line *line) {
if (self->count <= 0)
return false;
index_type idx = (self->start_of_data + self->count-1) % self->ynum;
init_line(self, idx, line);
self->count--;
return true;
}
static PyObject*
line(HistoryBuf *self, PyObject *val) {
#define line_doc "Return the line with line number val. This buffer grows upwards, i.e. 0 is the most recently added line"

View File

@@ -326,6 +326,15 @@ is_continued(LineBuf *self, PyObject *val) {
Py_RETURN_FALSE;
}
unsigned int
linebuf_continued_lines_count(LineBuf *self, index_type stop_at_line) {
unsigned int count = 0;
for (unsigned int i = 0; i < self->ynum && i < stop_at_line; i++)
if (self->line_attrs[i] & CONTINUED_MASK) count++;
return count;
}
void
linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom) {
index_type i;
@@ -396,6 +405,13 @@ delete_lines(LineBuf *self, PyObject *args) {
Py_RETURN_NONE;
}
void
linebuf_add_line_to_top(LineBuf *self, Line *line) {
init_line(self, self->line, self->line_map[0]);
copy_line(line, self->line);
self->line_attrs[0] = TEXT_DIRTY_MASK | (line->continued ? CONTINUED_MASK : 0);
}
static PyObject*
as_ansi(LineBuf *self, PyObject *callback) {
#define as_ansi_doc "as_ansi(callback) -> The contents of this buffer as ANSI escaped text. callback is called with each successive line."

View File

@@ -91,8 +91,10 @@ void linebuf_clear(LineBuf *, char_type ch);
void linebuf_index(LineBuf* self, index_type top, index_type bottom);
void linebuf_reverse_index(LineBuf *self, index_type top, index_type bottom);
void linebuf_clear_line(LineBuf *self, index_type y);
unsigned int linebuf_continued_lines_count(LineBuf *, index_type);
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_add_line_to_top(LineBuf *, Line *);
void linebuf_set_attribute(LineBuf *, unsigned int , unsigned int );
void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, HistoryBuf *, index_type *, index_type *, ANSIBuf*);
void linebuf_mark_line_dirty(LineBuf *self, index_type y);
@@ -101,6 +103,7 @@ void linebuf_mark_line_as_not_continued(LineBuf *self, index_type y);
unsigned int linebuf_char_width_at(LineBuf *self, index_type x, 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);
CPUCell* historybuf_cpu_cells(HistoryBuf *self, index_type num);

View File

@@ -212,6 +212,15 @@ realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, index_type *n
return ans;
}
#define INDEX_GRAPHICS(amtv) { \
bool is_main = self->linebuf == self->main_linebuf; \
static ScrollData s; \
s.amt = amtv; s.limit = is_main ? -self->historybuf->ynum : 0; \
s.has_margins = self->margin_top != 0 || self->margin_bottom != self->lines - 1; \
s.margin_top = top; s.margin_bottom = bottom; \
grman_scroll_images(self->grman, &s, self->cell_size); \
}
static bool
screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
if (self->overlay_line.is_active) deactivate_overlay_line(self);
@@ -236,6 +245,31 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
index_type x = self->cursor->x, y = self->cursor->y;
LineBuf *n = realloc_lb(self->main_linebuf, lines, columns, &num_content_lines_before, &num_content_lines_after, self->historybuf, &x, &y, &self->as_ansi_buf);
if (n == NULL) return false;
if (OPT(scrollback_fill_enlarged_window)) {
int lines_to_fill = (lines - self->main_linebuf->ynum) \
+ (linebuf_continued_lines_count(self->main_linebuf, y) - linebuf_continued_lines_count(n, y));
while (lines_to_fill > 0) {
if (self->historybuf->count <= 0) break;
unsigned int top = 0, bottom = lines-1;
linebuf_reverse_index(n, top, bottom);
y++;
INDEX_GRAPHICS(1);
Line last_history_line = {.xnum=self->historybuf->xnum};
bool line_popped = historybuf_pop_line(self->historybuf, &last_history_line);
if (!line_popped)
break;
linebuf_add_line_to_top(n, &last_history_line);
lines_to_fill--;
}
}
Py_CLEAR(self->main_linebuf); self->main_linebuf = n;
if (is_main) setup_cursor();
grman_resize(self->main_grman, self->lines, lines, self->columns, columns);
@@ -1054,15 +1088,6 @@ index_selection(const Screen *self, Selections *selections, bool up) {
}
}
#define INDEX_GRAPHICS(amtv) { \
bool is_main = self->linebuf == self->main_linebuf; \
static ScrollData s; \
s.amt = amtv; s.limit = is_main ? -self->historybuf->ynum : 0; \
s.has_margins = self->margin_top != 0 || self->margin_bottom != self->lines - 1; \
s.margin_top = top; s.margin_bottom = bottom; \
grman_scroll_images(self->grman, &s, self->cell_size); \
}
#define INDEX_UP \
if (self->overlay_line.is_active) deactivate_overlay_line(self); \
linebuf_index(self->linebuf, top, bottom); \

View File

@@ -688,6 +688,7 @@ PYWRAP1(set_options) {
S(dynamic_background_opacity, PyObject_IsTrue);
S(inactive_text_alpha, PyFloat_AsFloat);
S(scrollback_pager_history_size, PyLong_AsUnsignedLong);
S(scrollback_fill_enlarged_window, PyObject_IsTrue);
S(cursor_shape, PyLong_AsLong);
S(cursor_beam_thickness, PyFloat_AsFloat);
S(cursor_underline_thickness, PyFloat_AsFloat);

View File

@@ -32,6 +32,7 @@ typedef struct {
unsigned int terminal_select_modifiers;
unsigned int url_style;
unsigned int scrollback_pager_history_size;
bool scrollback_fill_enlarged_window;
char_type select_by_word_characters[256]; size_t select_by_word_characters_count;
color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color;
color_type mark1_foreground, mark1_background, mark2_foreground, mark2_background, mark3_foreground, mark3_background;

View File

@@ -91,10 +91,15 @@ class BaseTest(TestCase):
options = Options(merge_configs(defaults._asdict(), final_options))
set_options(options)
def create_screen(self, cols=5, lines=5, scrollback=5, cell_width=10, cell_height=20, options=None):
def create_screen(self, cols=5, lines=5, scrollback=5, cell_width=10, cell_height=20, options=None, content=tuple()):
self.set_options(options)
c = Callbacks()
return Screen(c, lines, cols, scrollback, cell_width, cell_height, 0, c)
s = Screen(c, lines, cols, scrollback, cell_width, cell_height, 0, c)
for content_line in content:
s.draw(content_line)
s.linefeed()
s.carriage_return()
return s
def assertEqualAttributes(self, c1, c2):
x1, y1, c1.x, c1.y = c1.x, c1.y, 0, 0

View File

@@ -303,6 +303,48 @@ class TestScreen(BaseTest):
s.resize(s.lines - 1, s.columns)
self.ae(x_before, s.cursor.x)
def test_scrollback_fill_after_resize(self):
def prepare_screen(content=tuple()):
return self.create_screen(
lines=5,
cols=5,
options={'scrollback_fill_enlarged_window': True},
content=content,
)
def assert_lines(lines, s):
return self.ae(lines, str(s.linebuf).split("\n"))
# Height increased, width unchanged → pull down lines to fill new space at the top
s = prepare_screen([str(i) for i in range(0, 6)])
assert_lines(['2', '3', '4', '5', ''], s)
s.resize(7, s.columns)
assert_lines(['0', '1', '2', '3', '4', '5', ''], s)
# Height increased, width increased → rewrap, pull down
s = prepare_screen(['0', '1', '2', '3' * 15])
assert_lines(['2', '33333', '33333', '33333', ''], s)
s.resize(7, 12)
assert_lines(['0', '1', '2', '333333333333', '333', '', ''], s)
# Height increased, width decreased → rewrap, pull down if possible
s = prepare_screen(['0', '1', '2', '3' * 5])
assert_lines(['0', '1', '2', '33333', ''], s)
s.resize(6, 4)
assert_lines(['0', '1', '2', '3333', '3', ''], s)
# Height unchanged, width increased → rewrap, pull down if possible
s = prepare_screen(['0', '1', '2', '3' * 15])
assert_lines(['2', '33333', '33333', '33333', ''], s)
s.resize(s.lines, 12)
assert_lines(['1', '2', '333333333333', '333', ''], s)
# Height decreased, width increased → rewrap, pull down if possible
s = prepare_screen(['0', '1', '2', '3' * 15])
assert_lines(['2', '33333', '33333', '33333', ''], s)
s.resize(4, 12)
assert_lines(['2', '333333333333', '333', ''], s)
def test_tab_stops(self):
# Taken from vttest/main.c
s = self.create_screen(cols=80, lines=2)