From e5b9bd2e5ac316cd9cb6e7a7230ab98d6429566c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 9 Jan 2018 22:48:24 +0530 Subject: [PATCH] Implement @get-text --- kitty/line-buf.c | 10 +++++++- kitty/line.c | 1 + kitty/remote_control.py | 54 ++++++++++++++++++++++++++++++++++++++--- kitty/window.py | 14 ++++++++--- 4 files changed, 70 insertions(+), 9 deletions(-) diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 542dbc08e..ad1bcc74e 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -383,7 +383,15 @@ 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." static Py_UCS4 t[5120]; Line l = {.xnum=self->xnum}; - for(index_type i = 0; i < self->ynum; i++) { + // remove trailing empty lines + index_type ylimit = self->ynum - 1; + do { + init_line(self, (&l), self->line_map[ylimit]); + if (line_as_ansi(&l, t, 5120) != 0) break; + ylimit--; + } while(ylimit > 0); + + for(index_type i = 0; i <= ylimit; i++) { l.continued = ((i < self->ynum - 1) ? self->line_attrs[i+1] : self->line_attrs[i]) & CONTINUED_MASK; init_line(self, (&l), self->line_map[i]); index_type num = line_as_ansi(&l, t, 5120); diff --git a/kitty/line.c b/kitty/line.c index 7402d449d..c6f6648b6 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -244,6 +244,7 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) { #define WRITE_CH(val) if (i > buflen - 1) return i; buf[i++] = val; index_type limit = xlimit_for_line(self), i=0; + if (limit == 0) return 0; char_type previous_width = 0; WRITE_SGR("0"); diff --git a/kitty/remote_control.py b/kitty/remote_control.py index 3b66f29a8..6dd87c9d1 100644 --- a/kitty/remote_control.py +++ b/kitty/remote_control.py @@ -244,6 +244,49 @@ def new_window(boss, window, payload): return str(w.id) +@cmd( + 'Get text from the specified window', + options_spec=MATCH_WINDOW_OPTION + '''\n +--extent +default=screen +choices=screen, all, selection +What text to get. The default of screen means all text currently on the screen. all means +all the screen+scrollback and selection means currently selected text. + + +--ansi +type=bool-set +By default, only plain text is return. If you specify this flag, the text will +include the formatting escape codes for colors/bold/italic/etc. Note that when +getting the current selection, the result is always plain text. + + +--self +type=bool-set +If specified get text from the window this command is run in, rather than the active window. +''' +) +def cmd_get_text(global_opts, opts, args): + return {'match': opts.match, 'extent': opts.extent, 'ansi': opts.ansi, 'self': opts.self} + + +def get_text(boss, window, payload): + match = payload['match'] + if match: + windows = tuple(boss.match_windows(match)) + if not windows: + raise ValueError('No matching windows for expression: {}'.format(match)) + else: + windows = [window if window and payload['self'] else boss.active_window] + window = windows[0] + if payload['extent'] == 'selection': + ans = window.text_for_selection() + else: + f = window.buffer_as_ansi if payload['ansi'] else window.buffer_as_text + ans = f(add_history=payload['extent'] == 'all') + return ans + + cmap = {v.name: v for v in globals().values() if hasattr(v, 'is_cmd')} @@ -270,12 +313,15 @@ global_options_spec = partial('''\ def read_from_stdin(send, no_response): send = ('@kitty-cmd' + json.dumps(send)).encode('ascii') - if not sys.stdout.isatty(): - raise SystemExit('stdout is not a terminal') - sys.stdout.buffer.write(b'\x1bP' + send + b'\x1b\\') - sys.stdout.flush() + out = sys.stdout if sys.stdout.isatty() else sys.stderr + if not out.isatty(): + raise SystemExit('Neither stdout nor stderr is a terminal') + out.buffer.write(b'\x1bP' + send + b'\x1b\\') + out.flush() if no_response: return {'ok': True} + if not sys.stdin.isatty(): + raise SystemExit('stdin is not a terminal') received = b'' dcs = re.compile(br'\x1bP@kitty-cmd([^\x1b]+)\x1b\\') diff --git a/kitty/window.py b/kitty/window.py index 4a30f56de..e0bf8c975 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -312,14 +312,20 @@ class Window: self.screen.reset_callbacks() self.screen = None - def buffer_as_ansi(self): + def buffer_as_ansi(self, add_history=True): data = [] - self.screen.historybuf.as_ansi(data.append) + if add_history: + self.screen.historybuf.as_ansi(data.append) self.screen.linebuf.as_ansi(data.append) return ''.join(data) - def buffer_as_text(self): - return str(self.screen.historybuf) + '\n' + str(self.screen.linebuf) + def buffer_as_text(self, add_history=True): + ans = str(self.screen.linebuf).rstrip('\n') + if add_history: + h = str(self.screen.historybuf) + if h.strip(): + ans = h + '\n' + ans + return ans # actions {{{