custom.rst 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. Custom kittens
  2. =================
  3. You can easily create your own kittens to extend kitty. They are just terminal
  4. programs written in Python. When launching a kitten, kitty will open an overlay
  5. window over the current window and optionally pass the contents of the current
  6. window/scrollback to the kitten over its :file:`STDIN`. The kitten can then
  7. perform whatever actions it likes, just as a normal terminal program. After
  8. execution of the kitten is complete, it has access to the running kitty instance
  9. so it can perform arbitrary actions such as closing windows, pasting text, etc.
  10. Let's see a simple example of creating a kitten. It will ask the user for some
  11. input and paste it into the terminal window.
  12. Create a file in the kitty config directory, :file:`~/.config/kitty/mykitten.py`
  13. (you might need to adjust the path to wherever the :ref:`kitty config directory
  14. <confloc>` is on your machine).
  15. .. code-block:: python
  16. from typing import List
  17. from kitty.boss import Boss
  18. def main(args: List[str]) -> str:
  19. # this is the main entry point of the kitten, it will be executed in
  20. # the overlay window when the kitten is launched
  21. answer = input('Enter some text: ')
  22. # whatever this function returns will be available in the
  23. # handle_result() function
  24. return answer
  25. def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
  26. # get the kitty window into which to paste answer
  27. w = boss.window_id_map.get(target_window_id)
  28. if w is not None:
  29. w.paste_text(answer)
  30. Now in :file:`kitty.conf` add the lines::
  31. map ctrl+k kitten mykitten.py
  32. Start kitty and press :kbd:`Ctrl+K` and you should see the kitten running.
  33. The best way to develop your own kittens is to modify one of the built-in
  34. kittens. Look in the `kittens sub-directory
  35. <https://github.com/kovidgoyal/kitty/tree/master/kittens>`__ of the kitty source
  36. code for those. Or see below for a list of :ref:`third-party kittens
  37. <external_kittens>`, that other kitty users have created.
  38. kitty API to use with kittens
  39. -------------------------------
  40. Kittens have full access to internal kitty APIs. However these are neither
  41. entirely stable nor documented. You can instead use the kitty
  42. :doc:`Remote control API </remote-control>`. Simply call
  43. :code:`boss.call_remote_control()`, with the same arguments you
  44. would pass to ``kitten @``. For example:
  45. .. code-block:: python
  46. def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
  47. # get the kitty window to which to send text
  48. w = boss.window_id_map.get(target_window_id)
  49. if w is not None:
  50. boss.call_remote_control(w, ('send-text', f'--match=id:{w.id}', 'hello world'))
  51. .. note::
  52. Inside handle_result() the active window is still the window in which the
  53. kitten was run, therefore when using call_remote_control() be sure to pass
  54. the appropriate option to select the target window, usually ``--match`` as
  55. shown above or ``--self``.
  56. Passing arguments to kittens
  57. ------------------------------
  58. You can pass arguments to kittens by defining them in the map directive in
  59. :file:`kitty.conf`. For example::
  60. map ctrl+k kitten mykitten.py arg1 arg2
  61. These will be available as the ``args`` parameter in the ``main()`` and
  62. ``handle_result()`` functions. Note also that the current working directory
  63. of the kitten is set to the working directory of whatever program is running in
  64. the active kitty window. The special argument ``@selection`` is replaced by the
  65. currently selected text in the active kitty window.
  66. Passing the contents of the screen to the kitten
  67. ---------------------------------------------------
  68. If you would like your kitten to have access to the contents of the screen
  69. and/or the scrollback buffer, you just need to add an annotation to the
  70. ``handle_result()`` function, telling kitty what kind of input your kitten would
  71. like. For example:
  72. .. code-block:: py
  73. from typing import List
  74. from kitty.boss import Boss
  75. # in main, STDIN is for the kitten process and will contain
  76. # the contents of the screen
  77. def main(args: List[str]) -> str:
  78. return sys.stdin.read()
  79. # in handle_result, STDIN is for the kitty process itself, rather
  80. # than the kitten process and should not be read from.
  81. from kittens.tui.handler import result_handler
  82. @result_handler(type_of_input='text')
  83. def handle_result(args: List[str], stdin_data: str, target_window_id: int, boss: Boss) -> None:
  84. pass
  85. This will send the plain text of the active window to the kitten's
  86. :file:`STDIN`. There are many other types of input you can ask for, described in
  87. the table below:
  88. .. table:: Types of input to kittens
  89. :align: left
  90. =========================== =======================================================================================================
  91. Keyword Type of :file:`STDIN` input
  92. =========================== =======================================================================================================
  93. ``text`` Plain text of active window
  94. ``ansi`` Formatted text of active window
  95. ``screen`` Plain text of active window with line wrap markers
  96. ``screen-ansi`` Formatted text of active window with line wrap markers
  97. ``history`` Plain text of active window and its scrollback
  98. ``ansi-history`` Formatted text of active window and its scrollback
  99. ``screen-history`` Plain text of active window and its scrollback with line wrap markers
  100. ``screen-ansi-history`` Formatted text of active window and its scrollback with line wrap markers
  101. ``output`` Plain text of the output from the last run command
  102. ``output-screen`` Plain text of the output from the last run command with wrap markers
  103. ``output-ansi`` Formatted text of the output from the last run command
  104. ``output-screen-ansi`` Formatted text of the output from the last run command with wrap markers
  105. ``selection`` The text currently selected with the mouse
  106. =========================== =======================================================================================================
  107. In addition to ``output``, that gets the output of the last run command,
  108. ``last_visited_output`` gives the output of the command last jumped to
  109. and ``first_output`` gives the output of the first command currently on screen.
  110. These can also be combined with ``screen`` and ``ansi`` for formatting.
  111. .. note::
  112. For the types based on the output of a command, :ref:`shell_integration` is
  113. required.
  114. Using kittens to script kitty, without any terminal UI
  115. -----------------------------------------------------------
  116. If you would like your kitten to script kitty, without bothering to write a
  117. terminal program, you can tell the kittens system to run the ``handle_result()``
  118. function without first running the ``main()`` function.
  119. For example, here is a kitten that "zooms in/zooms out" the current terminal
  120. window by switching to the stack layout or back to the previous layout. This is
  121. equivalent to the builtin :ac:`toggle_layout` action.
  122. Create a Python file in the :ref:`kitty config directory <confloc>`,
  123. :file:`~/.config/kitty/zoom_toggle.py`
  124. .. code-block:: py
  125. from typing import List
  126. from kitty.boss import Boss
  127. def main(args: List[str]) -> str:
  128. pass
  129. from kittens.tui.handler import result_handler
  130. @result_handler(no_ui=True)
  131. def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
  132. tab = boss.active_tab
  133. if tab is not None:
  134. if tab.current_layout.name == 'stack':
  135. tab.last_used_layout()
  136. else:
  137. tab.goto_layout('stack')
  138. Now in :file:`kitty.conf` add::
  139. map f11 kitten zoom_toggle.py
  140. Pressing :kbd:`F11` will now act as a zoom toggle function. You can get even
  141. more fancy, switching the kitty OS window to fullscreen as well as changing the
  142. layout, by simply adding the line::
  143. boss.toggle_fullscreen()
  144. to the ``handle_result()`` function, above.
  145. .. _send_mouse_event:
  146. Sending mouse events
  147. --------------------
  148. If the program running in a window is receiving mouse events, you can simulate
  149. those using::
  150. from kitty.fast_data_types import send_mouse_event
  151. send_mouse_event(screen, x, y, button, action, mods)
  152. ``screen`` is the ``screen`` attribute of the window you want to send the event
  153. to. ``x`` and ``y`` are the 0-indexed coordinates. ``button`` is a number using
  154. the same numbering as X11 (left: ``1``, middle: ``2``, right: ``3``, scroll up:
  155. ``4``, scroll down: ``5``, scroll left: ``6``, scroll right: ``7``, back:
  156. ``8``, forward: ``9``). ``action`` is one of ``PRESS``, ``RELEASE``, ``DRAG``
  157. or ``MOVE``. ``mods`` is a bitmask of ``GLFW_MOD_{mod}`` where ``{mod}`` is one
  158. of ``SHIFT``, ``CONTROL`` or ``ALT``. All the mentioned constants are imported
  159. from ``kitty.fast_data_types``.
  160. For example, to send a left click at position x: 2, y: 3 to the active window::
  161. from kitty.fast_data_types import send_mouse_event, PRESS
  162. send_mouse_event(boss.active_window.screen, 2, 3, 1, PRESS, 0)
  163. The function will only send the event if the program is receiving events of
  164. that type, and will return ``True`` if it sent the event, and ``False`` if not.
  165. Debugging kittens
  166. --------------------
  167. The part of the kitten that runs in ``main()`` is just a normal program and the
  168. output of print statements will be visible in the kitten window. Or alternately,
  169. you can use::
  170. from kittens.tui.loop import debug
  171. debug('whatever')
  172. The ``debug()`` function is just like ``print()`` except that the output will
  173. appear in the ``STDOUT`` of the kitty process inside which the kitten is
  174. running.
  175. The ``handle_result()`` part of the kitten runs inside the kitty process.
  176. The output of print statements will go to the ``STDOUT`` of the kitty process.
  177. So if you run kitty from another kitty instance, the output will be visible
  178. in the first kitty instance.
  179. Adding options to kittens
  180. ----------------------------
  181. If you would like to use kitty's config framework to make your kittens
  182. configurable, you will need some boilerplate. Put the following files in the
  183. directory of your kitten.
  184. :file:`kitten_options_definition.py`
  185. .. code-block:: python
  186. from kitty.conf.types import Action, Definition
  187. definition = Definition(
  188. '!kitten_options_utils',
  189. Action(
  190. 'map', 'parse_map',
  191. {'key_definitions': 'kitty.conf.utils.KittensKeyMap'},
  192. ['kitty.types.ParsedShortcut', 'kitty.conf.utils.KeyAction']
  193. ),
  194. )
  195. agr = definition.add_group
  196. egr = definition.end_group
  197. opt = definition.add_option
  198. map = definition.add_map
  199. # main options {{{
  200. agr('main', 'Main')
  201. opt('some_option', '33',
  202. option_type='some_option_parser',
  203. long_text='''
  204. Help text for this option
  205. '''
  206. )
  207. egr() # }}}
  208. # shortcuts {{{
  209. agr('shortcuts', 'Keyboard shortcuts')
  210. map('Quit', 'quit q quit')
  211. egr() # }}}
  212. :file:`kitten_options_utils.py`
  213. .. code-block:: python
  214. from kitty.conf.utils import KittensKeyDefinition, key_func, parse_kittens_key
  215. func_with_args, args_funcs = key_func()
  216. FuncArgsType = Tuple[str, Sequence[Any]]
  217. def some_option_parser(val: str) -> int:
  218. return int(val) + 3000
  219. def parse_map(val: str) -> Iterable[KittensKeyDefinition]:
  220. x = parse_kittens_key(val, args_funcs)
  221. if x is not None:
  222. yield x
  223. Then run::
  224. kitty +runpy 'from kitty.conf.generate import main; main()' /path/to/kitten_options_definition.py
  225. You can parse and read the options in your kitten using the following code:
  226. .. code-block:: python
  227. from .kitten_options_types import Options, defaults
  228. from kitty.conf.utils import load_config as _load_config, parse_config_base
  229. from typing import Optional, Iterable, Dict, Any
  230. def load_config(*paths: str, overrides: Optional[Iterable[str]] = None) -> Options:
  231. from .kitten_options_parse import (
  232. create_result_dict, merge_result_dicts, parse_conf_item
  233. )
  234. def parse_config(lines: Iterable[str]) -> Dict[str, Any]:
  235. ans: Dict[str, Any] = create_result_dict()
  236. parse_config_base(
  237. lines,
  238. parse_conf_item,
  239. ans,
  240. )
  241. return ans
  242. overrides = tuple(overrides) if overrides is not None else ()
  243. opts_dict, found_paths = _load_config(defaults, parse_config, merge_result_dicts, *paths, overrides=overrides)
  244. opts = Options(opts_dict)
  245. opts.config_paths = found_paths
  246. opts.all_config_paths = paths
  247. opts.config_overrides = overrides
  248. return opts
  249. See `the code <https://github.com/kovidgoyal/kitty/tree/master/kittens/diff>`__
  250. for the builtin :doc:`diff kitten </kittens/diff>` for examples of creating more
  251. options and keyboard shortcuts.
  252. .. _external_kittens:
  253. Kittens created by kitty users
  254. ---------------------------------------------
  255. `vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
  256. Allows you to navigate seamlessly between vim and kitty splits using a
  257. consistent set of hotkeys.
  258. `smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
  259. Makes the kitty scroll bindings work in full screen applications
  260. :iss:`insert password <1222>`
  261. Insert a password from a CLI password manager, taking care to only do it at
  262. a password prompt.
  263. `weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_
  264. URL hints kitten for WeeChat that works without having to use WeeChat's
  265. raw-mode.