From 2efb8f6dc29c93ba51fa5add1b1705d9f3fc5a30 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 20 Aug 2022 16:19:16 +0530 Subject: [PATCH] Create an API to easily use remote control commands from kittens --- docs/kittens/custom.rst | 17 +++++++++++++++++ kitty/boss.py | 36 +++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/docs/kittens/custom.rst b/docs/kittens/custom.rst index f13bc04c3..3ee3b7865 100644 --- a/docs/kittens/custom.rst +++ b/docs/kittens/custom.rst @@ -49,6 +49,23 @@ kittens. Look in the `kittens sub-directory code for those. Or see below for a list of :ref:`third-party kittens `, that other kitty users have created. +kitty API to use with kittens +------------------------------- + +Kittens have full access to internal kitty APIs. However these are neither +entirely stable nor documented. You can instead use the kitty :doc:`Remote +control API `. Simply call :code:`boss.remote_control`, with +the same arguments you would pass to ``kitty @ ``. For example: + +.. code-block:: python + + def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None: + # get the kitty window to which to send text + w = boss.window_id_map.get(target_window_id) + if w is not None: + boss.call_remote_control(w, ('send-text', 'some text')) + + Passing arguments to kittens ------------------------------ diff --git a/kitty/boss.py b/kitty/boss.py index 9f053679c..b8730a9fa 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -12,8 +12,8 @@ from functools import partial from gettext import gettext as _ from time import monotonic from typing import ( - Any, Callable, Container, Dict, Iterable, Iterator, List, Optional, Set, - Tuple, Union + TYPE_CHECKING, Any, Callable, Container, Dict, Iterable, Iterator, List, + Optional, Set, Tuple, Union ) from weakref import WeakValueDictionary @@ -67,6 +67,9 @@ from .utils import ( ) from .window import CommandOutput, CwdRequest, Window +if TYPE_CHECKING: + from .rc.base import ResponseType + RCResponse = Union[Dict[str, Any], None, AsyncResponse] @@ -563,6 +566,14 @@ class Boss: See :ref:`rc_mapping` for details. ''') def remote_control(self, *args: str) -> None: + try: + self.call_remote_control(self.active_window, args) + except (Exception, SystemExit): + import traceback + tb = traceback.format_exc() + self.show_error(_('remote_control mapping failed'), tb) + + def call_remote_control(self, active_window: Optional[Window], args: Tuple[str, ...]) -> 'ResponseType': from .rc.base import ( PayloadGetter, command_for_name, parse_subcommand_cli ) @@ -570,21 +581,20 @@ class Boss: try: global_opts, items = parse_rc_args(['@'] + list(args)) if not items: - return + return None cmd = items[0] c = command_for_name(cmd) opts, items = parse_subcommand_cli(c, items) payload = c.message_to_kitty(global_opts, opts, items) - import types - if isinstance(payload, types.GeneratorType): - for x in payload: - c.response_from_kitty(self, self.active_window, PayloadGetter(c, x if isinstance(x, dict) else {})) - else: - c.response_from_kitty(self, self.active_window, PayloadGetter(c, payload if isinstance(payload, dict) else {})) - except (Exception, SystemExit): - import traceback - tb = traceback.format_exc() - self.show_error(_('remote_control mapping failed'), tb) + except SystemExit as e: + raise Exception(str(e)) from e + import types + if isinstance(payload, types.GeneratorType): + for x in payload: + c.response_from_kitty(self, active_window, PayloadGetter(c, x if isinstance(x, dict) else {})) + else: + return c.response_from_kitty(self, active_window, PayloadGetter(c, payload if isinstance(payload, dict) else {})) + return None def peer_message_received(self, msg_bytes: bytes, peer_id: int) -> Union[bytes, bool, None]: cmd_prefix = b'\x1bP@kitty-cmd'