Allow adding line wrap markers when piping screen contents
Also make some data such as scroll position, cursor position and screen geometry available via an env var when piping. See #719
This commit is contained in:
parent
ae8ca5272b
commit
a7a7216b30
@ -13,7 +13,7 @@ Changelog
|
|||||||
- Modify the kittens sub-system to allow creating custom kittens without any
|
- Modify the kittens sub-system to allow creating custom kittens without any
|
||||||
user interface. This is useful for creating more complex actions that can
|
user interface. This is useful for creating more complex actions that can
|
||||||
be bound to key presses in :file:`kitty.conf`. See
|
be bound to key presses in :file:`kitty.conf`. See
|
||||||
`https://sw.kovidgoyal.net/kitty/kittens/custom.html`_. (:iss:`870`)
|
doc:`kittens/custom`. (:iss:`870`)
|
||||||
|
|
||||||
- Add a new ``nth_window`` action that can be used to go to the nth window and
|
- Add a new ``nth_window`` action that can be used to go to the nth window and
|
||||||
also previously active windows, using negative numbers. Similarly,
|
also previously active windows, using negative numbers. Similarly,
|
||||||
@ -443,7 +443,7 @@ Changelog
|
|||||||
|
|
||||||
- Add a config option (:opt:`editor`) to set the EDITOR kitty uses (:iss:`580`)
|
- Add a config option (:opt:`editor`) to set the EDITOR kitty uses (:iss:`580`)
|
||||||
|
|
||||||
- Add a config option (:opt:`x11_hide_window_decorations`) to hide window
|
- Add a config option (``x11_hide_window_decorations``) to hide window
|
||||||
decorations under X11/Wayland (:iss:`607`)
|
decorations under X11/Wayland (:iss:`607`)
|
||||||
|
|
||||||
- Add an option to @set-window-title to make the title change non-permanent
|
- Add an option to @set-window-title to make the title change non-permanent
|
||||||
|
|||||||
88
docs/pipe.rst
Normal file
88
docs/pipe.rst
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
Working with the screen and history buffer contents
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
|
||||||
|
You can pipe the contents of the current screen and history buffer as
|
||||||
|
:file:`STDIN` to an arbitrary program using the ``pipe`` function. The program
|
||||||
|
can be displayed in a kitty window or overlay.
|
||||||
|
|
||||||
|
For example, the following in :file:`kitty.conf` will open the scrollback
|
||||||
|
buffer in less in an overlay window, when you press :kbd:`F1`::
|
||||||
|
|
||||||
|
map f1 pipe @ansi overlay less +G -R
|
||||||
|
|
||||||
|
The syntax of the ``pipe`` function is::
|
||||||
|
|
||||||
|
pipe <input placeholder> <destination window type> <command line to run>
|
||||||
|
|
||||||
|
|
||||||
|
The piping environment
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
The program to which the data is piped has a special environment variable
|
||||||
|
declared, ``KITTY_PIPE_DATA`` whose contents are::
|
||||||
|
|
||||||
|
KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}
|
||||||
|
|
||||||
|
where ``scrolled_by`` is the number of lines kitty is currently scrolled by,
|
||||||
|
``cursor_(x|y)`` is the position of the cursor on the screen with ``(1,1)``
|
||||||
|
being the top left corner and ``{lines},{columns}`` being the number of rows
|
||||||
|
and columns of the screen.
|
||||||
|
|
||||||
|
You can choose where to run the pipe program:
|
||||||
|
|
||||||
|
``overlay``
|
||||||
|
An overlay window over the current kitty window
|
||||||
|
|
||||||
|
``window``
|
||||||
|
A new kitty window
|
||||||
|
|
||||||
|
``os_window``
|
||||||
|
A new top-level window
|
||||||
|
|
||||||
|
``tab``
|
||||||
|
A new window in a new tab
|
||||||
|
|
||||||
|
``none``
|
||||||
|
Run it in the background
|
||||||
|
|
||||||
|
|
||||||
|
Input placeholders
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
There are various different kinds of placeholders
|
||||||
|
|
||||||
|
``@text``
|
||||||
|
Plain text, current screen + scrollback buffer
|
||||||
|
|
||||||
|
``@ansi``
|
||||||
|
Text with formatting, current screen + scrollback buffer
|
||||||
|
|
||||||
|
``@screen``
|
||||||
|
Plain text, only current screen
|
||||||
|
|
||||||
|
``@ansi_screen``
|
||||||
|
Text with formatting, only current screen
|
||||||
|
|
||||||
|
``@alternate``
|
||||||
|
Plain text, secondary screen. The secondary screen is the screen not currently displayed. For
|
||||||
|
example if you run a fullscreen terminal application, the secondary screen will
|
||||||
|
be the screen you return to when quitting the application.
|
||||||
|
|
||||||
|
``@ansi_alternate``
|
||||||
|
Text with formatting, secondary screen.
|
||||||
|
|
||||||
|
``@alternate_scrollback``
|
||||||
|
Plain text, secondary screen + scrollback, if any.
|
||||||
|
|
||||||
|
``@ansi_alternate_scrollback``
|
||||||
|
Text with formatting, secondary screen + scrollback, if any.
|
||||||
|
|
||||||
|
``none``
|
||||||
|
No input
|
||||||
|
|
||||||
|
|
||||||
|
You can also add the suffix ``_wrap`` to the placeholder, in which case kitty
|
||||||
|
will insert the carriage return at every line wrap location (where long lines
|
||||||
|
are wrapped at screen edges). This is useful if you want to pipe to program
|
||||||
|
that wants to duplicate the screen layout of the screen.
|
||||||
@ -48,25 +48,29 @@ def listen_on(spec):
|
|||||||
return s.fileno()
|
return s.fileno()
|
||||||
|
|
||||||
|
|
||||||
def data_for_at(w, arg):
|
def data_for_at(w, arg, add_wrap_markers=False):
|
||||||
|
def as_text(**kw):
|
||||||
|
kw['add_wrap_markers'] = add_wrap_markers
|
||||||
|
return w.as_text(**kw)
|
||||||
|
|
||||||
if arg == '@selection':
|
if arg == '@selection':
|
||||||
return w.text_for_selection()
|
return w.text_for_selection()
|
||||||
if arg == '@ansi':
|
if arg == '@ansi':
|
||||||
return w.as_text(as_ansi=True, add_history=True)
|
return as_text(as_ansi=True, add_history=True)
|
||||||
if arg == '@text':
|
if arg == '@text':
|
||||||
return w.as_text(add_history=True)
|
return as_text(add_history=True)
|
||||||
if arg == '@screen':
|
if arg == '@screen':
|
||||||
return w.as_text()
|
return as_text()
|
||||||
if arg == '@ansi_screen':
|
if arg == '@ansi_screen':
|
||||||
return w.as_text(as_ansi=True)
|
return as_text(as_ansi=True)
|
||||||
if arg == '@alternate':
|
if arg == '@alternate':
|
||||||
return w.as_text(alternate_screen=True)
|
return as_text(alternate_screen=True)
|
||||||
if arg == '@alternate_scrollback':
|
if arg == '@alternate_scrollback':
|
||||||
return w.as_text(alternate_screen=True, add_history=True)
|
return as_text(alternate_screen=True, add_history=True)
|
||||||
if arg == '@ansi_alternate':
|
if arg == '@ansi_alternate':
|
||||||
return w.as_text(as_ansi=True, alternate_screen=True)
|
return as_text(as_ansi=True, alternate_screen=True)
|
||||||
if arg == '@ansi_alternate_scrollback':
|
if arg == '@ansi_alternate_scrollback':
|
||||||
return w.as_text(as_ansi=True, alternate_screen=True, add_history=True)
|
return as_text(as_ansi=True, alternate_screen=True, add_history=True)
|
||||||
|
|
||||||
|
|
||||||
class DumpCommands: # {{{
|
class DumpCommands: # {{{
|
||||||
@ -816,9 +820,19 @@ class Boss:
|
|||||||
|
|
||||||
def special_window_for_cmd(self, cmd, window=None, stdin=None, cwd_from=None, as_overlay=False):
|
def special_window_for_cmd(self, cmd, window=None, stdin=None, cwd_from=None, as_overlay=False):
|
||||||
w = window or self.active_window
|
w = window or self.active_window
|
||||||
|
env = None
|
||||||
if stdin:
|
if stdin:
|
||||||
stdin = data_for_at(w, stdin)
|
add_wrap_markers = stdin.endswith('_wrap')
|
||||||
|
if add_wrap_markers:
|
||||||
|
stdin = stdin[:-len('_wrap')]
|
||||||
|
stdin = data_for_at(w, stdin, add_wrap_markers=add_wrap_markers)
|
||||||
if stdin is not None:
|
if stdin is not None:
|
||||||
|
pipe_data = w.pipe_data(stdin, has_wrap_markers=add_wrap_markers) if w else {}
|
||||||
|
if pipe_data:
|
||||||
|
env = {
|
||||||
|
'KITTY_PIPE_DATA':
|
||||||
|
'{scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}'.format(**pipe_data)
|
||||||
|
}
|
||||||
stdin = stdin.encode('utf-8')
|
stdin = stdin.encode('utf-8')
|
||||||
cmdline = []
|
cmdline = []
|
||||||
for arg in cmd:
|
for arg in cmd:
|
||||||
@ -828,7 +842,7 @@ class Boss:
|
|||||||
continue
|
continue
|
||||||
cmdline.append(arg)
|
cmdline.append(arg)
|
||||||
overlay_for = w.id if as_overlay and w.overlay_for is None else None
|
overlay_for = w.id if as_overlay and w.overlay_for is None else None
|
||||||
return SpecialWindow(cmd, stdin, cwd_from=cwd_from, overlay_for=overlay_for)
|
return SpecialWindow(cmd, stdin, cwd_from=cwd_from, overlay_for=overlay_for, env=env)
|
||||||
|
|
||||||
def pipe(self, source, dest, exe, *args):
|
def pipe(self, source, dest, exe, *args):
|
||||||
cmd = [exe] + list(args)
|
cmd = [exe] + list(args)
|
||||||
|
|||||||
@ -783,7 +783,7 @@ an arbitrary color, such as :code:`#12af59` or :code:`red`. WARNING: This option
|
|||||||
using a hack, as there is no proper Cocoa API for it. It sets the background
|
using a hack, as there is no proper Cocoa API for it. It sets the background
|
||||||
color of the entire window and makes the titlebar transparent. As such it is
|
color of the entire window and makes the titlebar transparent. As such it is
|
||||||
incompatible with :opt:`background_opacity`. If you want to use both, you are
|
incompatible with :opt:`background_opacity`. If you want to use both, you are
|
||||||
probably better off just hiding the titlebar with :opt:`macos_hide_titlebar`.
|
probably better off just hiding the titlebar with :opt:`hide_window_decorations`.
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
o('macos_option_as_alt', True, long_text=_('''
|
o('macos_option_as_alt', True, long_text=_('''
|
||||||
@ -877,19 +877,9 @@ the following opens the scrollback buffer in less in an overlay window::
|
|||||||
|
|
||||||
map f1 pipe @ansi overlay less +G -R
|
map f1 pipe @ansi overlay less +G -R
|
||||||
|
|
||||||
Placeholders available are: ``@text`` (which is plain text) and ``@ansi`` (which
|
For more details on piping screen and buffer contents to external programs,
|
||||||
includes text styling escape codes). For only the current screen, use ``@screen``
|
see :doc:`pipe`.
|
||||||
or ``@ansi_screen``. For the secondary screen, use ``@alternate`` and ``@ansi_alternate``.
|
'''))
|
||||||
The secondary screen is the screen not currently displayed. For
|
|
||||||
example if you run a fullscreen terminal application, the secondary screen will
|
|
||||||
be the screen you return to when quitting the application. If you want access to the
|
|
||||||
secondary screen scrollback, use ``@alternate_scrollback``. You can also use
|
|
||||||
``none`` for no :file:`STDIN` input.
|
|
||||||
|
|
||||||
To open in a new window, tab or new OS window, use ``window``, ``tab``, or
|
|
||||||
``os_window`` respectively. You can also use ``none`` in which case the data
|
|
||||||
will be piped into the program without creating any windows, useful if the
|
|
||||||
program is a GUI program that creates its own windows. '''))
|
|
||||||
|
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|||||||
@ -483,15 +483,26 @@ class Window:
|
|||||||
if pid is not None:
|
if pid is not None:
|
||||||
return cwd_of_process(pid) or None
|
return cwd_of_process(pid) or None
|
||||||
|
|
||||||
|
def pipe_data(self, text, has_wrap_markers=False):
|
||||||
|
text = text or ''
|
||||||
|
if has_wrap_markers:
|
||||||
|
text = text.replace('\r\n', '\n').replace('\r', '\n')
|
||||||
|
lines = text.count('\n')
|
||||||
|
input_line_number = (lines - (self.screen.lines - 1) - self.screen.scrolled_by)
|
||||||
|
return {
|
||||||
|
'input_line_number': input_line_number, 'scrolled_by': self.screen.scrolled_by,
|
||||||
|
'cursor_x': self.screen.cursor.x + 1, 'cursor_y': self.screen.cursor.y + 1,
|
||||||
|
'lines': self.screen.lines, 'columns': self.screen.columns,
|
||||||
|
'text': text
|
||||||
|
}
|
||||||
|
|
||||||
# actions {{{
|
# actions {{{
|
||||||
|
|
||||||
def show_scrollback(self):
|
def show_scrollback(self):
|
||||||
data = self.as_text(as_ansi=True, add_history=True, add_wrap_markers=True)
|
text = self.as_text(as_ansi=True, add_history=True, add_wrap_markers=True)
|
||||||
data = data.replace('\r\n', '\n').replace('\r', '\n')
|
data = self.pipe_data(text, has_wrap_markers=True)
|
||||||
lines = data.count('\n')
|
cmd = [x.replace('INPUT_LINE_NUMBER', str(data['input_line_number'])) for x in self.opts.scrollback_pager]
|
||||||
input_line_number = (lines - (self.screen.lines - 1) - self.screen.scrolled_by)
|
get_boss().display_scrollback(self, data['text'], cmd)
|
||||||
cmd = [x.replace('INPUT_LINE_NUMBER', str(input_line_number)) for x in self.opts.scrollback_pager]
|
|
||||||
get_boss().display_scrollback(self, data, cmd)
|
|
||||||
|
|
||||||
def paste(self, text):
|
def paste(self, text):
|
||||||
if text and not self.destroyed:
|
if text and not self.destroyed:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user