Fix show_last_command_output not working when the output is stored partially in the scrollback pager history buffer
Fixes #4435
This commit is contained in:
parent
5d120a2f36
commit
80202d2679
@ -86,6 +86,9 @@ Detailed list of changes
|
||||
|
||||
- Fix a regression in the previous release that broke :opt:`active_tab_foreground` (:iss:`4620`)
|
||||
|
||||
- Fix :ac:`show_last_command_output` not working when the output is stored
|
||||
partially in the scrollback pager history buffer (:iss:`4435`)
|
||||
|
||||
- Improve CWD detection when there are multiple foreground processes in the TTY process group
|
||||
|
||||
- A new option :opt:`narrow_symbols` to turn off opportunistic wide rendering of private use codepoints
|
||||
|
||||
@ -987,7 +987,7 @@ class HistoryBuf:
|
||||
def as_text(self, callback: Callable[[str], None], as_ansi: bool, insert_wrap_markers: bool) -> None:
|
||||
pass
|
||||
|
||||
def pagerhist_as_text(self) -> str:
|
||||
def pagerhist_as_text(self, upto_output_start: bool = False) -> str:
|
||||
pass
|
||||
|
||||
def pagerhist_as_bytes(self) -> bytes:
|
||||
@ -1137,7 +1137,7 @@ class Screen:
|
||||
as_text_non_visual = as_text
|
||||
as_text_alternate = as_text
|
||||
|
||||
def cmd_output(self, which: int, callback: Callable[[str], None], as_ansi: bool, insert_wrap_markers: bool) -> None:
|
||||
def cmd_output(self, which: int, callback: Callable[[str], None], as_ansi: bool, insert_wrap_markers: bool) -> bool:
|
||||
pass
|
||||
|
||||
def scroll_until_cursor_prompt(self) -> None:
|
||||
|
||||
@ -418,8 +418,21 @@ pagerhist_write(HistoryBuf *self, PyObject *what) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static const uint8_t*
|
||||
reverse_find(const uint8_t *haystack, size_t haystack_sz, const uint8_t *needle) {
|
||||
const size_t needle_sz = strlen((const char*)needle);
|
||||
if (!needle_sz || needle_sz > haystack_sz) return NULL;
|
||||
const uint8_t *p = haystack + haystack_sz - (needle_sz - 1);
|
||||
while (--p >= haystack) {
|
||||
if (*p == needle[0] && memcmp(p, needle, MIN(needle_sz, haystack_sz - (p - haystack))) == 0) return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
pagerhist_as_bytes(HistoryBuf *self, PyObject *args UNUSED) {
|
||||
pagerhist_as_bytes(HistoryBuf *self, PyObject *args) {
|
||||
int upto_output_start = 0;
|
||||
if (!PyArg_ParseTuple(args, "|p", &upto_output_start)) return NULL;
|
||||
#define ph self->pagerhist
|
||||
if (!ph || !ringbuf_bytes_used(ph->ringbuf)) return PyBytes_FromStringAndSize("", 0);
|
||||
pagerhist_ensure_start_is_valid_utf8(ph);
|
||||
@ -433,6 +446,13 @@ pagerhist_as_bytes(HistoryBuf *self, PyObject *args UNUSED) {
|
||||
uint8_t *buf = (uint8_t*)PyBytes_AS_STRING(ans);
|
||||
ringbuf_memcpy_from(buf, ph->ringbuf, sz);
|
||||
if (!l.attrs.continued) buf[sz-1] = '\n';
|
||||
if (upto_output_start) {
|
||||
const uint8_t *p = reverse_find(buf, sz, (const uint8_t*)"\x1b]133;C\x1b\\");
|
||||
if (p) {
|
||||
PyObject *t = PyBytes_FromStringAndSize((const char*)p, sz - (p - buf));
|
||||
Py_DECREF(ans); ans = t;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
#undef ph
|
||||
}
|
||||
@ -501,8 +521,8 @@ static PyMethodDef methods[] = {
|
||||
METHOD(as_ansi, METH_O)
|
||||
METHODB(pagerhist_write, METH_O),
|
||||
METHODB(pagerhist_rewrap, METH_O),
|
||||
METHODB(pagerhist_as_text, METH_NOARGS),
|
||||
METHODB(pagerhist_as_bytes, METH_NOARGS),
|
||||
METHODB(pagerhist_as_text, METH_VARARGS),
|
||||
METHODB(pagerhist_as_bytes, METH_VARARGS),
|
||||
METHODB(as_text, METH_VARARGS),
|
||||
METHOD(dirty_lines, METH_NOARGS)
|
||||
METHOD(push, METH_VARARGS)
|
||||
|
||||
@ -326,7 +326,7 @@ Separate scrollback history size, used only for browsing the scrollback buffer
|
||||
(in MB). This separate buffer is not available for interactive scrolling but
|
||||
will be piped to the pager program when viewing scrollback buffer in a separate
|
||||
window. 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
|
||||
10000 lines per megabyte at 100 chars per line, for pure ASCII, unformatted
|
||||
text. A value of zero or less disables this feature. The maximum allowed size is
|
||||
4GB. Note that on config reload if this
|
||||
is changed it will only affect newly created windows, not existing ones.
|
||||
|
||||
@ -2736,6 +2736,7 @@ typedef struct OutputOffset {
|
||||
Screen *screen;
|
||||
int start;
|
||||
unsigned num_lines;
|
||||
bool reached_upper_limit;
|
||||
} OutputOffset;
|
||||
|
||||
static Line*
|
||||
@ -2788,7 +2789,10 @@ find_cmd_output(Screen *self, OutputOffset *oo, index_type start_screen_y, unsig
|
||||
}
|
||||
y1--;
|
||||
}
|
||||
if (y1 < upward_limit) start = upward_limit;
|
||||
if (y1 < upward_limit) {
|
||||
oo->reached_upper_limit = true;
|
||||
start = upward_limit;
|
||||
}
|
||||
found_output = true; found_prompt = true;
|
||||
}
|
||||
|
||||
@ -2848,8 +2852,12 @@ cmd_output(Screen *self, PyObject *args) {
|
||||
PyErr_Format(PyExc_KeyError, "%u is not a valid type of command", which);
|
||||
return NULL;
|
||||
}
|
||||
if (found) return as_text_generic(as_text_args, &oo, get_line_from_offset, oo.num_lines, &self->as_ansi_buf);
|
||||
Py_RETURN_NONE;
|
||||
if (found) {
|
||||
DECREF_AFTER_FUNCTION PyObject *ret = as_text_generic(as_text_args, &oo, get_line_from_offset, oo.num_lines, &self->as_ansi_buf);
|
||||
if (!ret) return NULL;
|
||||
}
|
||||
if (oo.reached_upper_limit && self->linebuf == self->main_linebuf && OPT(scrollback_pager_history_size) > 0) Py_RETURN_TRUE;
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@ -157,6 +157,14 @@ def call_watchers(windowref: Callable[[], Optional['Window']], which: str, data:
|
||||
add_timer(callback, 0, False)
|
||||
|
||||
|
||||
def pagerhist(screen: Screen, as_ansi: bool = False, add_wrap_markers: bool = True, upto_output_start: bool = False) -> str:
|
||||
pht = screen.historybuf.pagerhist_as_text(upto_output_start)
|
||||
if pht and (not as_ansi or not add_wrap_markers):
|
||||
sanitizer = text_sanitizer(as_ansi, add_wrap_markers)
|
||||
pht = sanitizer(pht)
|
||||
return pht
|
||||
|
||||
|
||||
def as_text(
|
||||
screen: Screen,
|
||||
as_ansi: bool = False,
|
||||
@ -186,13 +194,8 @@ def as_text(
|
||||
ctext += f'\x1b[{code} q'
|
||||
|
||||
if add_history:
|
||||
h: List[str] = []
|
||||
pht = screen.historybuf.pagerhist_as_text()
|
||||
if pht:
|
||||
h.append(pht)
|
||||
if h and (not as_ansi or not add_wrap_markers):
|
||||
sanitizer = text_sanitizer(as_ansi, add_wrap_markers)
|
||||
h = list(map(sanitizer, h))
|
||||
pht = pagerhist(screen, as_ansi, add_wrap_markers)
|
||||
h: List[str] = [pht] if pht else []
|
||||
screen.historybuf.as_text(h.append, as_ansi, add_wrap_markers)
|
||||
if h:
|
||||
if not screen.linebuf.is_continued(0):
|
||||
@ -1064,7 +1067,11 @@ class Window:
|
||||
|
||||
def cmd_output(self, which: CommandOutput = CommandOutput.last_run, as_ansi: bool = False, add_wrap_markers: bool = False) -> str:
|
||||
lines: List[str] = []
|
||||
self.screen.cmd_output(which, lines.append, as_ansi, add_wrap_markers)
|
||||
search_in_pager_hist = self.screen.cmd_output(which, lines.append, as_ansi, add_wrap_markers)
|
||||
if search_in_pager_hist:
|
||||
pht = pagerhist(self.screen, as_ansi, add_wrap_markers, True)
|
||||
if pht:
|
||||
lines.insert(0, pht)
|
||||
return ''.join(lines)
|
||||
|
||||
@property
|
||||
|
||||
@ -5,6 +5,7 @@ from kitty.fast_data_types import (
|
||||
DECAWM, DECCOLM, DECOM, IRM, Cursor, parse_bytes
|
||||
)
|
||||
from kitty.marks import marker_from_function, marker_from_regex
|
||||
from kitty.window import pagerhist
|
||||
|
||||
from . import BaseTest
|
||||
|
||||
@ -998,7 +999,10 @@ class TestScreen(BaseTest):
|
||||
|
||||
def lco(as_ansi=False):
|
||||
a = []
|
||||
s.cmd_output(0, a.append, as_ansi)
|
||||
if s.cmd_output(0, a.append, as_ansi):
|
||||
pht = pagerhist(s, as_ansi=as_ansi, upto_output_start=True)
|
||||
if pht:
|
||||
a.insert(0, pht)
|
||||
return ''.join(a)
|
||||
|
||||
def fco():
|
||||
@ -1076,3 +1080,9 @@ class TestScreen(BaseTest):
|
||||
self.ae(lvco(), '0\n1\n2')
|
||||
s.scroll_to_prompt(1)
|
||||
self.ae(lvco(), '0x\n1x')
|
||||
|
||||
# last command output from pager history
|
||||
s = self.create_screen()
|
||||
draw_prompt('p1')
|
||||
draw_output(30)
|
||||
self.ae(tuple(map(int, lco()[len('\x1b]133;C\x1b\\'):].split())), tuple(range(0, 30)))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user