Function to erase the last command and its output
This commit is contained in:
parent
97f1d7f436
commit
b0439d4183
5 changed files with 99 additions and 9 deletions
|
|
@ -1235,6 +1235,7 @@ class Screen:
|
|||
def test_commit_write_buffer(self, inp: memoryview, output: memoryview) -> int: ...
|
||||
def test_parse_written_data(self, dump_callback: None = None) -> None: ...
|
||||
def hyperlink_for_id(self, hyperlink_id: int) -> str: ...
|
||||
def erase_last_command(self, include_prompt: bool = True) -> bool: ...
|
||||
|
||||
def cursor_at_prompt(self) -> bool:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -327,6 +327,30 @@ historybuf_pop_line(HistoryBuf *self, Line *line) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
historybuf_delete_newest_lines(HistoryBuf *self, index_type count) {
|
||||
if (!count) return;
|
||||
count = MIN(self->count, count);
|
||||
self->count -= count - 1;
|
||||
// now nuke multi cell chars that overlap onto the last line
|
||||
index_type idx = (self->start_of_data + self->count - 1) % self->ynum;
|
||||
init_line(self, idx, self->line);
|
||||
CPUCell *cells = self->line->cpu_cells;
|
||||
self->count--;
|
||||
for (index_type x = 0; x < self->line->xnum; x++) {
|
||||
CPUCell *c = cells + x;
|
||||
if (c->is_multicell && c->y) {
|
||||
for (index_type i = 0, pos = self->count; i < c->y && pos; i++, pos--) {
|
||||
index_type idx = (self->start_of_data + pos - 1) % self->ynum;
|
||||
init_line(self, idx, self->line);
|
||||
CPUCell *m = self->line->cpu_cells + x;
|
||||
cell_set_char(m, 0); m->is_multicell = false;
|
||||
clear_sprite_position(self->line->gpu_cells[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -41,3 +41,4 @@ void historybuf_finish_rewrap(HistoryBuf *dest, HistoryBuf *src);
|
|||
void historybuf_fast_rewrap(HistoryBuf *dest, HistoryBuf *src);
|
||||
index_type historybuf_next_dest_line(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued);
|
||||
bool historybuf_is_line_continued(HistoryBuf *self, index_type lnum);
|
||||
void historybuf_delete_newest_lines(HistoryBuf *self, index_type count);
|
||||
|
|
|
|||
|
|
@ -2550,20 +2550,25 @@ screen_scroll_until_cursor_prompt(Screen *self, bool add_to_scrollback) {
|
|||
screen_ensure_bounds(self, false, in_margins);
|
||||
}
|
||||
|
||||
static void
|
||||
screen_delete_lines_impl(Screen *self, index_type start, index_type count, index_type top, index_type bottom) {
|
||||
index_type y = start;
|
||||
nuke_multiline_char_intersecting_with(self, 0, self->columns, y, y + 1, false);
|
||||
y += count;
|
||||
y = MIN(bottom, y);
|
||||
nuke_multiline_char_intersecting_with(self, 0, self->columns, y, y + 1, false);
|
||||
screen_dirty_line_graphics(self, top, bottom, self->linebuf == self->main_linebuf);
|
||||
linebuf_delete_lines(self->linebuf, count, start, bottom);
|
||||
self->is_dirty = true;
|
||||
clear_all_selections(self);
|
||||
}
|
||||
|
||||
void
|
||||
screen_delete_lines(Screen *self, unsigned int count) {
|
||||
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
||||
if (count == 0) count = 1;
|
||||
if (top <= self->cursor->y && self->cursor->y <= bottom) {
|
||||
index_type y = self->cursor->y;
|
||||
nuke_multiline_char_intersecting_with(self, 0, self->columns, y, y + 1, false);
|
||||
y += count;
|
||||
y = MIN(bottom, y);
|
||||
nuke_multiline_char_intersecting_with(self, 0, self->columns, y, y + 1, false);
|
||||
screen_dirty_line_graphics(self, top, bottom, self->linebuf == self->main_linebuf);
|
||||
linebuf_delete_lines(self->linebuf, count, self->cursor->y, bottom);
|
||||
self->is_dirty = true;
|
||||
clear_all_selections(self);
|
||||
screen_delete_lines_impl(self, self->cursor->y, count, self->margin_bottom, self->margin_bottom);
|
||||
screen_carriage_return(self);
|
||||
}
|
||||
}
|
||||
|
|
@ -4223,6 +4228,32 @@ find_cmd_output(Screen *self, OutputOffset *oo, index_type start_screen_y, unsig
|
|||
return oo->num_lines > 0;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
erase_last_command(Screen *self, PyObject *args) {
|
||||
int include_prompt = 1;
|
||||
if (!PyArg_ParseTuple(args, "|p", &include_prompt)) return NULL;
|
||||
OutputOffset oo = {.screen=self};
|
||||
if (self->linebuf != self->main_linebuf || !find_cmd_output(self, &oo, self->cursor->y + self->scrolled_by, self->scrolled_by, -1, false)) Py_RETURN_FALSE;
|
||||
if (include_prompt) {
|
||||
int y = oo.start - 1; Line *line;
|
||||
while ((line = checked_range_line(self, y))) {
|
||||
oo.start--; oo.num_lines++; y--;
|
||||
if (line->attrs.prompt_kind == PROMPT_START) break;
|
||||
}
|
||||
}
|
||||
index_type num_lines_to_erase_in_screen = oo.start >= 0 ? oo.num_lines : oo.num_lines + oo.start;
|
||||
num_lines_to_erase_in_screen = MIN(self->cursor->y, num_lines_to_erase_in_screen);
|
||||
if (num_lines_to_erase_in_screen) {
|
||||
screen_delete_lines_impl(self, self->cursor->y - num_lines_to_erase_in_screen, num_lines_to_erase_in_screen, 0, self->lines - 1);
|
||||
self->cursor->y -= num_lines_to_erase_in_screen;
|
||||
}
|
||||
if (oo.num_lines > num_lines_to_erase_in_screen) {
|
||||
index_type num_of_lines_to_erase_from_history = oo.num_lines - num_lines_to_erase_in_screen;
|
||||
historybuf_delete_newest_lines(self->historybuf, num_of_lines_to_erase_from_history);
|
||||
}
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cmd_output(Screen *self, PyObject *args) {
|
||||
unsigned int which = 0;
|
||||
|
|
@ -5555,6 +5586,7 @@ static PyMethodDef methods[] = {
|
|||
MND(draw, METH_O)
|
||||
MND(apply_sgr, METH_O)
|
||||
MND(cursor_position, METH_VARARGS)
|
||||
MND(erase_last_command, METH_VARARGS)
|
||||
MND(set_window_char, METH_VARARGS)
|
||||
MND(set_mode, METH_VARARGS)
|
||||
MND(reset_mode, METH_VARARGS)
|
||||
|
|
|
|||
|
|
@ -1227,6 +1227,10 @@ def draw_output(n, x='', m=True):
|
|||
for i in range(n):
|
||||
s.draw(f'{i}{x}'), s.index(), s.carriage_return()
|
||||
|
||||
from kitty.window import as_text
|
||||
def at():
|
||||
return as_text(s, add_history=True)
|
||||
|
||||
s = self.create_screen(cols=5, lines=5, scrollback=15)
|
||||
draw_output(3, 'oo')
|
||||
draw_prompt('pp')
|
||||
|
|
@ -1481,6 +1485,34 @@ def lvco():
|
|||
draw_prompt('p1')
|
||||
self.ae(lco(which=3), '0a\n1a')
|
||||
|
||||
# erase last command
|
||||
s = self.create_screen(cols=10, lines=10, scrollback=15)
|
||||
s.draw('before\r\n')
|
||||
draw_prompt('p1'), draw_output(2), mark_prompt(), s.draw('partial')
|
||||
self.ae('before\n$ p1\n0\n1\npartial', at().rstrip())
|
||||
self.ae(lco(), '0\n1')
|
||||
x = s.cursor.x
|
||||
s.erase_last_command()
|
||||
self.ae('before\npartial', at().rstrip())
|
||||
self.ae((x, 1), (s.cursor.x, s.cursor.y))
|
||||
s.reset()
|
||||
s.draw('before\r\n')
|
||||
draw_prompt('p1'), draw_output(2), mark_prompt(), s.draw('partial')
|
||||
x = s.cursor.x
|
||||
s.erase_last_command(False)
|
||||
self.ae('before\n$ p1\npartial', at().rstrip())
|
||||
for scroll in (8, 9, 10):
|
||||
s.reset()
|
||||
s.draw('before'), s.carriage_return(), s.linefeed()
|
||||
draw_prompt('p1'), draw_output(scroll), mark_prompt(), s.draw('partial')
|
||||
s.erase_last_command()
|
||||
self.ae('before\npartial', at().rstrip())
|
||||
s.reset()
|
||||
draw_multicell(s, 'A', scale=2), s.draw('a'), draw_multicell(s, 'B', scale=2), s.draw('b\r\n')
|
||||
draw_prompt('p1'), draw_output(9), mark_prompt(), s.draw('partial')
|
||||
s.erase_last_command()
|
||||
self.ae(at().rstrip(), ' a b\npartial')
|
||||
|
||||
def test_pointer_shapes(self):
|
||||
from kitty.window import set_pointer_shape
|
||||
s = self.create_screen()
|
||||
|
|
|
|||
Loading…
Reference in a new issue