Compare commits
311 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad128a3c29 | ||
|
|
b71d2e04ca | ||
|
|
cc3c7b0286 | ||
|
|
bf6c90a69a | ||
|
|
4c9a75c1ba | ||
|
|
98f46f8bd7 | ||
|
|
dec62b1929 | ||
|
|
79f7954048 | ||
|
|
fc217dafba | ||
|
|
a6aff817cf | ||
|
|
38cb18fe92 | ||
|
|
20b6a97159 | ||
|
|
98f6e24106 | ||
|
|
4ed6364b61 | ||
|
|
cd7751681d | ||
|
|
76531d2f7a | ||
|
|
eea652f1d0 | ||
|
|
f20f4762a8 | ||
|
|
c004fd1f4d | ||
|
|
f29039db29 | ||
|
|
fbf1ec43c7 | ||
|
|
a42200a430 | ||
|
|
ad576c333c | ||
|
|
5a0a980648 | ||
|
|
b3578a4fa5 | ||
|
|
3c952b01d3 | ||
|
|
b1453a51f6 | ||
|
|
2c3a7b5f41 | ||
|
|
0df5a6c6c8 | ||
|
|
bf4dc6365a | ||
|
|
ce3322bf91 | ||
|
|
835cb31501 | ||
|
|
762dd065f3 | ||
|
|
7b91447d89 | ||
|
|
1146c4ea3f | ||
|
|
729f7b8e03 | ||
|
|
d3ec632b9f | ||
|
|
cb01b5ee44 | ||
|
|
0828096e9e | ||
|
|
ebcbed290f | ||
|
|
d6afe6f2cb | ||
|
|
4daf70f636 | ||
|
|
2f3dd72c84 | ||
|
|
dca8c65376 | ||
|
|
12d8175d9a | ||
|
|
609bcd68dd | ||
|
|
313136803c | ||
|
|
b937033411 | ||
|
|
55eeb9c11e | ||
|
|
8fbec2dfd5 | ||
|
|
c4521290b8 | ||
|
|
e12a9f3caf | ||
|
|
56f8a06362 | ||
|
|
5c4abe749b | ||
|
|
62c27b3db7 | ||
|
|
1283a2b7e5 | ||
|
|
bec620a8d3 | ||
|
|
6b0bb2e845 | ||
|
|
135818f34b | ||
|
|
2a361d1de1 | ||
|
|
761a941d43 | ||
|
|
c03630a487 | ||
|
|
0b79cbdb3b | ||
|
|
1dc6b49e02 | ||
|
|
1962f8e5a0 | ||
|
|
cabfcbcb69 | ||
|
|
48bb43a2c4 | ||
|
|
af5e3e0c18 | ||
|
|
fb8398c2eb | ||
|
|
eefd4bb3e3 | ||
|
|
581c388838 | ||
|
|
7a63ea6480 | ||
|
|
65f1329635 | ||
|
|
0df9a5d5c5 | ||
|
|
6405849a1b | ||
|
|
e9340dfaaf | ||
|
|
0509855930 | ||
|
|
4c74462763 | ||
|
|
7f69381221 | ||
|
|
75942fb47e | ||
|
|
0893d7164a | ||
|
|
27906ea853 | ||
|
|
8540ca3990 | ||
|
|
e82c5a63b5 | ||
|
|
83602d7e53 | ||
|
|
64e30e5299 | ||
|
|
83ce23ff9f | ||
|
|
c5e5ec618d | ||
|
|
bd84ec03ce | ||
|
|
d71f18566e | ||
|
|
76e75d9f89 | ||
|
|
f5695ac4f4 | ||
|
|
b748cd21cf | ||
|
|
3a8bdf3b48 | ||
|
|
196c6b07aa | ||
|
|
1978cc5b55 | ||
|
|
ea47cee23f | ||
|
|
e6844ad6f2 | ||
|
|
02d5b3eafa | ||
|
|
151dacf17f | ||
|
|
117d1b02be | ||
|
|
cb55878efd | ||
|
|
ea4751eae5 | ||
|
|
cc4f1c0a24 | ||
|
|
3eb5320e29 | ||
|
|
4f63cff1a4 | ||
|
|
8381171c8d | ||
|
|
1b2d54d97a | ||
|
|
e7da874b82 | ||
|
|
ea8bed2a71 | ||
|
|
c747e681a2 | ||
|
|
a9a9ec80b5 | ||
|
|
eb84990f5a | ||
|
|
1938ba3108 | ||
|
|
0eee2db199 | ||
|
|
bba1455e28 | ||
|
|
8c41cc8d3e | ||
|
|
5efcb35cfb | ||
|
|
1b4cf1fea7 | ||
|
|
d3656bf7e9 | ||
|
|
aaffec1cbc | ||
|
|
ed9391d4d6 | ||
|
|
58333f260b | ||
|
|
cf8ccabad9 | ||
|
|
600c595fdf | ||
|
|
2427d2d9da | ||
|
|
4ca70bfa26 | ||
|
|
4e8eb2f7f0 | ||
|
|
b6cf4bc78f | ||
|
|
622cd5531a | ||
|
|
ec35b0cc7c | ||
|
|
0719c7f8bb | ||
|
|
bb7e4039e8 | ||
|
|
a0559e506b | ||
|
|
4ce4176bbe | ||
|
|
99409f0a8b | ||
|
|
13303416b8 | ||
|
|
9811d677e5 | ||
|
|
b9fd039668 | ||
|
|
6c1f20bb27 | ||
|
|
2b58316c64 | ||
|
|
f2df629634 | ||
|
|
8f46505a50 | ||
|
|
c48bf4fd85 | ||
|
|
8c67f5aea8 | ||
|
|
76f84e32c4 | ||
|
|
4a7125ec92 | ||
|
|
92c3af6a92 | ||
|
|
443f36ebc7 | ||
|
|
17632dea3c | ||
|
|
bb78dc5ccb | ||
|
|
2737cb7dd0 | ||
|
|
08b2ce444f | ||
|
|
289028b468 | ||
|
|
2b3be147e6 | ||
|
|
1a2d9c6fba | ||
|
|
af15b0313a | ||
|
|
b080296326 | ||
|
|
516e0e8bb3 | ||
|
|
57e23bd4e3 | ||
|
|
59534d620e | ||
|
|
068b7e8d63 | ||
|
|
2c1edb9611 | ||
|
|
510022c3c1 | ||
|
|
627c79ffbb | ||
|
|
e80cd36237 | ||
|
|
92ebada9a6 | ||
|
|
462ae3bb58 | ||
|
|
6d6d9cc26b | ||
|
|
a36d5dcde1 | ||
|
|
f9f6f98527 | ||
|
|
ee94114eb2 | ||
|
|
8e98fcf2f6 | ||
|
|
c4710bf9cb | ||
|
|
97a568a405 | ||
|
|
7bace19aed | ||
|
|
7282f1f684 | ||
|
|
9edb772a81 | ||
|
|
d123b6c377 | ||
|
|
c9ba3695d3 | ||
|
|
0ee970b418 | ||
|
|
8239cb1b5a | ||
|
|
97caa073b0 | ||
|
|
45bbe17559 | ||
|
|
288d16f7be | ||
|
|
ecb60b313e | ||
|
|
cf2a20e4ea | ||
|
|
bc4f40fea7 | ||
|
|
102097da5a | ||
|
|
0e3528df14 | ||
|
|
214631c2dd | ||
|
|
e919857dfc | ||
|
|
a5bd1dcb08 | ||
|
|
37cdaea9ed | ||
|
|
6bbaf5f1cf | ||
|
|
8808a252ae | ||
|
|
5f1376b9a0 | ||
|
|
efa873bf50 | ||
|
|
714461de76 | ||
|
|
b753cf6879 | ||
|
|
27010d0446 | ||
|
|
bdc039fbf5 | ||
|
|
72f92f3174 | ||
|
|
8fcd5e40d4 | ||
|
|
b76319b7e8 | ||
|
|
feeb8f28c4 | ||
|
|
55b21b741e | ||
|
|
6941884221 | ||
|
|
6174c2008f | ||
|
|
1a32e79470 | ||
|
|
dd032db89c | ||
|
|
f70e0e216c | ||
|
|
51362706d7 | ||
|
|
b2c317ebc6 | ||
|
|
90acbd0dd5 | ||
|
|
402fac3edf | ||
|
|
c4c62c1505 | ||
|
|
b569c01b49 | ||
|
|
c0d9b6e979 | ||
|
|
b94afbba13 | ||
|
|
1994c17c75 | ||
|
|
f88a4fe986 | ||
|
|
25616aaa7b | ||
|
|
19fdcec358 | ||
|
|
45eb4a0760 | ||
|
|
9e026983e6 | ||
|
|
be0a524e23 | ||
|
|
339af1b4b2 | ||
|
|
31fda568e4 | ||
|
|
3efbccc850 | ||
|
|
93c23f99cb | ||
|
|
6590d0690e | ||
|
|
230a9f4678 | ||
|
|
f2189b3e70 | ||
|
|
0ee5712e00 | ||
|
|
f9cad2c4ea | ||
|
|
4372cf2893 | ||
|
|
34c18bacd8 | ||
|
|
3af11e92d6 | ||
|
|
74d5f2c259 | ||
|
|
291f9e9a5e | ||
|
|
38e1d32065 | ||
|
|
b45fedd794 | ||
|
|
df9b13fb74 | ||
|
|
53788c4c89 | ||
|
|
831043b773 | ||
|
|
1daf745d74 | ||
|
|
d6c5b40ead | ||
|
|
57ea524464 | ||
|
|
e19ce6cce6 | ||
|
|
8d4f6681e8 | ||
|
|
9c855a8377 | ||
|
|
716813e38a | ||
|
|
e5322cdc52 | ||
|
|
b5701691c6 | ||
|
|
aab9482e31 | ||
|
|
775584b5a5 | ||
|
|
25f022cc14 | ||
|
|
981ad88309 | ||
|
|
e71b9091a3 | ||
|
|
bde468594e | ||
|
|
0fcfe104e3 | ||
|
|
4cf54d2dfb | ||
|
|
b866c3e783 | ||
|
|
c15a31e725 | ||
|
|
afaf866b15 | ||
|
|
af6b1837cb | ||
|
|
aab6f3e450 | ||
|
|
829ed0ec0d | ||
|
|
d9899cb489 | ||
|
|
1a370ea9b6 | ||
|
|
cc07b1f79d | ||
|
|
3ddea42660 | ||
|
|
236dbd95c9 | ||
|
|
6b078c4267 | ||
|
|
1dec5f2e08 | ||
|
|
e5f70b7306 | ||
|
|
e2bb856e41 | ||
|
|
88d597f6b2 | ||
|
|
d0c0b01724 | ||
|
|
4b7c50518e | ||
|
|
a1bfcd9fc5 | ||
|
|
eb024fa40a | ||
|
|
122475ad4d | ||
|
|
f7114bc1c5 | ||
|
|
feea6998f8 | ||
|
|
e187110611 | ||
|
|
c19e69855a | ||
|
|
7788f48dd5 | ||
|
|
073eecb6bb | ||
|
|
030e7e2db3 | ||
|
|
aead3c1c35 | ||
|
|
587f44ad4e | ||
|
|
c9ef4aa8c8 | ||
|
|
aabadd8517 | ||
|
|
b8866371a3 | ||
|
|
0f3883af02 | ||
|
|
5876ce0845 | ||
|
|
2c72c56e22 | ||
|
|
f15ce21da1 | ||
|
|
b3fa7310cb | ||
|
|
ade38870a0 | ||
|
|
11bc1b100c | ||
|
|
93a7b220c9 | ||
|
|
8f92c594f2 | ||
|
|
afebea8635 | ||
|
|
5d76cfb578 | ||
|
|
f085f853bd | ||
|
|
4ac2312b2a | ||
|
|
c6dcbe6521 | ||
|
|
49efbf0c6e |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -20,15 +20,15 @@ jobs:
|
|||||||
cc: [gcc, clang]
|
cc: [gcc, clang]
|
||||||
include:
|
include:
|
||||||
- python: a
|
- python: a
|
||||||
pyver: 3.7
|
pyver: "3.8"
|
||||||
sanitize: 0
|
sanitize: 0
|
||||||
|
|
||||||
- python: b
|
- python: b
|
||||||
pyver: 3.8
|
pyver: "3.9"
|
||||||
sanitize: 1
|
sanitize: 1
|
||||||
|
|
||||||
- python: c
|
- python: c
|
||||||
pyver: 3.9
|
pyver: "3.10"
|
||||||
sanitize: 1
|
sanitize: 1
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 10
|
fetch-depth: 0 # needed for :commit: docs role
|
||||||
|
|
||||||
- name: Test for trailing whitespace
|
- name: Test for trailing whitespace
|
||||||
run: if grep -Inr '\s$' kitty kitty_tests kittens docs *.py *.asciidoc *.rst .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi
|
run: if grep -Inr '\s$' kitty kitty_tests kittens docs *.py *.asciidoc *.rst .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi
|
||||||
|
|||||||
180
__main__.py
180
__main__.py
@@ -1,185 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
|
|
||||||
def icat(args: List[str]) -> None:
|
|
||||||
from kittens.runner import run_kitten as rk
|
|
||||||
sys.argv = args
|
|
||||||
rk('icat')
|
|
||||||
|
|
||||||
|
|
||||||
def list_fonts(args: List[str]) -> None:
|
|
||||||
from kitty.fonts.list import main as list_main
|
|
||||||
list_main(args)
|
|
||||||
|
|
||||||
|
|
||||||
def remote_control(args: List[str]) -> None:
|
|
||||||
from kitty.remote_control import main as rc_main
|
|
||||||
rc_main(args)
|
|
||||||
|
|
||||||
|
|
||||||
def runpy(args: List[str]) -> None:
|
|
||||||
if len(args) < 2:
|
|
||||||
raise SystemExit('Usage: kitty +runpy "some python code"')
|
|
||||||
sys.argv = ['kitty'] + args[2:]
|
|
||||||
exec(args[1])
|
|
||||||
|
|
||||||
|
|
||||||
def hold(args: List[str]) -> None:
|
|
||||||
import subprocess
|
|
||||||
ret = 1
|
|
||||||
try:
|
|
||||||
ret = subprocess.Popen(args[1:]).wait()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
except FileNotFoundError:
|
|
||||||
print(f'Could not find {args[1]!r} to execute', file=sys.stderr)
|
|
||||||
except Exception as e:
|
|
||||||
print(e, file=sys.stderr)
|
|
||||||
from kitty.utils import hold_till_enter
|
|
||||||
hold_till_enter()
|
|
||||||
raise SystemExit(ret)
|
|
||||||
|
|
||||||
|
|
||||||
def complete(args: List[str]) -> None:
|
|
||||||
from kitty.complete import main as complete_main
|
|
||||||
complete_main(args[1:], entry_points, namespaced_entry_points)
|
|
||||||
|
|
||||||
|
|
||||||
def open_urls(args: List[str]) -> None:
|
|
||||||
setattr(sys, 'cmdline_args_for_open', True)
|
|
||||||
sys.argv = ['kitty'] + args[1:]
|
|
||||||
from kitty.main import main as kitty_main
|
|
||||||
kitty_main()
|
|
||||||
|
|
||||||
|
|
||||||
def launch(args: List[str]) -> None:
|
|
||||||
import runpy
|
|
||||||
sys.argv = args[1:]
|
|
||||||
try:
|
|
||||||
exe = args[1]
|
|
||||||
except IndexError:
|
|
||||||
raise SystemExit(
|
|
||||||
'usage: kitty +launch script.py [arguments to be passed to script.py ...]\n\n'
|
|
||||||
'script.py will be run with full access to kitty code. If script.py is '
|
|
||||||
'prefixed with a : it will be searched for in PATH'
|
|
||||||
)
|
|
||||||
if exe.startswith(':'):
|
|
||||||
import shutil
|
|
||||||
q = shutil.which(exe[1:])
|
|
||||||
if not q:
|
|
||||||
raise SystemExit(f'{exe[1:]} not found in PATH')
|
|
||||||
exe = q
|
|
||||||
if not os.path.exists(exe):
|
|
||||||
raise SystemExit(f'{exe} does not exist')
|
|
||||||
runpy.run_path(exe, run_name='__main__')
|
|
||||||
|
|
||||||
|
|
||||||
def shebang(args: List[str]) -> None:
|
|
||||||
script_path = args[1]
|
|
||||||
cmd = args[2:]
|
|
||||||
if cmd == ['__ext__']:
|
|
||||||
cmd = [os.path.splitext(script_path)[1][1:].lower()]
|
|
||||||
try:
|
|
||||||
f = open(script_path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
raise SystemExit(f'The file {script_path} does not exist')
|
|
||||||
with f:
|
|
||||||
if f.read(2) == '#!':
|
|
||||||
line = f.readline().strip()
|
|
||||||
_plat = sys.platform.lower()
|
|
||||||
is_macos: bool = 'darwin' in _plat
|
|
||||||
if is_macos:
|
|
||||||
cmd = line.split(' ')
|
|
||||||
else:
|
|
||||||
cmd = line.split(' ', maxsplit=1)
|
|
||||||
os.execvp(cmd[0], cmd + [script_path])
|
|
||||||
|
|
||||||
|
|
||||||
def run_kitten(args: List[str]) -> None:
|
|
||||||
try:
|
|
||||||
kitten = args[1]
|
|
||||||
except IndexError:
|
|
||||||
from kittens.runner import list_kittens
|
|
||||||
list_kittens()
|
|
||||||
raise SystemExit(1)
|
|
||||||
sys.argv = args[1:]
|
|
||||||
from kittens.runner import run_kitten as rk
|
|
||||||
rk(kitten)
|
|
||||||
|
|
||||||
|
|
||||||
def edit_config_file(args: List[str]) -> None:
|
|
||||||
from kitty.cli import create_default_opts
|
|
||||||
from kitty.fast_data_types import set_options
|
|
||||||
from kitty.utils import edit_config_file as f
|
|
||||||
set_options(create_default_opts())
|
|
||||||
f()
|
|
||||||
|
|
||||||
|
|
||||||
def namespaced(args: List[str]) -> None:
|
|
||||||
try:
|
|
||||||
func = namespaced_entry_points[args[1]]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
func(args[1:])
|
|
||||||
return
|
|
||||||
raise SystemExit(f'{args[1]} is not a known entry point. Choices are: ' + ', '.join(namespaced_entry_points))
|
|
||||||
|
|
||||||
|
|
||||||
entry_points = {
|
|
||||||
# These two are here for backwards compat
|
|
||||||
'icat': icat,
|
|
||||||
'list-fonts': list_fonts,
|
|
||||||
|
|
||||||
'@': remote_control,
|
|
||||||
'+': namespaced,
|
|
||||||
}
|
|
||||||
namespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}
|
|
||||||
namespaced_entry_points['hold'] = hold
|
|
||||||
namespaced_entry_points['complete'] = complete
|
|
||||||
namespaced_entry_points['runpy'] = runpy
|
|
||||||
namespaced_entry_points['launch'] = launch
|
|
||||||
namespaced_entry_points['open'] = open_urls
|
|
||||||
namespaced_entry_points['kitten'] = run_kitten
|
|
||||||
namespaced_entry_points['edit-config'] = edit_config_file
|
|
||||||
namespaced_entry_points['shebang'] = shebang
|
|
||||||
|
|
||||||
|
|
||||||
def setup_openssl_environment() -> None:
|
|
||||||
# Use our bundled CA certificates instead of the system ones, since
|
|
||||||
# many systems come with no certificates in a useable form or have various
|
|
||||||
# locations for the certificates.
|
|
||||||
d = os.path.dirname
|
|
||||||
ext_dir: str = getattr(sys, 'kitty_extensions_dir')
|
|
||||||
if 'darwin' in sys.platform.lower():
|
|
||||||
cert_file = os.path.join(d(d(d(ext_dir))), 'cacert.pem')
|
|
||||||
else:
|
|
||||||
cert_file = os.path.join(d(ext_dir), 'cacert.pem')
|
|
||||||
os.environ['SSL_CERT_FILE'] = cert_file
|
|
||||||
setattr(sys, 'kitty_ssl_env_var', 'SSL_CERT_FILE')
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
if getattr(sys, 'frozen', False) and getattr(sys, 'kitty_extensions_dir', ''):
|
|
||||||
setup_openssl_environment()
|
|
||||||
first_arg = '' if len(sys.argv) < 2 else sys.argv[1]
|
|
||||||
func = entry_points.get(first_arg)
|
|
||||||
if func is None:
|
|
||||||
if first_arg.startswith('@'):
|
|
||||||
remote_control(['@', first_arg[1:]] + sys.argv[2:])
|
|
||||||
elif first_arg.startswith('+'):
|
|
||||||
namespaced(['+', first_arg[1:]] + sys.argv[2:])
|
|
||||||
else:
|
|
||||||
from kitty.main import main as kitty_main
|
|
||||||
kitty_main()
|
|
||||||
else:
|
|
||||||
func(sys.argv[1:])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
from kitty.entry_points import main
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -13,11 +12,10 @@ import tempfile
|
|||||||
|
|
||||||
def compile_terminfo(base):
|
def compile_terminfo(base):
|
||||||
with tempfile.TemporaryDirectory() as tdir:
|
with tempfile.TemporaryDirectory() as tdir:
|
||||||
proc = subprocess.run(['tic', '-x', f'-o{tdir}', 'terminfo/kitty.terminfo'], check=True, stderr=subprocess.PIPE)
|
proc = subprocess.run(['tic', '-x', f'-o{tdir}', 'terminfo/kitty.terminfo'], capture_output=True)
|
||||||
regex = '^"terminfo/kitty.terminfo", line [0-9]+, col [0-9]+, terminal \'xterm-kitty\': older tic versions may treat the description field as an alias$'
|
if proc.returncode != 0:
|
||||||
for error in proc.stderr.decode('utf-8').splitlines():
|
sys.stderr.buffer.write(proc.stderr)
|
||||||
if not re.match(regex, error):
|
raise SystemExit(proc.returncode)
|
||||||
print(error, file=sys.stderr)
|
|
||||||
tfiles = glob.glob(os.path.join(tdir, '*', 'xterm-kitty'))
|
tfiles = glob.glob(os.path.join(tdir, '*', 'xterm-kitty'))
|
||||||
if not tfiles:
|
if not tfiles:
|
||||||
raise SystemExit('tic failed to output the compiled kitty terminfo file')
|
raise SystemExit('tic failed to output the compiled kitty terminfo file')
|
||||||
@@ -40,6 +38,13 @@ def generate_terminfo():
|
|||||||
|
|
||||||
with open('terminfo/kitty.terminfo', 'w') as f:
|
with open('terminfo/kitty.terminfo', 'w') as f:
|
||||||
f.write(generate_terminfo())
|
f.write(generate_terminfo())
|
||||||
|
proc = subprocess.run(['tic', '-CrT0', 'terminfo/kitty.terminfo'], capture_output=True)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
sys.stderr.buffer.write(proc.stderr)
|
||||||
|
raise SystemExit(proc.returncode)
|
||||||
|
tcap = proc.stdout.decode('utf-8').splitlines()[-1]
|
||||||
|
with open('terminfo/kitty.termcap', 'w') as f:
|
||||||
|
f.write(tcap)
|
||||||
|
|
||||||
compile_terminfo(os.path.join(base, 'terminfo'))
|
compile_terminfo(os.path.join(base, 'terminfo'))
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ def binary_includes():
|
|||||||
'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16', 'lcms2', 'crypt',
|
'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16', 'lcms2', 'crypt',
|
||||||
'iconv', 'pcre', 'graphite2', 'glib-2.0', 'freetype', 'rsync',
|
'iconv', 'pcre', 'graphite2', 'glib-2.0', 'freetype', 'rsync',
|
||||||
'harfbuzz', 'xkbcommon', 'xkbcommon-x11',
|
'harfbuzz', 'xkbcommon', 'xkbcommon-x11',
|
||||||
|
# fontconfig is not bundled because in typical brain dead Linux
|
||||||
|
# distro fashion, different distros use different default config
|
||||||
|
# paths for fontconfig.
|
||||||
'ncursesw', 'readline', 'brotlicommon', 'brotlienc', 'brotlidec',
|
'ncursesw', 'readline', 'brotlicommon', 'brotlienc', 'brotlidec',
|
||||||
'wayland-client', 'wayland-cursor',
|
'wayland-client', 'wayland-cursor',
|
||||||
))) + (
|
))) + (
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
Tabs and Windows
|
Tabs and Windows
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|kitty| is capable of running multiple programs organized into tabs and
|
|kitty| is capable of running multiple programs organized into tabs and windows.
|
||||||
windows. The top level of organization is the *Tab*. Each tab consists
|
The top level of organization is the :term:`OS window <os_window>`. Each OS
|
||||||
of one or more *windows*. The windows can be arranged in multiple
|
window consists of one or more :term:`tabs <tab>`. Each tab consists of one or more
|
||||||
different layouts, like windows are organized in a tiling window
|
:term:`kitty windows <window>`. The kitty windows can be arranged in multiple
|
||||||
manager. The keyboard controls (which are all customizable) for tabs and
|
different :term:`layouts <layout>`, like windows are organized in a tiling
|
||||||
windows are:
|
window manager. The keyboard controls (which are :ref:`all customizable
|
||||||
|
<conf-kitty-shortcuts>`) for tabs and windows are:
|
||||||
|
|
||||||
Scrolling
|
Scrolling
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
@@ -64,8 +65,9 @@ Focus specific window :sc:`first_window`, :sc:`second_window` ... :sc:`ten
|
|||||||
(clockwise from the top-left)
|
(clockwise from the top-left)
|
||||||
======================== =======================
|
======================== =======================
|
||||||
|
|
||||||
Additionally, you can define shortcuts in :file:`kitty.conf` to focus neighboring
|
Additionally, you can define shortcuts in :file:`kitty.conf` to focus
|
||||||
windows and move windows around (similar to window movement in vim)::
|
neighboring windows and move windows around (similar to window movement in
|
||||||
|
:program:`vim`)::
|
||||||
|
|
||||||
map ctrl+left neighboring_window left
|
map ctrl+left neighboring_window left
|
||||||
map shift+left move_window right
|
map shift+left move_window right
|
||||||
@@ -77,20 +79,20 @@ You can also define a shortcut to switch to the previously active window::
|
|||||||
|
|
||||||
map ctrl+p nth_window -1
|
map ctrl+p nth_window -1
|
||||||
|
|
||||||
``nth_window`` will focus the nth window for positive numbers (starting from
|
:ac:`nth_window` will focus the nth window for positive numbers (starting from
|
||||||
zero) and the previously active windows for negative numbers.
|
zero) and the previously active windows for negative numbers.
|
||||||
|
|
||||||
To switch to the nth OS window, you can define ``nth_os_window``. Only positive
|
To switch to the nth OS window, you can define :ac:`nth_os_window`. Only
|
||||||
numbers are accepted, starting from one.
|
positive numbers are accepted, starting from one.
|
||||||
|
|
||||||
.. _detach_window:
|
.. _detach_window:
|
||||||
|
|
||||||
You can define shortcuts to detach the current window and
|
You can define shortcuts to detach the current window and move it to another tab
|
||||||
move it to another tab or another OS window::
|
or another OS window::
|
||||||
|
|
||||||
# moves the window into a new OS window
|
# moves the window into a new OS window
|
||||||
map ctrl+f2 detach_window
|
map ctrl+f2 detach_window
|
||||||
# moves the window into a new Tab
|
# moves the window into a new tab
|
||||||
map ctrl+f3 detach_window new-tab
|
map ctrl+f3 detach_window new-tab
|
||||||
# moves the window into the previously active tab
|
# moves the window into the previously active tab
|
||||||
map ctrl+f3 detach_window tab-prev
|
map ctrl+f3 detach_window tab-prev
|
||||||
@@ -106,8 +108,8 @@ Similarly, you can detach the current tab, with::
|
|||||||
# asks which OS Window to move the tab into
|
# asks which OS Window to move the tab into
|
||||||
map ctrl+f4 detach_tab ask
|
map ctrl+f4 detach_tab ask
|
||||||
|
|
||||||
Finally, you can define a shortcut to close all windows in a tab other than
|
Finally, you can define a shortcut to close all windows in a tab other than the
|
||||||
the currently active window::
|
currently active window::
|
||||||
|
|
||||||
map f9 close_other_windows_in_tab
|
map f9 close_other_windows_in_tab
|
||||||
|
|
||||||
@@ -124,18 +126,18 @@ Action Shortcut
|
|||||||
Copy to clipboard :sc:`copy_to_clipboard` (also :kbd:`⌘+c` on macOS)
|
Copy to clipboard :sc:`copy_to_clipboard` (also :kbd:`⌘+c` on macOS)
|
||||||
Paste from clipboard :sc:`paste_from_clipboard` (also :kbd:`⌘+v` on macOS)
|
Paste from clipboard :sc:`paste_from_clipboard` (also :kbd:`⌘+v` on macOS)
|
||||||
Paste from selection :sc:`paste_from_selection`
|
Paste from selection :sc:`paste_from_selection`
|
||||||
|
Pass selection to program :sc:`pass_selection_to_program`
|
||||||
Increase font size :sc:`increase_font_size` (also :kbd:`⌘++` on macOS)
|
Increase font size :sc:`increase_font_size` (also :kbd:`⌘++` on macOS)
|
||||||
Decrease font size :sc:`decrease_font_size` (also :kbd:`⌘+-` on macOS)
|
Decrease font size :sc:`decrease_font_size` (also :kbd:`⌘+-` on macOS)
|
||||||
Restore font size :sc:`reset_font_size` (also :kbd:`⌘+0` on macOS)
|
Restore font size :sc:`reset_font_size` (also :kbd:`⌘+0` on macOS)
|
||||||
Toggle fullscreen :sc:`toggle_fullscreen` (also :kbd:`⌃+⌘+f` on macOS)
|
Toggle fullscreen :sc:`toggle_fullscreen` (also :kbd:`⌃+⌘+f` on macOS)
|
||||||
Toggle maximized :sc:`toggle_maximized`
|
Toggle maximized :sc:`toggle_maximized`
|
||||||
Input unicode character :sc:`input_unicode_character` (also :kbd:`⌃+⌘+space` on macOS)
|
Input Unicode character :sc:`input_unicode_character` (also :kbd:`⌃+⌘+space` on macOS)
|
||||||
Open URL in web browser :sc:`open_url`
|
Open URL in web browser :sc:`open_url`
|
||||||
Reset the terminal :sc:`reset_terminal` (also :kbd:`⌥+⌘+r` on macOS)
|
Reset the terminal :sc:`reset_terminal` (also :kbd:`⌥+⌘+r` on macOS)
|
||||||
|
Edit :file:`kitty.conf` :sc:`edit_config_file` (also :kbd:`⌘+,` on macOS)
|
||||||
Reload :file:`kitty.conf` :sc:`reload_config_file` (also :kbd:`⌃+⌘+,` on macOS)
|
Reload :file:`kitty.conf` :sc:`reload_config_file` (also :kbd:`⌃+⌘+,` on macOS)
|
||||||
Debug :file:`kitty.conf` :sc:`debug_config` (also :kbd:`⌥+⌘+,` on macOS)
|
Debug :file:`kitty.conf` :sc:`debug_config` (also :kbd:`⌥+⌘+,` on macOS)
|
||||||
Pass current selection to program :sc:`pass_selection_to_program`
|
|
||||||
Edit |kitty| config file :sc:`edit_config_file` (also :kbd:`⌘+,` on macOS)
|
|
||||||
Open a |kitty| shell :sc:`kitty_shell`
|
Open a |kitty| shell :sc:`kitty_shell`
|
||||||
Increase background opacity :sc:`increase_background_opacity`
|
Increase background opacity :sc:`increase_background_opacity`
|
||||||
Decrease background opacity :sc:`decrease_background_opacity`
|
Decrease background opacity :sc:`decrease_background_opacity`
|
||||||
|
|||||||
@@ -4,17 +4,14 @@ Install kitty
|
|||||||
Binary install
|
Binary install
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
.. |ins| replace:: curl -L :literal:`https://sw.kovidgoyal.net/kitty/installer.sh` | sh /dev/stdin
|
|
||||||
|
|
||||||
.. highlight:: sh
|
.. highlight:: sh
|
||||||
|
|
||||||
You can install pre-built binaries of |kitty| if you are on macOS or Linux using
|
You can install pre-built binaries of |kitty| if you are on macOS or Linux using
|
||||||
the following simple command:
|
the following simple command:
|
||||||
|
|
||||||
.. parsed-literal::
|
.. code-block:: sh
|
||||||
:class: pre
|
|
||||||
|
|
||||||
|ins|
|
_kitty_install_cmd
|
||||||
|
|
||||||
|
|
||||||
The binaries will be installed in the standard location for your OS,
|
The binaries will be installed in the standard location for your OS,
|
||||||
@@ -24,7 +21,7 @@ simply re-run the command.
|
|||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
**Do not** copy the kitty binary out of the installation folder. If you want
|
**Do not** copy the kitty binary out of the installation folder. If you want
|
||||||
to add it to your ``PATH`` create a symlink in :file:`~/.local/bin` or
|
to add it to your :envvar:`PATH`, create a symlink in :file:`~/.local/bin` or
|
||||||
:file:`/usr/bin` or wherever.
|
:file:`/usr/bin` or wherever.
|
||||||
|
|
||||||
|
|
||||||
@@ -33,11 +30,12 @@ Manually installing
|
|||||||
|
|
||||||
If something goes wrong or you simply do not want to run the installer, you can
|
If something goes wrong or you simply do not want to run the installer, you can
|
||||||
manually download and install |kitty| from the `GitHub releases page
|
manually download and install |kitty| from the `GitHub releases page
|
||||||
<https://github.com/kovidgoyal/kitty/releases>`_. If you are on macOS, download
|
<https://github.com/kovidgoyal/kitty/releases>`__. If you are on macOS, download
|
||||||
the :file:`.dmg` and install as normal. If you are on Linux, download the tarball
|
the :file:`.dmg` and install as normal. If you are on Linux, download the
|
||||||
and extract it into a directory. The |kitty| executable will be in the
|
tarball and extract it into a directory. The |kitty| executable will be in the
|
||||||
:file:`bin` sub-directory.
|
:file:`bin` sub-directory.
|
||||||
|
|
||||||
|
|
||||||
Desktop integration on Linux
|
Desktop integration on Linux
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
@@ -60,9 +58,19 @@ particular desktop, but it should work for most major desktop environments.
|
|||||||
sed -i "s|Exec=kitty|Exec=/home/$USER/.local/kitty.app/bin/kitty|g" ~/.local/share/applications/kitty*.desktop
|
sed -i "s|Exec=kitty|Exec=/home/$USER/.local/kitty.app/bin/kitty|g" ~/.local/share/applications/kitty*.desktop
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If you use the venerable `stow <https://www.gnu.org/software/stow/>`_
|
In :file:`kitty-open.desktop`, kitty is registered to handle some supported
|
||||||
|
MIME types. This will cause kitty to take precedence on some systems where
|
||||||
|
the default apps are not explicitly set. For example, you expect to use
|
||||||
|
other GUI file managers to open dir paths when using commands such as
|
||||||
|
:program:`xdg-open`, you should configure the default opener for the MIME
|
||||||
|
type ``inode/directory``::
|
||||||
|
|
||||||
|
xdg-mime default org.kde.dolphin.desktop inode/directory
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If you use the venerable `stow <https://www.gnu.org/software/stow/>`__
|
||||||
command to manage your manual installations, the following takes care of the
|
command to manage your manual installations, the following takes care of the
|
||||||
above for you (use with :file:`dest=~/.local/stow`)::
|
above for you (use with :code:`dest=~/.local/stow`)::
|
||||||
|
|
||||||
cd ~/.local/stow
|
cd ~/.local/stow
|
||||||
stow -v kitty.app
|
stow -v kitty.app
|
||||||
@@ -75,44 +83,39 @@ Customizing the installation
|
|||||||
|
|
||||||
* You can install the latest nightly kitty build with ``installer``:
|
* You can install the latest nightly kitty build with ``installer``:
|
||||||
|
|
||||||
.. parsed-literal::
|
.. code-block:: sh
|
||||||
:class: pre
|
|
||||||
|
|
||||||
|ins| \\
|
_kitty_install_cmd \\
|
||||||
installer=nightly
|
installer=nightly
|
||||||
|
|
||||||
If you want to install it in parallel to the released kitty specify a
|
If you want to install it in parallel to the released kitty specify a
|
||||||
different install locations with ``dest``:
|
different install locations with ``dest``:
|
||||||
|
|
||||||
.. parsed-literal::
|
.. code-block:: sh
|
||||||
:class: pre
|
|
||||||
|
|
||||||
|ins| \\
|
_kitty_install_cmd \\
|
||||||
installer=nightly dest=/some/other/location
|
installer=nightly dest=/some/other/location
|
||||||
|
|
||||||
* You can specify a different install location, with ``dest``:
|
* You can specify a different install location, with ``dest``:
|
||||||
|
|
||||||
.. parsed-literal::
|
.. code-block:: sh
|
||||||
:class: pre
|
|
||||||
|
|
||||||
|ins| \\
|
_kitty_install_cmd \\
|
||||||
dest=/some/other/location
|
dest=/some/other/location
|
||||||
|
|
||||||
* You can tell the installer not to launch |kitty| after installing it with
|
* You can tell the installer not to launch |kitty| after installing it with
|
||||||
``launch=n``:
|
``launch=n``:
|
||||||
|
|
||||||
.. parsed-literal::
|
.. code-block:: sh
|
||||||
:class: pre
|
|
||||||
|
|
||||||
|ins| \\
|
_kitty_install_cmd \\
|
||||||
launch=n
|
launch=n
|
||||||
|
|
||||||
* You can use a previously downloaded dmg/tarball, with ``installer``:
|
* You can use a previously downloaded dmg/tarball, with ``installer``:
|
||||||
|
|
||||||
.. parsed-literal::
|
.. code-block:: sh
|
||||||
:class: pre
|
|
||||||
|
|
||||||
|ins| \\
|
_kitty_install_cmd \\
|
||||||
installer=/path/to/dmg or tarball
|
installer=/path/to/dmg or tarball
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,21 +7,21 @@ Build from source
|
|||||||
|
|
||||||
.. highlight:: sh
|
.. highlight:: sh
|
||||||
|
|
||||||
|kitty| is designed to run from source, for easy hack-ability. Make sure
|
|kitty| is designed to run from source, for easy hack-ability. Make sure the
|
||||||
the following dependencies are installed first.
|
following dependencies are installed first.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If you just want to test the latest changes to kitty you dont need to build
|
If you just want to test the latest changes to kitty, you don't need to build
|
||||||
from source. Instead install the :ref:`latest nightly build <nightly>`.
|
from source. Instead install the :ref:`latest nightly build <nightly>`.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If you are making small changes only to the python parts of kitty, there is no need to
|
If you are making small changes only to the Python parts of kitty, there is
|
||||||
build kitty at all, instead, assuming you have installed the official kitty
|
no need to build kitty at all, instead, assuming you have installed the
|
||||||
binaries, you can simply set the KITTY_DEVELOP_FROM enviroment variable to
|
official kitty binaries, you can simply set the :envvar:`KITTY_DEVELOP_FROM`
|
||||||
point to the directory into which you have checked out the kitty source
|
enviroment variable to point to the directory into which you have checked out
|
||||||
code. kitty will then load its python code from there. You should use a
|
the kitty source code. kitty will then load its Python code from there. You
|
||||||
version of the source that matches the binary version as closely as
|
should use a version of the source that matches the binary version as closely
|
||||||
possible, since the two are tightly coupled.
|
as possible, since the two are tightly coupled.
|
||||||
|
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
@@ -46,7 +46,9 @@ Build-time dependencies:
|
|||||||
|
|
||||||
* ``gcc`` or ``clang``
|
* ``gcc`` or ``clang``
|
||||||
* ``pkg-config``
|
* ``pkg-config``
|
||||||
* For building on Linux in addition to the above dependencies you might also need to install the following packages, if they are not already installed by your distro:
|
* For building on Linux in addition to the above dependencies you might also
|
||||||
|
need to install the following packages, if they are not already installed by
|
||||||
|
your distro:
|
||||||
|
|
||||||
- ``libdbus-1-dev``
|
- ``libdbus-1-dev``
|
||||||
- ``libxcursor-dev``
|
- ``libxcursor-dev``
|
||||||
@@ -77,9 +79,8 @@ You can run |kitty|, as::
|
|||||||
|
|
||||||
./kitty/launcher/kitty
|
./kitty/launcher/kitty
|
||||||
|
|
||||||
If that works, you can create a symlink to the launcher in :file:`~/bin` or
|
If that works, you can create a symlink to the launcher in :file:`~/bin` or some
|
||||||
some other directory on your PATH so that you can run |kitty| using
|
other directory on your PATH so that you can run |kitty| using just ``kitty``.
|
||||||
just ``kitty``.
|
|
||||||
|
|
||||||
|
|
||||||
Building kitty.app on macOS from source
|
Building kitty.app on macOS from source
|
||||||
@@ -90,25 +91,24 @@ Run::
|
|||||||
make app
|
make app
|
||||||
|
|
||||||
This :file:`kitty.app` unlike the released one does not include its own copy of
|
This :file:`kitty.app` unlike the released one does not include its own copy of
|
||||||
python and the other dependencies. So if you ever un-install/upgrade those dependencies
|
Python and the other dependencies. So if you ever un-install/upgrade those
|
||||||
you might have to rebuild the app.
|
dependencies you might have to rebuild the app.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The released :file:`kitty.dmg` includes all dependencies, unlike the
|
The released :file:`kitty.dmg` includes all dependencies, unlike the
|
||||||
:file:`kitty.app` built above and is built automatically by using the
|
:file:`kitty.app` built above and is built automatically by using the
|
||||||
`bypy framework <https://github.com/kovidgoyal/bypy>`_ however, that is
|
`bypy framework <https://github.com/kovidgoyal/bypy>`__ however, that is
|
||||||
designed to run on Linux and is not for the faint of heart.
|
designed to run on Linux and is not for the faint of heart.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Apple disallows certain functionality, such as notifications for unsigned applications.
|
Apple disallows certain functionality, such as notifications for unsigned
|
||||||
If you need this functionality, you can try signing the built kitty.app with
|
applications. If you need this functionality, you can try signing the built
|
||||||
a self signed certificate, see for example, `here
|
:file:`kitty.app` with a self signed certificate, see for example, `here
|
||||||
<https://stackoverflow.com/questions/27474751/how-can-i-codesign-an-app-without-being-in-the-mac-developer-program/27474942>`_.
|
<https://stackoverflow.com/questions/27474751/how-can-i-codesign-an-app-without-being-in-the-mac-developer-program/27474942>`__.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If you are facing issues with ``linker`` while building,
|
If you are facing issues with ``linker`` while building, try with a ``brew``
|
||||||
try with a ``brew`` installed python instead, see :iss:`289`
|
installed Python instead, see :iss:`289` for more discussion.
|
||||||
for more discussion.
|
|
||||||
|
|
||||||
|
|
||||||
Build and run from source with Nix
|
Build and run from source with Nix
|
||||||
@@ -116,13 +116,14 @@ Build and run from source with Nix
|
|||||||
|
|
||||||
On NixOS or any other Linux or macOS system with the Nix package manager
|
On NixOS or any other Linux or macOS system with the Nix package manager
|
||||||
installed, execute `nix-shell
|
installed, execute `nix-shell
|
||||||
<https://nixos.org/guides/nix-pills/developing-with-nix-shell.html>`_ to create
|
<https://nixos.org/guides/nix-pills/developing-with-nix-shell.html>`__ to create
|
||||||
the correct environment to build kitty or use ``nix-shell --pure`` instead to
|
the correct environment to build kitty or use ``nix-shell --pure`` instead to
|
||||||
eliminate most of the influence of the outside system, e.g. globally installed
|
eliminate most of the influence of the outside system, e.g. globally installed
|
||||||
packages. ``nix-shell`` will automatically fetch all required dependencies and
|
packages. ``nix-shell`` will automatically fetch all required dependencies and
|
||||||
make them available in the newly spawned shell.
|
make them available in the newly spawned shell.
|
||||||
|
|
||||||
Then proceed with ``make`` or ``make app`` according to the platform specific instructions above.
|
Then proceed with ``make`` or ``make app`` according to the platform specific
|
||||||
|
instructions above.
|
||||||
|
|
||||||
|
|
||||||
Debug builds
|
Debug builds
|
||||||
@@ -132,7 +133,8 @@ A basic debug build can be done with::
|
|||||||
|
|
||||||
make debug
|
make debug
|
||||||
|
|
||||||
This includes debug info in the binary for better traces. To build with address sanitizer, use::
|
This includes debug info in the binary for better traces. To build with address
|
||||||
|
sanitizer, use::
|
||||||
|
|
||||||
make asan
|
make asan
|
||||||
|
|
||||||
@@ -144,46 +146,45 @@ Notes for Linux/macOS packagers
|
|||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
The released |kitty| source code is available as a `tarball`_ from
|
The released |kitty| source code is available as a `tarball`_ from
|
||||||
`the GitHub releases page <https://github.com/kovidgoyal/kitty/releases>`_.
|
`the GitHub releases page <https://github.com/kovidgoyal/kitty/releases>`__.
|
||||||
|
|
||||||
While |kitty| does use python, it is not a traditional python package, so please
|
While |kitty| does use Python, it is not a traditional Python package, so please
|
||||||
do not install it in site-packages.
|
do not install it in site-packages.
|
||||||
Instead run::
|
Instead run::
|
||||||
|
|
||||||
python3 setup.py linux-package
|
python3 setup.py linux-package
|
||||||
|
|
||||||
This will install |kitty| into the directory :file:`linux-package`. You can run |kitty|
|
This will install |kitty| into the directory :file:`linux-package`. You can run
|
||||||
with :file:`linux-package/bin/kitty`. All the files needed to run kitty will be in
|
|kitty| with :file:`linux-package/bin/kitty`. All the files needed to run kitty
|
||||||
:file:`linux-package/lib/kitty`. The terminfo file will be installed into
|
will be in :file:`linux-package/lib/kitty`. The terminfo file will be installed
|
||||||
:file:`linux-package/share/terminfo`. Simply copy these files into :file:`/usr` to install
|
into :file:`linux-package/share/terminfo`. Simply copy these files into
|
||||||
|kitty|. In other words, :file:`linux-package` is the staging area into which |kitty| is
|
:file:`/usr` to install |kitty|. In other words, :file:`linux-package` is the
|
||||||
installed. You can choose a different staging area, by passing the ``--prefix``
|
staging area into which |kitty| is installed. You can choose a different staging
|
||||||
argument to :file:`setup.py`.
|
area, by passing the ``--prefix`` argument to :file:`setup.py`.
|
||||||
|
|
||||||
You should probably split |kitty| into three packages:
|
You should probably split |kitty| into three packages:
|
||||||
|
|
||||||
:code:`kitty-terminfo`
|
:code:`kitty-terminfo`
|
||||||
installs the terminfo file
|
Installs the terminfo file
|
||||||
|
|
||||||
:code:`kitty-shell-integration`
|
:code:`kitty-shell-integration`
|
||||||
installs the shell integration scripts (the contents of the
|
Installs the shell integration scripts (the contents of the
|
||||||
shell-integration directory in the kitty source code, probably to
|
shell-integration directory in the kitty source code), probably to
|
||||||
:file:`/usr/share/kitty/shell-integration`
|
:file:`/usr/share/kitty/shell-integration`
|
||||||
|
|
||||||
:code:`kitty`
|
:code:`kitty`
|
||||||
installs the main program
|
Installs the main program
|
||||||
|
|
||||||
This allows users to install the terminfo and shell integration files on
|
This allows users to install the terminfo and shell integration files on servers
|
||||||
servers into which they ssh, without needing to install all of |kitty|.
|
into which they ssh, without needing to install all of |kitty|.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
You need a couple of extra dependencies to build linux-package.
|
You need a couple of extra dependencies to build linux-package. :file:`tic`
|
||||||
:file:`tic` to compile terminfo files, usually found in the
|
to compile terminfo files, usually found in the development package of
|
||||||
development package of :file:`ncurses`. Also, if you are building from
|
:file:`ncurses`. Also, if you are building from a git checkout instead of the
|
||||||
a git checkout instead of the released source code tarball, you will
|
released source code tarball, you will need to install the dependencies from
|
||||||
need to install the dependencies from ``docs/requirements.txt`` to
|
:file:`docs/requirements.txt` to build the kitty documentation. They can be
|
||||||
build the kitty documentation. They can be installed most easily with
|
installed most easily with ``python -m pip -r docs/requirements.txt``.
|
||||||
``python -m pip -r docs/requirements.txt``.
|
|
||||||
|
|
||||||
This applies to creating packages for |kitty| for macOS package managers such as
|
This applies to creating packages for |kitty| for macOS package managers such as
|
||||||
Homebrew or MacPorts as well.
|
Homebrew or MacPorts as well.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ The :doc:`ssh kitten <kittens/ssh>` is redesigned with powerful new features:
|
|||||||
|
|
||||||
* Automatic :ref:`shell_integration` on remote machines
|
* Automatic :ref:`shell_integration` on remote machines
|
||||||
* Easily :ref:`clone local shell/editor config <real_world_ssh_kitten_config>` on remote machines
|
* Easily :ref:`clone local shell/editor config <real_world_ssh_kitten_config>` on remote machines
|
||||||
|
* Easily :ref:`edit files in your local editor <edit_file>` on remote machines
|
||||||
* Automatic :opt:`re-use of existing connections <kitten-ssh.share_connections>` to avoid connection setup latency
|
* Automatic :opt:`re-use of existing connections <kitten-ssh.share_connections>` to avoid connection setup latency
|
||||||
|
|
||||||
|
|
||||||
@@ -34,6 +35,89 @@ mouse anywhere in the current command to move the cursor there. See
|
|||||||
Detailed list of changes
|
Detailed list of changes
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
|
0.25.2 [2022-06-07]
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- A new command :command:`edit-in-kitty` to :ref:`edit_file`
|
||||||
|
|
||||||
|
- Allow getting the last non-empty command output easily via an action or
|
||||||
|
remote control (:pull:`4973`)
|
||||||
|
|
||||||
|
- Fix a bug that caused :opt:`macos_colorspace` to always be ``default`` regardless of its actual value (:iss:`5129`)
|
||||||
|
|
||||||
|
- diff kitten: A new option :opt:`kitten-diff.ignore_name` to exclude files and directories from being scanned (:pull:`5171`)
|
||||||
|
|
||||||
|
- ssh kitten: Fix bash not being executed as a login shell since kitty 0.25.0 (:iss:`5130`)
|
||||||
|
|
||||||
|
- macOS: When pasting text and the clipboard has a filesystem path, paste the
|
||||||
|
full path instead of the text, which is sometimes just the file name (:pull:`5142`)
|
||||||
|
|
||||||
|
- macOS: Allow opening executables without a file extension with kitty as well
|
||||||
|
(:iss:`5160`)
|
||||||
|
|
||||||
|
- Themes kitten: Add a tab to show user defined custom color themes separately
|
||||||
|
(:pull:`5150`)
|
||||||
|
|
||||||
|
- Iosevka: Fix incorrect rendering when there is a combining char that does not
|
||||||
|
group with its neighbors (:iss:`5153`)
|
||||||
|
|
||||||
|
- Weston: Fix client side decorations flickering on slow computers during
|
||||||
|
window resize (:iss:`5162`)
|
||||||
|
|
||||||
|
- Remote control: Fix commands with large or asynchronous payloads like
|
||||||
|
:command:`kitty @ set-backround-image`, :command:`kitty @ set-window-logo`
|
||||||
|
and :command:`kitty @ select-window` not working correctly
|
||||||
|
when using a socket (:iss:`5165`)
|
||||||
|
|
||||||
|
- hints kitten: Fix surrounding quotes/brackets and embedded carriage returns
|
||||||
|
not being removed when using line number processing (:iss:`5170`)
|
||||||
|
|
||||||
|
|
||||||
|
0.25.1 [2022-05-26]
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Shell integration: Add a command to :ref:`clone_shell`
|
||||||
|
|
||||||
|
- Remote control: Allow using :ref:`Boolean operators <search_syntax>` when constructing queries to match windows or tabs
|
||||||
|
|
||||||
|
- Sessions: Fix :code:`os_window_size` and :code:`os_window_class` not applying to the first OS Window (:iss:`4957`)
|
||||||
|
|
||||||
|
- Allow using the cwd of the oldest as well as the newest foreground process for :option:`launch --cwd` (:disc:`4869`)
|
||||||
|
|
||||||
|
- Bash integration: Fix the value of :opt:`shell_integration` not taking effect if the integration script is sourced in bashrc (:pull:`4964`)
|
||||||
|
|
||||||
|
- Fix a regression in the previous release that caused mouse move events to be incorrectly reported as drag events even when a button is not pressed (:iss:`4992`)
|
||||||
|
|
||||||
|
- remote file kitten: Integrate with the ssh kitten for improved performance
|
||||||
|
and robustness. Re-uses the control master connection of the ssh kitten to
|
||||||
|
avoid round-trip latency.
|
||||||
|
|
||||||
|
- Fix tab selection when closing a new tab not correct in some scenarios (:iss:`4987`)
|
||||||
|
|
||||||
|
- A new action :ac:`open_url` to open the specified URL (:pull:`5004`)
|
||||||
|
|
||||||
|
- A new option :opt:`select_by_word_characters_forward` that allows changing
|
||||||
|
which characters are considered part of a word to the right when double clicking to select
|
||||||
|
words (:pull:`5103`)
|
||||||
|
|
||||||
|
- macOS: Make the global menu shortcut to open kitty website configurable (:pull:`5004`)
|
||||||
|
|
||||||
|
- macOS: Add the :opt:`macos_colorspace` option to control what color space colors are rendered in (:iss:`4686`)
|
||||||
|
|
||||||
|
- Fix reloading of config not working when :file:`kitty.conf` does not exist when kitty is launched (:iss:`5071`)
|
||||||
|
|
||||||
|
- Fix deleting images by row not calculating image bounds correctly (:iss:`5081`)
|
||||||
|
|
||||||
|
- Increase the max number of combining chars per cell from two to three, without increasing memory usage.
|
||||||
|
|
||||||
|
- Linux: Load libfontconfig at runtime to allow the binaries to work for
|
||||||
|
running kittens on servers without FontConfig
|
||||||
|
|
||||||
|
- GNOME: Fix for high CPU usage caused by GNOME's text input subsystem going
|
||||||
|
into an infinite loop when IME cursor position is updated after a done event
|
||||||
|
(:iss:`5105`)
|
||||||
|
|
||||||
|
|
||||||
0.25.0 [2022-04-11]
|
0.25.0 [2022-04-11]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@@ -43,6 +127,10 @@ Detailed list of changes
|
|||||||
|
|
||||||
- When pasting URLs at shell prompts automatically quote them. Also allow filtering pasted text and confirm pastes. See :opt:`paste_actions` for details. (:iss:`4873`)
|
- When pasting URLs at shell prompts automatically quote them. Also allow filtering pasted text and confirm pastes. See :opt:`paste_actions` for details. (:iss:`4873`)
|
||||||
|
|
||||||
|
- Change the default value of :opt:`confirm_os_window_close` to ask for confirmation when closing windows that are not sitting at shell prompts
|
||||||
|
|
||||||
|
- A new value :code:`last_reported` for :option:`launch --cwd` to use the current working directory last reported by the program running in the terminal
|
||||||
|
|
||||||
- macOS: When using Apple's less as the pager for viewing scrollback strip out OSC codes as it cant parse them (:iss:`4788`)
|
- macOS: When using Apple's less as the pager for viewing scrollback strip out OSC codes as it cant parse them (:iss:`4788`)
|
||||||
|
|
||||||
- diff kitten: Fix incorrect rendering in rare circumstances when scrolling after changing the context size (:iss:`4831`)
|
- diff kitten: Fix incorrect rendering in rare circumstances when scrolling after changing the context size (:iss:`4831`)
|
||||||
|
|||||||
@@ -2,25 +2,25 @@ Saving and restoring colors
|
|||||||
==============================
|
==============================
|
||||||
|
|
||||||
It is often useful for a full screen application with its own color themes to
|
It is often useful for a full screen application with its own color themes to
|
||||||
set the default foreground, background, selection and cursor colors and the
|
set the default foreground, background, selection and cursor colors and the ANSI
|
||||||
ANSI color table. This allows for various performance optimizations when
|
color table. This allows for various performance optimizations when drawing the
|
||||||
drawing the screen. The problem is that if the user previously used the escape
|
screen. The problem is that if the user previously used the escape codes to
|
||||||
codes to change these colors herself, then running the full screen application
|
change these colors themselves, then running the full screen application will
|
||||||
will lose her changes even after it exits. To avoid this, kitty introduces a
|
lose those changes even after it exits. To avoid this, kitty introduces a new
|
||||||
new pair of *OSC* escape codes to push and pop the current color values from a
|
pair of *OSC* escape codes to push and pop the current color values from a
|
||||||
stack::
|
stack::
|
||||||
|
|
||||||
<ESC>]30001<ESC>\ # push onto stack
|
<ESC>]30001<ESC>\ # push onto stack
|
||||||
<ESC>]30101<ESC>\ # pop from stack
|
<ESC>]30101<ESC>\ # pop from stack
|
||||||
|
|
||||||
These escape codes save/restore the colors, default
|
These escape codes save/restore the colors, default background, default
|
||||||
background, default foreground, selection background, selection foreground and
|
foreground, selection background, selection foreground and cursor color and the
|
||||||
cursor color and the 256 colors of the ANSI color table.
|
256 colors of the ANSI color table.
|
||||||
|
|
||||||
.. note:: In July 2020, after several years, XTerm copied this protocol
|
.. note:: In July 2020, after several years, xterm copied this protocol
|
||||||
extension, without acknowledgement, and using incompatible escape codes
|
extension, without acknowledgement, and using incompatible escape codes
|
||||||
(XTPUSHCOLORS, XTPOPCOLORS, XTREPORTCOLORS). And they decided to save not
|
(XTPUSHCOLORS, XTPOPCOLORS, XTREPORTCOLORS). And they decided to save not
|
||||||
just the dynamic colors but the entire ANSI color table. In the interests of
|
just the dynamic colors but the entire ANSI color table. In the interests of
|
||||||
promoting interoperability, kitty added support for XTerm's escape codes as
|
promoting interoperability, kitty added support for xterm's escape codes as
|
||||||
well, and changed this extension to also save/restore the entire ANSI color
|
well, and changed this extension to also save/restore the entire ANSI color
|
||||||
table.
|
table.
|
||||||
|
|||||||
18
docs/conf.py
18
docs/conf.py
@@ -13,7 +13,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
|
from typing import Any, Callable, Dict, Iterable, List, Tuple
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.parsers.rst.roles import set_classes
|
from docutils.parsers.rst.roles import set_classes
|
||||||
@@ -85,7 +85,7 @@ master_doc = 'index'
|
|||||||
#
|
#
|
||||||
# This is also used if you do content translation via gettext catalogs.
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
# Usually you set "language" from the command line for these cases.
|
# Usually you set "language" from the command line for these cases.
|
||||||
language: Optional[str] = None
|
language: str = 'en'
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
@@ -104,6 +104,10 @@ rst_prolog = '''
|
|||||||
'''.replace('VERSION', str_version)
|
'''.replace('VERSION', str_version)
|
||||||
smartquotes_action = 'qe' # educate quotes and ellipses but not dashes
|
smartquotes_action = 'qe' # educate quotes and ellipses but not dashes
|
||||||
|
|
||||||
|
string_replacements = {
|
||||||
|
'_kitty_install_cmd': 'curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
|
||||||
@@ -300,8 +304,16 @@ def write_remote_control_protocol_docs() -> None: # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
def replace_string(app: Any, docname: str, source: List[str]) -> None: # {{{
|
||||||
|
src = source[0]
|
||||||
|
for k, v in app.config.string_replacements.items():
|
||||||
|
src = src.replace(k, v)
|
||||||
|
source[0] = src
|
||||||
|
# }}}
|
||||||
|
|
||||||
# config file docs {{{
|
# config file docs {{{
|
||||||
|
|
||||||
|
|
||||||
class ConfLexer(RegexLexer): # type: ignore
|
class ConfLexer(RegexLexer): # type: ignore
|
||||||
name = 'Conf'
|
name = 'Conf'
|
||||||
aliases = ['conf']
|
aliases = ['conf']
|
||||||
@@ -516,6 +528,8 @@ def setup(app: Any) -> None:
|
|||||||
write_cli_docs(kn)
|
write_cli_docs(kn)
|
||||||
write_remote_control_protocol_docs()
|
write_remote_control_protocol_docs()
|
||||||
write_conf_docs(app, kn)
|
write_conf_docs(app, kn)
|
||||||
|
app.add_config_value('string_replacements', {}, True)
|
||||||
|
app.connect('source-read', replace_string)
|
||||||
app.add_config_value('analytics_id', '', 'env')
|
app.add_config_value('analytics_id', '', 'env')
|
||||||
app.connect('html-page-context', add_html_context)
|
app.connect('html-page-context', add_html_context)
|
||||||
app.add_lexer('session', SessionLexer() if version_info[0] < 3 else SessionLexer)
|
app.add_lexer('session', SessionLexer() if version_info[0] < 3 else SessionLexer)
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ kitty.conf
|
|||||||
|
|
||||||
.. highlight:: conf
|
.. highlight:: conf
|
||||||
|
|
||||||
|kitty| is highly customizable, everything from keyboard shortcuts, to
|
|kitty| is highly customizable, everything from keyboard shortcuts, to rendering
|
||||||
rendering frames-per-second. See below for an overview of all customization
|
frames-per-second. See below for an overview of all customization possibilities.
|
||||||
possibilities.
|
|
||||||
|
|
||||||
You can open the config file within kitty by pressing :sc:`edit_config_file` (:kbd:`⌘+,` on macOS).
|
You can open the config file within kitty by pressing :sc:`edit_config_file`
|
||||||
You can reload the config file within kitty by pressing
|
(:kbd:`⌘+,` on macOS). A :file:`kitty.conf` with commented default
|
||||||
:sc:`reload_config_file` (:kbd:`⌃+⌘+,` on macOS) or sending kitty the ``SIGUSR1`` signal.
|
configurations and descriptions will be created if the file does not exist.
|
||||||
|
You can reload the config file within kitty by pressing :sc:`reload_config_file`
|
||||||
|
(:kbd:`⌃+⌘+,` on macOS) or sending kitty the ``SIGUSR1`` signal.
|
||||||
You can also display the current configuration by pressing :sc:`debug_config`
|
You can also display the current configuration by pressing :sc:`debug_config`
|
||||||
(:kbd:`⌥+⌘+,` on macOS).
|
(:kbd:`⌥+⌘+,` on macOS).
|
||||||
|
|
||||||
@@ -17,12 +18,12 @@ You can also display the current configuration by pressing :sc:`debug_config`
|
|||||||
|
|
||||||
|kitty| looks for a config file in the OS config directories (usually
|
|kitty| looks for a config file in the OS config directories (usually
|
||||||
:file:`~/.config/kitty/kitty.conf`) but you can pass a specific path via the
|
:file:`~/.config/kitty/kitty.conf`) but you can pass a specific path via the
|
||||||
:option:`kitty --config` option or use the ``KITTY_CONFIG_DIRECTORY``
|
:option:`kitty --config` option or use the :envvar:`KITTY_CONFIG_DIRECTORY`
|
||||||
environment variable. See the :option:`kitty --config` option for full details.
|
environment variable. See :option:`kitty --config` for full details.
|
||||||
|
|
||||||
Comments can be added to the config file as lines starting with the ``#``
|
Comments can be added to the config file as lines starting with the ``#``
|
||||||
character. This works only if the ``#`` character is the first character
|
character. This works only if the ``#`` character is the first character in the
|
||||||
in the line.
|
line.
|
||||||
|
|
||||||
.. _include:
|
.. _include:
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ from environment variables. For example::
|
|||||||
|
|
||||||
|
|
||||||
.. note:: Syntax highlighting for :file:`kitty.conf` in vim is available via
|
.. note:: Syntax highlighting for :file:`kitty.conf` in vim is available via
|
||||||
`vim-kitty <https://github.com/fladson/vim-kitty>`_.
|
`vim-kitty <https://github.com/fladson/vim-kitty>`__.
|
||||||
|
|
||||||
|
|
||||||
.. include:: /generated/conf-kitty.rst
|
.. include:: /generated/conf-kitty.rst
|
||||||
@@ -53,17 +54,17 @@ Sample kitty.conf
|
|||||||
|
|
||||||
.. only:: html
|
.. only:: html
|
||||||
|
|
||||||
You can download a sample :file:`kitty.conf` file with all default settings and
|
You can download a sample :file:`kitty.conf` file with all default settings
|
||||||
comments describing each setting by clicking: :download:`sample kitty.conf
|
and comments describing each setting by clicking: :download:`sample
|
||||||
</generated/conf/kitty.conf>`.
|
kitty.conf </generated/conf/kitty.conf>`.
|
||||||
|
|
||||||
.. only:: man
|
.. only:: man
|
||||||
|
|
||||||
You can edit a fully commented sample kitty.conf by pressing the
|
You can edit a fully commented sample kitty.conf by pressing the
|
||||||
:sc:`edit_config_file` shortcut in kitty. This will generate a config
|
:sc:`edit_config_file` shortcut in kitty. This will generate a config file
|
||||||
file with full documentation and all settings commented out. If you
|
with full documentation and all settings commented out. If you have a
|
||||||
have a pre-existing kitty.conf, then that will be used instead, delete
|
pre-existing :file:`kitty.conf`, then that will be used instead, delete it to
|
||||||
it to see the sample file.
|
see the sample file.
|
||||||
|
|
||||||
|
|
||||||
All mappable actions
|
All mappable actions
|
||||||
|
|||||||
@@ -3,19 +3,18 @@ Setting text styles/colors in arbitrary regions of the screen
|
|||||||
|
|
||||||
There already exists an escape code to set *some* text attributes in arbitrary
|
There already exists an escape code to set *some* text attributes in arbitrary
|
||||||
regions of the screen, `DECCARA
|
regions of the screen, `DECCARA
|
||||||
<https://vt100.net/docs/vt510-rm/DECCARA.html>`_. However, it is limited to
|
<https://vt100.net/docs/vt510-rm/DECCARA.html>`__. However, it is limited to
|
||||||
only a few attributes. |kitty| extends this to work with *all* SGR attributes.
|
only a few attributes. |kitty| extends this to work with *all* SGR attributes.
|
||||||
So, for example, this can be used to set the background color in an arbitrary
|
So, for example, this can be used to set the background color in an arbitrary
|
||||||
region of the screen.
|
region of the screen.
|
||||||
|
|
||||||
The motivation for this extension is the various problems with the existing
|
The motivation for this extension is the various problems with the existing
|
||||||
solution for erasing to background color, namely the *background color erase
|
solution for erasing to background color, namely the *background color erase
|
||||||
(bce)* capability. See
|
(bce)* capability. See :iss:`this discussion <160#issuecomment-346470545>`
|
||||||
:iss:`this discussion <160#issuecomment-346470545>`
|
and `this FAQ <https://invisible-island.net/ncurses/ncurses.faq.html#bce_mismatches>`__
|
||||||
and `this FAQ <https://invisible-island.net/ncurses/ncurses.faq.html#bce_mismatches>`_
|
|
||||||
for a summary of problems with *bce*.
|
for a summary of problems with *bce*.
|
||||||
|
|
||||||
For example, to set the background color to blue in a
|
For example, to set the background color to blue in a rectangular region of the
|
||||||
rectangular region of the screen from (3, 4) to (10, 11), you use::
|
screen from (3, 4) to (10, 11), you use::
|
||||||
|
|
||||||
<ESC>[2*x<ESC>[4;3;11;10;44$r<ESC>[*x
|
<ESC>[2*x<ESC>[4;3;11;10;44$r<ESC>[*x
|
||||||
|
|||||||
@@ -5,21 +5,20 @@ Desktop notifications
|
|||||||
=======================
|
=======================
|
||||||
|
|
||||||
|kitty| implements an extensible escape code (OSC 99) to show desktop
|
|kitty| implements an extensible escape code (OSC 99) to show desktop
|
||||||
notifications. It is easy to use from shell scripts and fully extensible to
|
notifications. It is easy to use from shell scripts and fully extensible to show
|
||||||
show title and body. Clicking on the notification can optionally focus the
|
title and body. Clicking on the notification can optionally focus the window it
|
||||||
window it came from, and/or send an escape code back to the application running
|
came from, and/or send an escape code back to the application running in that
|
||||||
in that window.
|
window.
|
||||||
|
|
||||||
The design of the escape code is partially based on the discussion in
|
The design of the escape code is partially based on the discussion in the
|
||||||
the defunct
|
defunct `terminal-wg <https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/13>`__
|
||||||
`terminal-wg <https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/13>`_
|
|
||||||
|
|
||||||
The escape code has the form::
|
The escape code has the form::
|
||||||
|
|
||||||
<OSC> 99 ; metadata ; payload <terminator>
|
<OSC> 99 ; metadata ; payload <terminator>
|
||||||
|
|
||||||
Here ``<OSC>`` is :code:`<ESC>]` and ``<terminator>`` is
|
Here ``<OSC>`` is :code:`<ESC>]` and ``<terminator>`` is
|
||||||
:code:`<ESC><backslash>`. The metadata is a section of colon separated
|
:code:`<ESC><backslash>`. The ``metadata`` is a section of colon separated
|
||||||
:code:`key=value` pairs. Every key must be a single character from the set
|
:code:`key=value` pairs. Every key must be a single character from the set
|
||||||
:code:`a-zA-Z` and every value must be a word consisting of characters from
|
:code:`a-zA-Z` and every value must be a word consisting of characters from
|
||||||
the set :code:`a-zA-Z0-9-_/\+.,(){}[]*&^%$#@!`~`. The payload must be
|
the set :code:`a-zA-Z0-9-_/\+.,(){}[]*&^%$#@!`~`. The payload must be
|
||||||
@@ -45,28 +44,27 @@ The design of the escape code is fundamentally chunked, this is because
|
|||||||
different terminal emulators have different limits on how large a single escape
|
different terminal emulators have different limits on how large a single escape
|
||||||
code can be. Chunking is accomplished by the ``i`` and ``d`` keys. The ``i``
|
code can be. Chunking is accomplished by the ``i`` and ``d`` keys. The ``i``
|
||||||
key is the *notification id* which can be any string containing the characters
|
key is the *notification id* which can be any string containing the characters
|
||||||
``[a-zA-Z0-9_-+.]``. The ``d`` key stands for *done* and
|
``[a-zA-Z0-9_-+.]``. The ``d`` key stands for *done* and can only take the
|
||||||
can only take the values ``0`` and ``1``. A value of ``0`` means the
|
values ``0`` and ``1``. A value of ``0`` means the notification is not yet done
|
||||||
notification is not yet done and the terminal emulator should hold off
|
and the terminal emulator should hold off displaying it. A value of ``1`` means
|
||||||
displaying it. A value of ``1`` means the notification is done, and should be
|
the notification is done, and should be displayed. You can specify the title or
|
||||||
displayed. You can specify the title or body multiple times and the terminal
|
body multiple times and the terminal emulator will concatenate them, thereby
|
||||||
emulator will concatenate them, thereby allowing arbitrarily long text
|
allowing arbitrarily long text (terminal emulators are free to impose a sensible
|
||||||
(terminal emulators are free to impose a sensible limit to avoid
|
limit to avoid Denial-of-Service attacks).
|
||||||
Denial-of-Service attacks).
|
|
||||||
|
|
||||||
Both the ``title`` and ``body`` payloads must be either UTF-8 encoded plain
|
Both the ``title`` and ``body`` payloads must be either UTF-8 encoded plain
|
||||||
text with no embedded escape codes, or UTF-8 text that is base64 encoded, in
|
text with no embedded escape codes, or UTF-8 text that is Base64 encoded, in
|
||||||
which case there must be an ``e=1`` key in the metadata to indicate the payload
|
which case there must be an ``e=1`` key in the metadata to indicate the payload
|
||||||
is base64 encoded.
|
is Base64 encoded.
|
||||||
|
|
||||||
When the user clicks the notification, a couple of things can happen, the
|
When the user clicks the notification, a couple of things can happen, the
|
||||||
terminal emulator can focus the window from which the notification came, and/or
|
terminal emulator can focus the window from which the notification came, and/or
|
||||||
it can send back an escape code to the application indicating the notification
|
it can send back an escape code to the application indicating the notification
|
||||||
was activated. This is controlled by the ``a`` key which takes a comma
|
was activated. This is controlled by the ``a`` key which takes a comma separated
|
||||||
separated set of values, ``report`` and ``focus``. The value ``focus`` means
|
set of values, ``report`` and ``focus``. The value ``focus`` means focus the
|
||||||
focus the window from which the notification was issued and is the default.
|
window from which the notification was issued and is the default. ``report``
|
||||||
``report`` means send an escape code back to the application. The format of the
|
means send an escape code back to the application. The format of the returned
|
||||||
returned escape code is::
|
escape code is::
|
||||||
|
|
||||||
<OSC> 99 ; i=identifier ; <terminator>
|
<OSC> 99 ; i=identifier ; <terminator>
|
||||||
|
|
||||||
@@ -87,9 +85,9 @@ to display it based on what it does understand.
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
It is possible to extend this escape code to allow specifying an icon for
|
It is possible to extend this escape code to allow specifying an icon for
|
||||||
the notification, however, given that some platforms, such as macOS, dont
|
the notification, however, given that some platforms, such as legacy versions
|
||||||
allow displaying custom icons on a notification, at all, it was decided to
|
of macOS, don't allow displaying custom images on a notification, it was
|
||||||
leave it out of the spec for the time being.
|
decided to leave it out of the spec for the time being.
|
||||||
|
|
||||||
Similarly, features such as scheduled notifications could be added in future
|
Similarly, features such as scheduled notifications could be added in future
|
||||||
revisions.
|
revisions.
|
||||||
@@ -107,7 +105,7 @@ Key Value Default Description
|
|||||||
``d`` ``0`` or ``1`` ``1`` Indicates if the notification is
|
``d`` ``0`` or ``1`` ``1`` Indicates if the notification is
|
||||||
complete or not.
|
complete or not.
|
||||||
|
|
||||||
``e`` ``0`` or ``1`` ``0`` If set to ``1`` means the payload is base64 encoded UTF-8,
|
``e`` ``0`` or ``1`` ``0`` If set to ``1`` means the payload is Base64 encoded UTF-8,
|
||||||
otherwise it is plain UTF-8 text with no C0 control codes in it
|
otherwise it is plain UTF-8 text with no C0 control codes in it
|
||||||
|
|
||||||
``i`` ``[a-zA-Z0-9-_+.]`` ``0`` Identifier for the notification
|
``i`` ``[a-zA-Z0-9-_+.]`` ``0`` Identifier for the notification
|
||||||
@@ -118,5 +116,6 @@ Key Value Default Description
|
|||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|kitty| also supports the legacy OSC 9 protocol developed by iTerm2 for
|
|kitty| also supports the `legacy OSC 9 protocol developed by iTerm2
|
||||||
desktop notifications.
|
<https://iterm2.com/documentation-escape-codes.html>`__ for desktop
|
||||||
|
notifications.
|
||||||
|
|||||||
198
docs/faq.rst
198
docs/faq.rst
@@ -27,14 +27,15 @@ turned off for specific symbols using :opt:`narrow_symbols`.
|
|||||||
Using a color theme with a background color does not work well in vim?
|
Using a color theme with a background color does not work well in vim?
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
|
|
||||||
First make sure you have not changed the TERM environment variable, it should
|
First make sure you have not changed the :envvar:`TERM` environment variable, it
|
||||||
be ``xterm-kitty``. vim uses *background color erase* even if the terminfo file
|
should be ``xterm-kitty``. vim uses *background color erase* even if the
|
||||||
does not contain the ``bce`` capability. This is a bug in vim. You can work around
|
terminfo file does not contain the ``bce`` capability. This is a bug in vim. You
|
||||||
it by adding the following to your vimrc::
|
can work around it by adding the following to your vimrc::
|
||||||
|
|
||||||
let &t_ut=''
|
let &t_ut=''
|
||||||
|
|
||||||
See :doc:`here <deccara>` for why |kitty| does not support background color erase.
|
See :doc:`here <deccara>` for why |kitty| does not support background color
|
||||||
|
erase.
|
||||||
|
|
||||||
|
|
||||||
I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer?
|
I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer?
|
||||||
@@ -47,32 +48,32 @@ terminfo files to the server::
|
|||||||
kitty +kitten ssh myserver
|
kitty +kitten ssh myserver
|
||||||
|
|
||||||
This :doc:`ssh kitten <kittens/ssh>` takes all the same command line arguments
|
This :doc:`ssh kitten <kittens/ssh>` takes all the same command line arguments
|
||||||
as ssh, you can alias it to something small in your shell's rc files to avoid
|
as :program:`ssh`, you can alias it to something small in your shell's rc files
|
||||||
having to type it each time::
|
to avoid having to type it each time::
|
||||||
|
|
||||||
alias s="kitty +kitten ssh"
|
alias s="kitty +kitten ssh"
|
||||||
|
|
||||||
If the ssh kitten fails, use the following one-liner instead (it
|
If the ssh kitten fails, use the following one-liner instead (it is slower as it
|
||||||
is slower as it needs to ssh into the server twice, but will work with most
|
needs to ssh into the server twice, but will work with most servers)::
|
||||||
servers)::
|
|
||||||
|
|
||||||
infocmp -a xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
|
infocmp -a xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
|
||||||
|
|
||||||
If you are behind a proxy (like Balabit) that prevents this, or ``tic`` comes
|
If you are behind a proxy (like Balabit) that prevents this, or :program:`tic`
|
||||||
with macOS that does not support reading from STDIN, you must redirect the 1st
|
comes with macOS that does not support reading from STDIN, you must redirect the
|
||||||
command to a file, copy that to the server and run ``tic`` manually. If you
|
first command to a file, copy that to the server and run :program:`tic`
|
||||||
connect to a server, embedded or Android system that doesn't have ``tic``, copy over
|
manually. If you connect to a server, embedded or Android system that doesn't
|
||||||
your local file terminfo to the other system as :file:`~/.terminfo/x/xterm-kitty`.
|
have :program:`tic`, copy over your local file terminfo to the other system as
|
||||||
|
:file:`~/.terminfo/x/xterm-kitty`.
|
||||||
|
|
||||||
Really, the correct solution for this is to convince the OpenSSH maintainers to
|
Really, the correct solution for this is to convince the OpenSSH maintainers to
|
||||||
have ssh do this automatically, if possible, when connecting to a server, so that
|
have :program:`ssh` do this automatically, if possible, when connecting to a
|
||||||
all terminals work transparently.
|
server, so that all terminals work transparently.
|
||||||
|
|
||||||
If the server is running FreeBSD, or another system that relies on termcap
|
If the server is running FreeBSD, or another system that relies on termcap
|
||||||
rather than terminfo, you will need to convert the terminfo file on your local
|
rather than terminfo, you will need to convert the terminfo file on your local
|
||||||
machine by running (on local machine with |kitty|)::
|
machine by running (on local machine with |kitty|)::
|
||||||
|
|
||||||
infocmp -C xterm-kitty
|
infocmp -CrT0 xterm-kitty
|
||||||
|
|
||||||
The output of this command is the termcap description, which should be appended
|
The output of this command is the termcap description, which should be appended
|
||||||
to :file:`/usr/share/misc/termcap` on the remote server. Then run the following
|
to :file:`/usr/share/misc/termcap` on the remote server. Then run the following
|
||||||
@@ -84,9 +85,9 @@ command to apply your change (on the server)::
|
|||||||
Keys such as arrow keys, backspace, delete, home/end, etc. do not work when using su or sudo?
|
Keys such as arrow keys, backspace, delete, home/end, etc. do not work when using su or sudo?
|
||||||
-------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
Make sure the TERM environment variable, is ``xterm-kitty``. And either the
|
Make sure the :envvar:`TERM` environment variable, is ``xterm-kitty``. And
|
||||||
TERMINFO environment variable points to a directory containing :file:`x/xterm-kitty`
|
either the :envvar:`TERMINFO` environment variable points to a directory
|
||||||
or that file is under :file:`~/.terminfo/x/`.
|
containing :file:`x/xterm-kitty` or that file is under :file:`~/.terminfo/x/`.
|
||||||
|
|
||||||
For macOS, you may also need to put that file under :file:`~/.terminfo/78/`::
|
For macOS, you may also need to put that file under :file:`~/.terminfo/78/`::
|
||||||
|
|
||||||
@@ -94,18 +95,19 @@ For macOS, you may also need to put that file under :file:`~/.terminfo/78/`::
|
|||||||
ln -snf ../x/xterm-kitty ~/.terminfo/78/xterm-kitty
|
ln -snf ../x/xterm-kitty ~/.terminfo/78/xterm-kitty
|
||||||
tic -x -o ~/.terminfo "$KITTY_INSTALLATION_DIR/terminfo/kitty.terminfo"
|
tic -x -o ~/.terminfo "$KITTY_INSTALLATION_DIR/terminfo/kitty.terminfo"
|
||||||
|
|
||||||
Note that ``sudo`` might remove TERMINFO. Then setting it at the shell prompt can
|
Note that :program:`sudo` might remove :envvar:`TERMINFO`. Then setting it at
|
||||||
be too late, because command line editing may not be reinitialized. In that case
|
the shell prompt can be too late, because command line editing may not be
|
||||||
you can either ask ``sudo`` to set it or if that is not supported, insert an ``env``
|
reinitialized. In that case you can either ask :program:`sudo` to set it or if
|
||||||
command before starting the shell, or, if not possible, after sudo start another
|
that is not supported, insert an :program:`env` command before starting the
|
||||||
Shell providing the right terminfo path::
|
shell, or, if not possible, after sudo start another shell providing the right
|
||||||
|
terminfo path::
|
||||||
|
|
||||||
sudo … TERMINFO=$HOME/.terminfo bash -i
|
sudo … TERMINFO=$HOME/.terminfo bash -i
|
||||||
sudo … env TERMINFO=$HOME/.terminfo bash -i
|
sudo … env TERMINFO=$HOME/.terminfo bash -i
|
||||||
TERMINFO=/home/ORIGINALUSER/.terminfo exec bash -i
|
TERMINFO=/home/ORIGINALUSER/.terminfo exec bash -i
|
||||||
|
|
||||||
You can configure sudo to preserve TERMINFO by running ``sudo
|
You can configure :program:`sudo` to preserve :envvar:`TERMINFO` by running
|
||||||
visudo`` and adding the following line::
|
``sudo visudo`` and adding the following line::
|
||||||
|
|
||||||
Defaults env_keep += "TERM TERMINFO"
|
Defaults env_keep += "TERM TERMINFO"
|
||||||
|
|
||||||
@@ -129,12 +131,15 @@ You can also define keyboard shortcuts to set colors, for example::
|
|||||||
|
|
||||||
map f1 set_colors --configured /path/to/some/config/file/colors.conf
|
map f1 set_colors --configured /path/to/some/config/file/colors.conf
|
||||||
|
|
||||||
Or you can enable :doc:`remote control <remote-control>` for |kitty| and use :ref:`at_set-colors`.
|
Or you can enable :doc:`remote control <remote-control>` for |kitty| and use
|
||||||
The shortcut mapping technique has the same syntax as the remote control
|
:ref:`at_set-colors`. The shortcut mapping technique has the same syntax as the
|
||||||
command, for details, see :ref:`at_set-colors`.
|
remote control command, for details, see :ref:`at_set-colors`.
|
||||||
|
|
||||||
|
To change colors when SSHing into a remote host, use the :opt:`color_scheme
|
||||||
|
<kitten-ssh.color_scheme>` setting for the :doc:`ssh kitten <kittens/ssh>`.
|
||||||
|
|
||||||
Additionally, You can use the
|
Additionally, You can use the
|
||||||
`OSC terminal escape codes <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands>`_
|
`OSC terminal escape codes <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands>`__
|
||||||
to set colors. Examples of using OSC escape codes to set colors::
|
to set colors. Examples of using OSC escape codes to set colors::
|
||||||
|
|
||||||
Change the default foreground color:
|
Change the default foreground color:
|
||||||
@@ -151,7 +156,7 @@ to set colors. Examples of using OSC escape codes to set colors::
|
|||||||
printf '\x1b]4;n;green\x1b\\'
|
printf '\x1b]4;n;green\x1b\\'
|
||||||
|
|
||||||
You can use various syntaxes/names for color specifications in the above
|
You can use various syntaxes/names for color specifications in the above
|
||||||
examples. See `XParseColor <https://linux.die.net/man/3/xparsecolor>`_
|
examples. See `XParseColor <https://linux.die.net/man/3/xparsecolor>`__
|
||||||
for full details.
|
for full details.
|
||||||
|
|
||||||
If a ``?`` is given rather than a color specification, kitty will respond
|
If a ``?`` is given rather than a color specification, kitty will respond
|
||||||
@@ -164,14 +169,15 @@ How do I specify command line options for kitty on macOS?
|
|||||||
Apple does not want you to use command line options with GUI applications. To
|
Apple does not want you to use command line options with GUI applications. To
|
||||||
workaround that limitation, |kitty| will read command line options from the file
|
workaround that limitation, |kitty| will read command line options from the file
|
||||||
:file:`<kitty config dir>/macos-launch-services-cmdline` when it is launched
|
:file:`<kitty config dir>/macos-launch-services-cmdline` when it is launched
|
||||||
from the GUI, i.e. by clicking the |kitty| application icon or using ``open -a kitty``.
|
from the GUI, i.e. by clicking the |kitty| application icon or using
|
||||||
Note that this file is *only read* when running via the GUI.
|
``open -a kitty``. Note that this file is *only read* when running via the GUI.
|
||||||
|
|
||||||
You can, of course, also run |kitty| from a terminal with command line options, using:
|
You can, of course, also run |kitty| from a terminal with command line options,
|
||||||
:file:`/Applications/kitty.app/Contents/MacOS/kitty`.
|
using: :file:`/Applications/kitty.app/Contents/MacOS/kitty`.
|
||||||
|
|
||||||
|
And within |kitty| itself, you can always run |kitty| using just ``kitty`` as it
|
||||||
|
cleverly adds itself to the :envvar:`PATH`.
|
||||||
|
|
||||||
And within |kitty| itself, you can always run |kitty| using just `kitty` as it
|
|
||||||
cleverly adds itself to the ``PATH``.
|
|
||||||
|
|
||||||
I catted a binary file and now kitty is hung?
|
I catted a binary file and now kitty is hung?
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
@@ -179,10 +185,10 @@ I catted a binary file and now kitty is hung?
|
|||||||
**Never** output unknown binary data directly into a terminal.
|
**Never** output unknown binary data directly into a terminal.
|
||||||
|
|
||||||
Terminals have a single channel for both data and control. Certain bytes
|
Terminals have a single channel for both data and control. Certain bytes
|
||||||
are control codes. Some of these control codes are of arbitrary length, so
|
are control codes. Some of these control codes are of arbitrary length, so if
|
||||||
if the binary data you output into the terminal happens to contain the starting
|
the binary data you output into the terminal happens to contain the starting
|
||||||
sequence for one of these control codes, the terminal will hang waiting for
|
sequence for one of these control codes, the terminal will hang waiting for the
|
||||||
the closing sequence. Press :kbd:`ctrl+shift+delete` to reset the terminal.
|
closing sequence. Press :sc:`reset_terminal` to reset the terminal.
|
||||||
|
|
||||||
If you do want to cat unknown data, use ``cat -v``.
|
If you do want to cat unknown data, use ``cat -v``.
|
||||||
|
|
||||||
@@ -190,30 +196,34 @@ If you do want to cat unknown data, use ``cat -v``.
|
|||||||
kitty is not able to use my favorite font?
|
kitty is not able to use my favorite font?
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
|kitty| achieves its stellar performance by caching alpha masks of each
|
|kitty| achieves its stellar performance by caching alpha masks of each rendered
|
||||||
rendered character on the GPU, and rendering them all in parallel. This means
|
character on the GPU, and rendering them all in parallel. This means it is a
|
||||||
it is a strictly character cell based display. As such it can use only
|
strictly character cell based display. As such it can use only monospace fonts,
|
||||||
monospace fonts, since every cell in the grid has to be the same size.
|
since every cell in the grid has to be the same size. Furthermore, it needs
|
||||||
Furthermore, it needs fonts to be freely resizable, so it does not support
|
fonts to be freely resizable, so it does not support bitmapped fonts.
|
||||||
bitmapped fonts.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If you are trying to use a font patched with NERD font symbols, dont do that
|
If you are trying to use a font patched with `Nerd Fonts
|
||||||
as patching destroys fonts. There is no need, simply install the standalone
|
<https://nerdfonts.com/>`__ symbols, don't do that as patching destroys
|
||||||
NERD font (the file :file:`NerdFontsSymbolsOnly.zip` from the `NERD font
|
fonts. There is no need, simply install the standalone ``Symbols Nerd Font``
|
||||||
releases page <https://github.com/ryanoasis/nerd-fonts/releases>`__). kitty
|
(the file :file:`NerdFontsSymbolsOnly.zip` from the `Nerd Fonts releases page
|
||||||
should pick up symbols from it automatically, and you can tell it to do so
|
<https://github.com/ryanoasis/nerd-fonts/releases>`__). kitty should pick up
|
||||||
explicitly in case it doesnt with the :opt:`symbol_map` directive::
|
symbols from it automatically, and you can tell it to do so explicitly in
|
||||||
|
case it doesn't with the :opt:`symbol_map` directive::
|
||||||
|
|
||||||
symbol_map U+23FB-U+23FE,U+2665,U+26A1,U+2B58,U+E000-U+E00A,U+E0A0-U+E0A3,U+E0B0-U+E0C8,U+E0CA,U+E0CC-U+E0D2,U+E0D4,U+E200-U+E2A9,U+E300-U+E3E3,U+E5FA-U+E62F,U+E700-U+E7C5,U+F000-U+F2E0,U+F300-U+F31C,U+F400-U+F4A9,U+F500-U+F8FF Symbols Nerd Font
|
symbol_map U+23FB-U+23FE,U+2665,U+26A1,U+2B58,U+E000-U+E00A,U+E0A0-U+E0A3,U+E0B0-U+E0C8,U+E0CA,U+E0CC-U+E0D2,U+E0D4,U+E200-U+E2A9,U+E300-U+E3E3,U+E5FA-U+E62F,U+E700-U+E7C5,U+F000-U+F2E0,U+F300-U+F31C,U+F400-U+F4A9,U+F500-U+F8FF Symbols Nerd Font
|
||||||
|
|
||||||
|
Those Unicode symbols beyond the ``E000-F8FF`` Unicode private use area are
|
||||||
|
not included.
|
||||||
|
|
||||||
If your font is not listed in ``kitty +list-fonts`` it means that it is not
|
If your font is not listed in ``kitty +list-fonts`` it means that it is not
|
||||||
monospace or is a bitmapped font. On Linux you can list all monospace fonts with::
|
monospace or is a bitmapped font. On Linux you can list all monospace fonts
|
||||||
|
with::
|
||||||
|
|
||||||
fc-list : family spacing outline scalable | grep -e spacing=100 -e spacing=90 | grep -e outline=True | grep -e scalable=True
|
fc-list : family spacing outline scalable | grep -e spacing=100 -e spacing=90 | grep -e outline=True | grep -e scalable=True
|
||||||
|
|
||||||
Note that the spacing property is calculated by fontconfig based on actual
|
Note that the spacing property is calculated by fontconfig based on actual glyph
|
||||||
glyph widths in the font. If for some reason fontconfig concludes your favorite
|
widths in the font. If for some reason fontconfig concludes your favorite
|
||||||
monospace font does not have ``spacing=100`` you can override it by using the
|
monospace font does not have ``spacing=100`` you can override it by using the
|
||||||
following :file:`~/.config/fontconfig/fonts.conf`::
|
following :file:`~/.config/fontconfig/fonts.conf`::
|
||||||
|
|
||||||
@@ -275,7 +285,7 @@ homepage:
|
|||||||
|
|
||||||
On macOS you can change the icon by following the steps:
|
On macOS you can change the icon by following the steps:
|
||||||
|
|
||||||
#. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+i`
|
#. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+I`
|
||||||
#. Drag :file:`kitty.icns` onto the application icon in the kitty info pane
|
#. Drag :file:`kitty.icns` onto the application icon in the kitty info pane
|
||||||
#. Delete the icon cache and restart Dock::
|
#. Delete the icon cache and restart Dock::
|
||||||
|
|
||||||
@@ -295,7 +305,10 @@ the :sc:`send_text <send_text>` you can use the ``show_key`` kitten. Run::
|
|||||||
|
|
||||||
kitty +kitten show_key
|
kitty +kitten show_key
|
||||||
|
|
||||||
Then press the key you want to emulate.
|
Then press the key you want to emulate. Note that this kitten will only show
|
||||||
|
keys that actually reach the terminal program, in particular, keys mapped to
|
||||||
|
actions in kitty will not be shown. To check those first map them to
|
||||||
|
:ac:`no_op`.
|
||||||
|
|
||||||
How do I open a new window or tab with the same working directory as the current window?
|
How do I open a new window or tab with the same working directory as the current window?
|
||||||
--------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------
|
||||||
@@ -322,8 +335,8 @@ variables which kitty will now inherit.
|
|||||||
You need to make sure that the environment variables you define in your shell's
|
You need to make sure that the environment variables you define in your shell's
|
||||||
rc files are either also defined system wide or via the :opt:`env` directive in
|
rc files are either also defined system wide or via the :opt:`env` directive in
|
||||||
:file:`kitty.conf`. Common environment variables that cause issues are those
|
:file:`kitty.conf`. Common environment variables that cause issues are those
|
||||||
related to localization, such as ``LANG, LC_*`` and loading of configuration
|
related to localization, such as :envvar:`LANG`, ``LC_*`` and loading of
|
||||||
files such as ``XDG_*, KITTY_CONFIG_DIRECTORY``.
|
configuration files such as ``XDG_*``, :envvar:`KITTY_CONFIG_DIRECTORY`.
|
||||||
|
|
||||||
To see the environment variables that kitty sees, you can add the following
|
To see the environment variables that kitty sees, you can add the following
|
||||||
mapping to :file:`kitty.conf`::
|
mapping to :file:`kitty.conf`::
|
||||||
@@ -340,40 +353,42 @@ sorts of places where they may or may not work.
|
|||||||
I am using tmux and have a problem
|
I am using tmux and have a problem
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
First, terminal multiplexers are :iss:`a bad idea <391#issuecomment-638320745>`, do
|
First, terminal multiplexers are :iss:`a bad idea <391#issuecomment-638320745>`,
|
||||||
not use them, if at all possible. kitty contains features that do all of what
|
do not use them, if at all possible. kitty contains features that do all of what
|
||||||
tmux does, but better, with the exception of remote persistence (:iss:`391`).
|
tmux does, but better, with the exception of remote persistence (:iss:`391`).
|
||||||
If you still want to use tmux, read on.
|
If you still want to use tmux, read on.
|
||||||
|
|
||||||
Image display will not work, see `tmux issue
|
Image display will not work, see `tmux issue
|
||||||
<https://github.com/tmux/tmux/issues/1391>`_.
|
<https://github.com/tmux/tmux/issues/1391>`__.
|
||||||
|
|
||||||
Using ancient versions of tmux such as 1.8 will
|
Using ancient versions of tmux such as 1.8 will cause gibberish on screen when
|
||||||
cause gibberish on screen when pressing keys (:iss:`3541`).
|
pressing keys (:iss:`3541`).
|
||||||
|
|
||||||
If you are using tmux with multiple terminals or you start it under one
|
If you are using tmux with multiple terminals or you start it under one terminal
|
||||||
terminal and then switch to another and these terminals have different TERM
|
and then switch to another and these terminals have different :envvar:`TERM`
|
||||||
variables, tmux will break. You will need to restart it as tmux does not
|
variables, tmux will break. You will need to restart it as tmux does not support
|
||||||
support multiple terminfo definitions.
|
multiple terminfo definitions.
|
||||||
|
|
||||||
If you use any of the advanced features that kitty has innovated, such as
|
If you use any of the advanced features that kitty has innovated, such as
|
||||||
styled underlines, desktop notifications, extended keyboard support, etc.
|
:doc:`styled underlines </underlines>`, :doc:`desktop notifications
|
||||||
they may or may not work, depending on the whims of tmux's maintainer, your
|
</desktop-notifications>`, :doc:`extended keyboard support
|
||||||
version of tmux, etc.
|
</keyboard-protocol>`, etc. they may or may not work, depending on the whims of
|
||||||
|
tmux's maintainer, your version of tmux, etc.
|
||||||
|
|
||||||
|
|
||||||
I opened and closed a lot of windows/tabs and top shows kitty's memory usage is very high?
|
I opened and closed a lot of windows/tabs and top shows kitty's memory usage is very high?
|
||||||
-------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
``top`` is not a good way to measure process memory usage. That is because on
|
:program:`top` is not a good way to measure process memory usage. That is
|
||||||
modern systems, when allocating memory to a process, the C library functions
|
because on modern systems, when allocating memory to a process, the C library
|
||||||
will typically allocate memory in large blocks, and give the process chunks of
|
functions will typically allocate memory in large blocks, and give the process
|
||||||
these blocks. When the process frees a chunk, the C library will not
|
chunks of these blocks. When the process frees a chunk, the C library will not
|
||||||
necessarily release the underlying block back to the OS. So even though the
|
necessarily release the underlying block back to the OS. So even though the
|
||||||
application has released the memory, ``top`` will still claim the process is
|
application has released the memory, :program:`top` will still claim the process
|
||||||
using it.
|
is using it.
|
||||||
|
|
||||||
To check for memory leaks, instead use a tool like ``valgrind``. Run::
|
To check for memory leaks, instead use a tool like `Valgrind
|
||||||
|
<https://valgrind.org/>`__. Run::
|
||||||
|
|
||||||
PYTHONMALLOC=malloc valgrind --tool=massif kitty
|
PYTHONMALLOC=malloc valgrind --tool=massif kitty
|
||||||
|
|
||||||
@@ -386,18 +401,19 @@ that window, maybe run yes or find again. Then quit kitty and run::
|
|||||||
You will see the allocations graph goes up when you opened the windows, then
|
You will see the allocations graph goes up when you opened the windows, then
|
||||||
goes back down when you closed them, indicating there were no memory leaks.
|
goes back down when you closed them, indicating there were no memory leaks.
|
||||||
|
|
||||||
For those interested, you can get a similar profile out of ``valgrind`` as you get
|
For those interested, you can get a similar profile out of :program:`valgrind`
|
||||||
with ``top`` by adding ``--pages-as-heap=yes`` then you will see that memory
|
as you get with :program:`top` by adding ``--pages-as-heap=yes`` then you will
|
||||||
allocated in malloc is not freed in free. This can be further refined if you
|
see that memory allocated in malloc is not freed in free. This can be further
|
||||||
use `glibc`` as your C library by setting the environment variable
|
refined if you use ``glibc`` as your C library by setting the environment
|
||||||
``MALLOC_MMAP_THRESHOLD_=64``. This will cause free to actually free memory
|
variable ``MALLOC_MMAP_THRESHOLD_=64``. This will cause free to actually free
|
||||||
allocated in sizes of more than 64 bytes. With this set, memory usage will
|
memory allocated in sizes of more than 64 bytes. With this set, memory usage
|
||||||
climb high, then fall when closing windows, but not fall all the way back. The
|
will climb high, then fall when closing windows, but not fall all the way back.
|
||||||
remaining used memory can be investigated using valgrind again, and it will
|
The remaining used memory can be investigated using valgrind again, and it will
|
||||||
come from arenas in the GPU drivers and the per thread arenas glibc's malloc
|
come from arenas in the GPU drivers and the per thread arenas glibc's malloc
|
||||||
maintains. These too allocate memory in large blocks and dont release it back
|
maintains. These too allocate memory in large blocks and don't release it back
|
||||||
to the OS immediately.
|
to the OS immediately.
|
||||||
|
|
||||||
|
|
||||||
Why does kitty sometimes start slowly on my Linux system?
|
Why does kitty sometimes start slowly on my Linux system?
|
||||||
-------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -421,4 +437,4 @@ The correct command will depend on your situation and hardware.
|
|||||||
:file:`libEGL_mesa.so` and ignore :file:`libEGL_nvidia.so` also available on the
|
:file:`libEGL_mesa.so` and ignore :file:`libEGL_nvidia.so` also available on the
|
||||||
system, which will wake the NVIDIA card during device enumeration.
|
system, which will wake the NVIDIA card during device enumeration.
|
||||||
``MESA_LOADER_DRIVER_OVERRIDE`` also assures that Mesa won't offer any NVIDIA
|
``MESA_LOADER_DRIVER_OVERRIDE`` also assures that Mesa won't offer any NVIDIA
|
||||||
card during enumeration, and will instead just use `/lib/dri/radeonsi_dri.so`.
|
card during enumeration, and will instead just use :file:`radeonsi_dri.so`.
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ Glossary
|
|||||||
hyperlinks
|
hyperlinks
|
||||||
Terminals can have hyperlinks, just like the internet. In kitty you can
|
Terminals can have hyperlinks, just like the internet. In kitty you can
|
||||||
:doc:`control exactly what happens <open_actions>` when clicking on a
|
:doc:`control exactly what happens <open_actions>` when clicking on a
|
||||||
hyperlink, based on the type of link and its URL.
|
hyperlink, based on the type of link and its URL. See also `Hyperlinks in terminal
|
||||||
|
emulators <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`__.
|
||||||
|
|
||||||
.. _env_vars:
|
.. _env_vars:
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ Environment variables
|
|||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
Variables that influence kitty behavior
|
Variables that influence kitty behavior
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. envvar:: KITTY_CONFIG_DIRECTORY
|
.. envvar:: KITTY_CONFIG_DIRECTORY
|
||||||
|
|
||||||
@@ -65,9 +67,9 @@ Variables that influence kitty behavior
|
|||||||
|
|
||||||
.. envvar:: VISUAL
|
.. envvar:: VISUAL
|
||||||
|
|
||||||
The terminal editor (such as ``vi`` or ``nano``) kitty uses, when, for
|
The terminal based text editor (such as :program:`vi` or :program:`nano`)
|
||||||
instance, opening :file:`kitty.conf` in response to :sc:`edit_config_file`.
|
kitty uses, when, for instance, opening :file:`kitty.conf` in response to
|
||||||
|
:sc:`edit_config_file`.
|
||||||
|
|
||||||
.. envvar:: EDITOR
|
.. envvar:: EDITOR
|
||||||
|
|
||||||
@@ -84,13 +86,41 @@ Variables that influence kitty behavior
|
|||||||
is possible for the autodiscovery to fail; the default Wayland XKB mappings
|
is possible for the autodiscovery to fail; the default Wayland XKB mappings
|
||||||
are used in this case. See :pull:`3943` for details.
|
are used in this case. See :pull:`3943` for details.
|
||||||
|
|
||||||
|
.. envvar:: SSH_ASKPASS
|
||||||
|
|
||||||
|
Specify the program for SSH to ask for passwords. When this is set, :doc:`ssh
|
||||||
|
kitten </kittens/ssh>` will use this environment variable by default. See
|
||||||
|
:opt:`askpass <kitten-ssh.askpass>` for details.
|
||||||
|
|
||||||
|
.. envvar:: KITTY_CLONE_SOURCE_CODE
|
||||||
|
|
||||||
|
Set this to some shell code that will be executed in the cloned window with
|
||||||
|
:code:`eval` when :ref:`clone-in-kitty <clone_shell>` is used.
|
||||||
|
|
||||||
|
.. envvar:: KITTY_CLONE_SOURCE_PATH
|
||||||
|
|
||||||
|
Set this to the path of a file that will be sourced in the cloned window when
|
||||||
|
:ref:`clone-in-kitty <clone_shell>` is used.
|
||||||
|
|
||||||
|
.. envvar:: KITTY_DEVELOP_FROM
|
||||||
|
|
||||||
|
Set this to the directory path of the kitty source code and its Python code
|
||||||
|
will be loaded from there. Only works with official binary builds.
|
||||||
|
|
||||||
|
|
||||||
Variables that kitty sets when running child programs
|
Variables that kitty sets when running child programs
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. envvar:: LANG
|
.. envvar:: LANG
|
||||||
|
|
||||||
This is set only on macOS, and only if the country and language from the
|
This is only set on macOS. If the country and language from the macOS user
|
||||||
macOS user settings form a valid locale.
|
settings form an invalid locale, it will be set to :code:`en_US.UTF-8`.
|
||||||
|
|
||||||
|
|
||||||
|
.. envvar:: PATH
|
||||||
|
|
||||||
|
kitty prepends itself to the PATH of its own environment to ensure the
|
||||||
|
functions calling :program:`kitty` will work properly.
|
||||||
|
|
||||||
|
|
||||||
.. envvar:: KITTY_WINDOW_ID
|
.. envvar:: KITTY_WINDOW_ID
|
||||||
@@ -137,7 +167,7 @@ Variables that kitty sets when running child programs
|
|||||||
|
|
||||||
Set when the :doc:`remote control <remote-control>` facility is enabled and
|
Set when the :doc:`remote control <remote-control>` facility is enabled and
|
||||||
the a socket is used for control via :option:`kitty --listen-on` or :opt:`listen_on`.
|
the a socket is used for control via :option:`kitty --listen-on` or :opt:`listen_on`.
|
||||||
Contains the path to the socket. Avoids needs to use :option:`kitty @ --to` when
|
Contains the path to the socket. Avoid the need to use :option:`kitty @ --to` when
|
||||||
issuing remote control commands.
|
issuing remote control commands.
|
||||||
|
|
||||||
|
|
||||||
@@ -158,10 +188,28 @@ Variables that kitty sets when running child programs
|
|||||||
.. envvar:: KITTY_COMMON_OPTS
|
.. envvar:: KITTY_COMMON_OPTS
|
||||||
|
|
||||||
Set with the values of some common kitty options when running
|
Set with the values of some common kitty options when running
|
||||||
kittens, so kittens can use them without needing to load kitty.conf.
|
kittens, so kittens can use them without needing to load :file:`kitty.conf`.
|
||||||
|
|
||||||
|
|
||||||
.. envvar:: KITTY_SHELL_INTEGRATION
|
.. envvar:: KITTY_SHELL_INTEGRATION
|
||||||
|
|
||||||
Set when enabling :ref:`shell_integration`. It is automatically removed by
|
Set when enabling :ref:`shell_integration`. It is automatically removed by
|
||||||
the shell integration scripts.
|
the shell integration scripts.
|
||||||
|
|
||||||
|
|
||||||
|
.. envvar:: ZDOTDIR
|
||||||
|
|
||||||
|
Set when enabling :ref:`shell_integration` with :program:`zsh`, allowing
|
||||||
|
:program:`zsh` to automatically load the integration script.
|
||||||
|
|
||||||
|
|
||||||
|
.. envvar:: XDG_DATA_DIRS
|
||||||
|
|
||||||
|
Set when enabling :ref:`shell_integration` with :program:`fish`, allowing
|
||||||
|
:program:`fish` to automatically load the integration script.
|
||||||
|
|
||||||
|
|
||||||
|
.. envvar:: ENV
|
||||||
|
|
||||||
|
Set when enabling :ref:`shell_integration` with :program:`bash`, allowing
|
||||||
|
:program:`bash` to automatically load the integration script.
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ Some programs and libraries that use the kitty graphics protocol:
|
|||||||
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with
|
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with
|
||||||
image previews, see this `PR <https://github.com/ranger/ranger/pull/1077>`_
|
image previews, see this `PR <https://github.com/ranger/ranger/pull/1077>`_
|
||||||
* :doc:`kitty-diff <kittens/diff>` - a side-by-side terminal diff program with support for images
|
* :doc:`kitty-diff <kittens/diff>` - a side-by-side terminal diff program with support for images
|
||||||
|
* `tpix <https://github.com/jesvedberg/tpix>` - a statically compiled binary that can be used to display images and easily installed on remote servers without root access
|
||||||
* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol
|
* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol
|
||||||
* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system
|
* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system
|
||||||
information tool
|
information tool
|
||||||
@@ -377,7 +378,9 @@ id. To do so add the ``p`` key with a number between ``1`` and ``4294967295``.
|
|||||||
When you specify a placement id, it will be added to the acknowledgement code
|
When you specify a placement id, it will be added to the acknowledgement code
|
||||||
above. Every placement is uniquely identified by the pair of the ``image id``
|
above. Every placement is uniquely identified by the pair of the ``image id``
|
||||||
and the ``placement id``. If you specify a placement id for an image that does
|
and the ``placement id``. If you specify a placement id for an image that does
|
||||||
not have an id, it will be ignored. An example response::
|
not have an id (i.e. has id=0), it will be ignored. In particular this means
|
||||||
|
there can exist multiple images with ``image id=0, placement id=0``.
|
||||||
|
An example response::
|
||||||
|
|
||||||
<ESC>_Gi=<image id>,p=<placement id>;OK<ESC>\
|
<ESC>_Gi=<image id>,p=<placement id>;OK<ESC>\
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,6 @@ exec_kitty() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
[ -n "$KITTY_WINDOW_ID" ] && die "You should not try to update kitty from within kitty itself. For best results, quit all kitty instances and run this script from another terminal"
|
|
||||||
detect_os
|
detect_os
|
||||||
parse_args "$@"
|
parse_args "$@"
|
||||||
detect_network_tool
|
detect_network_tool
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ Integrations with other tools
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
kitty provides extremely powerful interfaces such as :doc:`remote-control` and
|
kitty provides extremely powerful interfaces such as :doc:`remote-control` and
|
||||||
:doc:`kittens/custom` and :doc:`kittens/icat`
|
:doc:`kittens/custom` and :doc:`kittens/icat` that allow it to be integrated
|
||||||
that allow it to be integrated with other tools seamlessly.
|
with other tools seamlessly.
|
||||||
|
|
||||||
|
|
||||||
Image and document viewers
|
Image and document viewers
|
||||||
@@ -30,19 +30,22 @@ Display markdown files nicely formatted with images in the terminal
|
|||||||
|
|
||||||
`ranger <https://github.com/ranger/ranger>`_
|
`ranger <https://github.com/ranger/ranger>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
A terminal file manager, with previews of file contents powered by kitty's graphics protocol.
|
A terminal file manager, with previews of file contents powered by kitty's
|
||||||
|
graphics protocol.
|
||||||
|
|
||||||
.. _tool_nnn:
|
.. _tool_nnn:
|
||||||
|
|
||||||
`nnn <https://github.com/jarun/nnn/>`_
|
`nnn <https://github.com/jarun/nnn/>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Another terminal file manager, with previews of file contents powered by kitty's graphics protocol.
|
Another terminal file manager, with previews of file contents powered by kitty's
|
||||||
|
graphics protocol.
|
||||||
|
|
||||||
.. _tool_hunter:
|
.. _tool_hunter:
|
||||||
|
|
||||||
`hunter <https://github.com/rabite0/hunter>`_
|
`hunter <https://github.com/rabite0/hunter>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Another terminal file manager, with previews of file contents powered by kitty's graphics protocol.
|
Another terminal file manager, with previews of file contents powered by kitty's
|
||||||
|
graphics protocol.
|
||||||
|
|
||||||
.. _tool_koneko:
|
.. _tool_koneko:
|
||||||
|
|
||||||
@@ -61,8 +64,8 @@ View images in the terminal, similar to kitty's icat.
|
|||||||
|
|
||||||
`nb <https://github.com/xwmx/nb>`_
|
`nb <https://github.com/xwmx/nb>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
command line and local web note-taking, bookmarking, archiving, and
|
Command line and local web note-taking, bookmarking, archiving, and knowledge
|
||||||
knowledge base application that uses kitty's graphics protocol for images.
|
base application that uses kitty's graphics protocol for images.
|
||||||
|
|
||||||
.. _tool_w3m:
|
.. _tool_w3m:
|
||||||
|
|
||||||
@@ -75,9 +78,9 @@ images.
|
|||||||
|
|
||||||
`timg <https://github.com/hzeller/timg>`_
|
`timg <https://github.com/hzeller/timg>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
A terminal image and video viewer, that displays static and animated
|
A terminal image and video viewer, that displays static and animated images or
|
||||||
images or plays videos. Fast multi-threaded loading, JPEG exif rotation,
|
plays videos. Fast multi-threaded loading, JPEG exif rotation, grid view and
|
||||||
grid view and connecting to the webcam make it a versatile terminal utility.
|
connecting to the webcam make it a versatile terminal utility.
|
||||||
|
|
||||||
|
|
||||||
System and data visualisation tools
|
System and data visualisation tools
|
||||||
@@ -87,7 +90,8 @@ System and data visualisation tools
|
|||||||
|
|
||||||
`neofetch <https://github.com/dylanaraps/neofetch>`_
|
`neofetch <https://github.com/dylanaraps/neofetch>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
A command line system information tool that shows images using kitty's graphics protocol
|
A command line system information tool that shows images using kitty's graphics
|
||||||
|
protocol
|
||||||
|
|
||||||
.. _tool_matplotlib:
|
.. _tool_matplotlib:
|
||||||
|
|
||||||
@@ -105,16 +109,16 @@ Show images from Julia directly in kitty
|
|||||||
|
|
||||||
`euporie <https://github.com/joouha/euporie>`_
|
`euporie <https://github.com/joouha/euporie>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
A text-based user interface for running and editing Jupyter notebooks,
|
A text-based user interface for running and editing Jupyter notebooks, powered
|
||||||
powered by kitty's graphics protocol for displaying plots
|
by kitty's graphics protocol for displaying plots
|
||||||
|
|
||||||
.. _tool_gnuplot:
|
.. _tool_gnuplot:
|
||||||
|
|
||||||
`gnuplot <http://www.gnuplot.info/>`_
|
`gnuplot <http://www.gnuplot.info/>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
A graphing and data visualization tool that can be made to display its
|
A graphing and data visualization tool that can be made to display its output in
|
||||||
output in kitty with the following bash snippet:
|
kitty with the following bash snippet:
|
||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
@@ -171,26 +175,28 @@ Run applications on your system with fuzzy find inside a kitty window
|
|||||||
Editor integration
|
Editor integration
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|kitty| can be integrated into many different terminal editors to add features
|
|kitty| can be integrated into many different terminal based text editors to add
|
||||||
such a split windows, previews, REPLs etc.
|
features such a split windows, previews, REPLs etc.
|
||||||
|
|
||||||
.. tool_kakoune:
|
.. tool_kakoune:
|
||||||
|
|
||||||
`kakoune <https://kakoune.org/>`_
|
`kakoune <https://kakoune.org/>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
integrates with kitty to use native kitty windows for its windows/panels and REPLs.
|
Integrates with kitty to use native kitty windows for its windows/panels and
|
||||||
|
REPLs.
|
||||||
|
|
||||||
.. tool_vim_slime:
|
.. tool_vim_slime:
|
||||||
|
|
||||||
`vim-slime <https://github.com/jpalardy/vim-slime#kitty>`_
|
`vim-slime <https://github.com/jpalardy/vim-slime#kitty>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
uses kitty remote control for a Lisp REPL.
|
Uses kitty remote control for a Lisp REPL.
|
||||||
|
|
||||||
.. tool_vim_kitty_navigator:
|
.. tool_vim_kitty_navigator:
|
||||||
|
|
||||||
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
|
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
|
Allows you to navigate seamlessly between vim and kitty splits using a
|
||||||
|
consistent set of hotkeys.
|
||||||
|
|
||||||
.. tool_vim_test:
|
.. tool_vim_test:
|
||||||
|
|
||||||
@@ -202,7 +208,7 @@ Allows easily running tests in a terminal window
|
|||||||
|
|
||||||
`hologram.nvim <https://github.com/edluffy/hologram.nvim>`_
|
`hologram.nvim <https://github.com/edluffy/hologram.nvim>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Terminal image viewer for nvim
|
Terminal image viewer for Neovim
|
||||||
|
|
||||||
|
|
||||||
Scrollback manipulation
|
Scrollback manipulation
|
||||||
@@ -228,21 +234,21 @@ Miscellaneous
|
|||||||
|
|
||||||
`kitty-smart-tab <https://github.com/yurikhan/kitty-smart-tab>`_
|
`kitty-smart-tab <https://github.com/yurikhan/kitty-smart-tab>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
use keys to either control tabs or pass them onto running applications if
|
Use keys to either control tabs or pass them onto running applications if no
|
||||||
no tabs are present
|
tabs are present
|
||||||
|
|
||||||
.. tool_kitty_smart_scroll:
|
.. tool_kitty_smart_scroll:
|
||||||
|
|
||||||
`kitty-smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
|
`kitty-smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
use keys to either scroll or pass them onto running applications if
|
Use keys to either scroll or pass them onto running applications if no
|
||||||
no scrollback buffer is present
|
scrollback buffer is present
|
||||||
|
|
||||||
.. tool_kitti3:
|
.. tool_kitti3:
|
||||||
|
|
||||||
`kitti3 <https://github.com/LandingEllipse/kitti3>`_
|
`kitti3 <https://github.com/LandingEllipse/kitti3>`_
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
allow using kitty as a drop-down terminal under the i3 window manager
|
Allow using kitty as a drop-down terminal under the i3 window manager
|
||||||
|
|
||||||
.. tool_weechat_hints:
|
.. tool_weechat_hints:
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ In addition to kitty, this protocol is also implemented in:
|
|||||||
* The `foot terminal <https://codeberg.org/dnkl/foot/issues/319>`__
|
* The `foot terminal <https://codeberg.org/dnkl/foot/issues/319>`__
|
||||||
* The `notcurses library
|
* The `notcurses library
|
||||||
<https://github.com/dankamongmen/notcurses/issues/2131>`__
|
<https://github.com/dankamongmen/notcurses/issues/2131>`__
|
||||||
|
* The `neovim text editor <https://github.com/neovim/neovim/pull/18181>`__
|
||||||
* The `kakoune text editor <https://github.com/mawww/kakoune/issues/4103>`__
|
* The `kakoune text editor <https://github.com/mawww/kakoune/issues/4103>`__
|
||||||
* The `dte text editor <https://gitlab.com/craigbarnes/dte/-/issues/138>`__
|
* The `dte text editor <https://gitlab.com/craigbarnes/dte/-/issues/138>`__
|
||||||
|
|
||||||
@@ -377,7 +378,7 @@ An application can query the terminal for support of this protocol by sending
|
|||||||
the escape code querying for the :ref:`current progressive enhancement
|
the escape code querying for the :ref:`current progressive enhancement
|
||||||
<progressive_enhancement>` status
|
<progressive_enhancement>` status
|
||||||
followed by request for the `primary device attributes
|
followed by request for the `primary device attributes
|
||||||
<https://vt100.net/docs/vt510-rm/DA1.html>`. If an answer for the device
|
<https://vt100.net/docs/vt510-rm/DA1.html>`__. If an answer for the device
|
||||||
attributes is received without getting back an answer for the progressive
|
attributes is received without getting back an answer for the progressive
|
||||||
enhancement the terminal does not support this protocol.
|
enhancement the terminal does not support this protocol.
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,21 @@ broadcast
|
|||||||
|
|
||||||
*Type text in all kitty windows simultaneously*
|
*Type text in all kitty windows simultaneously*
|
||||||
|
|
||||||
The ``broadcast`` kitten can be used to type text simultaneously in
|
The ``broadcast`` kitten can be used to type text simultaneously in all
|
||||||
all kitty windows (or a subset as desired).
|
:term:`kitty windows <window>` (or a subset as desired).
|
||||||
|
|
||||||
To use it, simply create a mapping in :file:`kitty.conf` such as::
|
To use it, simply create a mapping in :file:`kitty.conf` such as::
|
||||||
|
|
||||||
map F1 launch --allow-remote-control kitty +kitten broadcast
|
map f1 launch --allow-remote-control kitty +kitten broadcast
|
||||||
|
|
||||||
Then press the :kbd:`F1` key and whatever you type in the newly created widow
|
Then press the :kbd:`F1` key and whatever you type in the newly created window
|
||||||
will be sent to all kitty windows.
|
will be sent to all kitty windows.
|
||||||
|
|
||||||
You can use the options described below to control which windows
|
You can use the options described below to control which windows are selected.
|
||||||
are selected.
|
|
||||||
|
For example, only broadcast to other windows in the current tab::
|
||||||
|
|
||||||
|
map f1 launch --allow-remote-control kitty +kitten broadcast --match-tab state:focused
|
||||||
|
|
||||||
.. program:: kitty +kitten broadcast
|
.. program:: kitty +kitten broadcast
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
Custom kittens
|
Custom kittens
|
||||||
=================
|
=================
|
||||||
|
|
||||||
You can easily create your own kittens to extend kitty. They are just
|
You can easily create your own kittens to extend kitty. They are just terminal
|
||||||
terminal programs written in Python. When launching a kitten, kitty will
|
programs written in Python. When launching a kitten, kitty will open an overlay
|
||||||
open an overlay window over the current window and optionally pass the
|
window over the current window and optionally pass the contents of the current
|
||||||
contents of the current window/scrollback to the kitten over its :file:`STDIN`.
|
window/scrollback to the kitten over its :file:`STDIN`. The kitten can then
|
||||||
The kitten can then perform whatever actions it likes, just as a normal
|
perform whatever actions it likes, just as a normal terminal program. After
|
||||||
terminal program. After execution of the kitten is complete, it has access
|
execution of the kitten is complete, it has access to the running kitty instance
|
||||||
to the running kitty instance so it can perform arbitrary actions
|
so it can perform arbitrary actions such as closing windows, pasting text, etc.
|
||||||
such as closing windows, pasting text, etc.
|
|
||||||
|
|
||||||
Let's see a simple example of creating a kitten. It will ask the user for some
|
Let's see a simple example of creating a kitten. It will ask the user for some
|
||||||
input and paste it into the terminal window.
|
input and paste it into the terminal window.
|
||||||
|
|
||||||
Create a file in the kitty config folder, :file:`~/.config/kitty/mykitten.py`
|
Create a file in the kitty config directory, :file:`~/.config/kitty/mykitten.py`
|
||||||
(you might need to adjust the path to wherever the kitty config folder is on
|
(you might need to adjust the path to wherever the :ref:`kitty config directory
|
||||||
your machine).
|
<confloc>` is on your machine).
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
@@ -43,11 +42,12 @@ Now in :file:`kitty.conf` add the lines::
|
|||||||
map ctrl+k kitten mykitten.py
|
map ctrl+k kitten mykitten.py
|
||||||
|
|
||||||
|
|
||||||
Start kitty and press :kbd:`ctrl+k` and you should see the kitten running.
|
Start kitty and press :kbd:`Ctrl+K` and you should see the kitten running.
|
||||||
The best way to develop your own kittens is to modify one of the built in
|
The best way to develop your own kittens is to modify one of the built-in
|
||||||
kittens. Look in the kittens sub-directory of the kitty source code for those.
|
kittens. Look in the `kittens sub-directory
|
||||||
Or see below for a list of :ref:`third-party kittens <external_kittens>`,
|
<https://github.com/kovidgoyal/kitty/tree/master/kittens>`__ of the kitty source
|
||||||
that other kitty users have created.
|
code for those. Or see below for a list of :ref:`third-party kittens
|
||||||
|
<external_kittens>`, that other kitty users have created.
|
||||||
|
|
||||||
|
|
||||||
Passing arguments to kittens
|
Passing arguments to kittens
|
||||||
@@ -60,36 +60,40 @@ You can pass arguments to kittens by defining them in the map directive in
|
|||||||
|
|
||||||
These will be available as the ``args`` parameter in the ``main()`` and
|
These will be available as the ``args`` parameter in the ``main()`` and
|
||||||
``handle_result()`` functions. Note also that the current working directory
|
``handle_result()`` functions. Note also that the current working directory
|
||||||
of the kitten is set to the working directory of whatever program is
|
of the kitten is set to the working directory of whatever program is running in
|
||||||
running in the active kitty window. The special argument ``@selection``
|
the active kitty window. The special argument ``@selection`` is replaced by the
|
||||||
is replaced by the currently selected text in the active kitty window.
|
currently selected text in the active kitty window.
|
||||||
|
|
||||||
|
|
||||||
Passing the contents of the screen to the kitten
|
Passing the contents of the screen to the kitten
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
If you would like your kitten to have access to the contents of the screen
|
If you would like your kitten to have access to the contents of the screen
|
||||||
and/or the scrollback buffer, you just need to add an annotation to the ``handle_result()``
|
and/or the scrollback buffer, you just need to add an annotation to the
|
||||||
function, telling kitty what kind of input your kitten would like. For example:
|
``handle_result()`` function, telling kitty what kind of input your kitten would
|
||||||
|
like. For example:
|
||||||
|
|
||||||
.. code-block:: py
|
.. code-block:: py
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from kitty.boss import Boss
|
||||||
|
|
||||||
# in main, STDIN is for the kitten process and will contain
|
# in main, STDIN is for the kitten process and will contain
|
||||||
# the contents of the screen
|
# the contents of the screen
|
||||||
def main(args):
|
def main(args: List[str]) -> str:
|
||||||
return sys.stdin.read()
|
return sys.stdin.read()
|
||||||
|
|
||||||
# in handle_result, STDIN is for the kitty process itself, rather
|
# in handle_result, STDIN is for the kitty process itself, rather
|
||||||
# than the kitten process and should not be read from.
|
# than the kitten process and should not be read from.
|
||||||
from kittens.tui.handler import result_handler
|
from kittens.tui.handler import result_handler
|
||||||
@result_handler(type_of_input='text')
|
@result_handler(type_of_input='text')
|
||||||
def handle_result(args, stdin_data, target_window_id, boss):
|
def handle_result(args: List[str], stdin_data: str, target_window_id: int, boss: Boss) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
This will send the plain text of the active window to the kitten's
|
This will send the plain text of the active window to the kitten's
|
||||||
:file:`STDIN`. There are many other types of input you can ask for,
|
:file:`STDIN`. There are many other types of input you can ask for, described in
|
||||||
described in the table below:
|
the table below:
|
||||||
|
|
||||||
.. table:: Types of input to kittens
|
.. table:: Types of input to kittens
|
||||||
:align: left
|
:align: left
|
||||||
@@ -121,31 +125,35 @@ and ``first_output`` gives the output of the first command currently on screen.
|
|||||||
These can also be combined with ``screen`` and ``ansi`` for formatting.
|
These can also be combined with ``screen`` and ``ansi`` for formatting.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
For the types based on the output of a command,
|
For the types based on the output of a command, :ref:`shell_integration` is
|
||||||
:ref:`shell_integration` is required.
|
required.
|
||||||
|
|
||||||
|
|
||||||
Using kittens to script kitty, without any terminal UI
|
Using kittens to script kitty, without any terminal UI
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
If you would like your kitten to script kitty, without bothering to write a
|
If you would like your kitten to script kitty, without bothering to write a
|
||||||
terminal program, you can tell the kittens system to run the
|
terminal program, you can tell the kittens system to run the ``handle_result()``
|
||||||
``handle_result()`` function without first running the ``main()`` function.
|
function without first running the ``main()`` function.
|
||||||
|
|
||||||
For example, here is a kitten that "zooms/unzooms" the current terminal window
|
For example, here is a kitten that "zooms in/zooms out" the current terminal
|
||||||
by switching to the stack layout or back to the previous layout. This is
|
window by switching to the stack layout or back to the previous layout. This is
|
||||||
equivalent to the builtin :ac:`toggle_layout` action.
|
equivalent to the builtin :ac:`toggle_layout` action.
|
||||||
|
|
||||||
Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py`
|
Create a Python file in the :ref:`kitty config directory <confloc>`,
|
||||||
|
:file:`~/.config/kitty/zoom_toggle.py`
|
||||||
|
|
||||||
.. code-block:: py
|
.. code-block:: py
|
||||||
|
|
||||||
def main(args):
|
from typing import List
|
||||||
|
from kitty.boss import Boss
|
||||||
|
|
||||||
|
def main(args: List[str]) -> str:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from kittens.tui.handler import result_handler
|
from kittens.tui.handler import result_handler
|
||||||
@result_handler(no_ui=True)
|
@result_handler(no_ui=True)
|
||||||
def handle_result(args, answer, target_window_id, boss):
|
def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
|
||||||
tab = boss.active_tab
|
tab = boss.active_tab
|
||||||
if tab is not None:
|
if tab is not None:
|
||||||
if tab.current_layout.name == 'stack':
|
if tab.current_layout.name == 'stack':
|
||||||
@@ -154,7 +162,7 @@ Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py`
|
|||||||
tab.goto_layout('stack')
|
tab.goto_layout('stack')
|
||||||
|
|
||||||
|
|
||||||
Now in kitty.conf add::
|
Now in :file:`kitty.conf` add::
|
||||||
|
|
||||||
map f11 kitten zoom_toggle.py
|
map f11 kitten zoom_toggle.py
|
||||||
|
|
||||||
@@ -165,7 +173,7 @@ layout, by simply adding the line::
|
|||||||
boss.toggle_fullscreen()
|
boss.toggle_fullscreen()
|
||||||
|
|
||||||
|
|
||||||
To the ``handle_result()`` function, above.
|
to the ``handle_result()`` function, above.
|
||||||
|
|
||||||
|
|
||||||
.. _send_mouse_event:
|
.. _send_mouse_event:
|
||||||
@@ -173,7 +181,7 @@ To the ``handle_result()`` function, above.
|
|||||||
Sending mouse events
|
Sending mouse events
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
If the program running in a window is receiving mouse events you can simulate
|
If the program running in a window is receiving mouse events, you can simulate
|
||||||
those using::
|
those using::
|
||||||
|
|
||||||
from kitty.fast_data_types import send_mouse_event
|
from kitty.fast_data_types import send_mouse_event
|
||||||
@@ -200,15 +208,15 @@ that type, and will return ``True`` if it sent the event, and ``False`` if not.
|
|||||||
Debugging kittens
|
Debugging kittens
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
The part of the kitten that runs in ``main()`` is just a normal program and
|
The part of the kitten that runs in ``main()`` is just a normal program and the
|
||||||
the output of print statements will be visible in the kitten window. Or
|
output of print statements will be visible in the kitten window. Or alternately,
|
||||||
alternately, you can use::
|
you can use::
|
||||||
|
|
||||||
from kittens.tui.loop import debug
|
from kittens.tui.loop import debug
|
||||||
debug('whatever')
|
debug('whatever')
|
||||||
|
|
||||||
The ``debug()`` function is just like ``print()`` except that the output
|
The ``debug()`` function is just like ``print()`` except that the output will
|
||||||
will appear in the ``STDOUT`` of the kitty process inside which the kitten is
|
appear in the ``STDOUT`` of the kitty process inside which the kitten is
|
||||||
running.
|
running.
|
||||||
|
|
||||||
The ``handle_result()`` part of the kitten runs inside the kitty process.
|
The ``handle_result()`` part of the kitten runs inside the kitty process.
|
||||||
@@ -216,12 +224,13 @@ The output of print statements will go to the ``STDOUT`` of the kitty process.
|
|||||||
So if you run kitty from another kitty instance, the output will be visible
|
So if you run kitty from another kitty instance, the output will be visible
|
||||||
in the first kitty instance.
|
in the first kitty instance.
|
||||||
|
|
||||||
|
|
||||||
Adding options to kittens
|
Adding options to kittens
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
If you would like to use kitty's config framework to make your kittens
|
If you would like to use kitty's config framework to make your kittens
|
||||||
configurable, you will need some boilerplate. In the directory
|
configurable, you will need some boilerplate. Put the following files in the
|
||||||
of your kitten make the following files.
|
directory of your kitten.
|
||||||
|
|
||||||
:file:`kitten_options_definition.py`
|
:file:`kitten_options_definition.py`
|
||||||
|
|
||||||
@@ -311,8 +320,9 @@ You can parse and read the options in your kitten using the following code:
|
|||||||
opts.config_overrides = overrides
|
opts.config_overrides = overrides
|
||||||
return opts
|
return opts
|
||||||
|
|
||||||
See the code for the builtin diff kitten for examples of creating more options
|
See `the code <https://github.com/kovidgoyal/kitty/tree/master/kittens/diff>`__
|
||||||
and keyboard shortcuts.
|
for the builtin :doc:`diff kitten </kittens/diff>` for examples of creating more
|
||||||
|
options and keyboard shortcuts.
|
||||||
|
|
||||||
.. _external_kittens:
|
.. _external_kittens:
|
||||||
|
|
||||||
@@ -320,7 +330,8 @@ Kittens created by kitty users
|
|||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
|
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
|
||||||
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
|
Allows you to navigate seamlessly between vim and kitty splits using a
|
||||||
|
consistent set of hotkeys.
|
||||||
|
|
||||||
`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
|
`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
|
||||||
Makes the kitty scroll bindings work in full screen applications
|
Makes the kitty scroll bindings work in full screen applications
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ Major Features
|
|||||||
|
|
||||||
* Displays diffs side-by-side in the kitty terminal
|
* Displays diffs side-by-side in the kitty terminal
|
||||||
|
|
||||||
* Does syntax highlighting of the displayed diffs, asynchronously, for maximum
|
* Does syntax highlighting of the displayed diffs, asynchronously, for
|
||||||
speed
|
maximum speed
|
||||||
|
|
||||||
* Displays images as well as text diffs, even over SSH
|
* Displays images as well as text diffs, even over SSH
|
||||||
|
|
||||||
@@ -31,11 +31,11 @@ Major Features
|
|||||||
Installation
|
Installation
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Simply :ref:`install kitty <quickstart>`. You also need
|
Simply :ref:`install kitty <quickstart>`. You also need to have either the `git
|
||||||
to have either the `git <https://git-scm.com/>`_ program or the ``diff`` program
|
<https://git-scm.com/>`__ program or the :program:`diff` program installed.
|
||||||
installed. Additionally, for syntax highlighting to work,
|
Additionally, for syntax highlighting to work, `pygments
|
||||||
`pygments <https://pygments.org/>`_ must be installed (note that pygments is
|
<https://pygments.org/>`__ must be installed (note that pygments is included in
|
||||||
included in the official kitty binary builds).
|
the official kitty binary builds).
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
@@ -45,9 +45,10 @@ In the kitty terminal, run::
|
|||||||
|
|
||||||
kitty +kitten diff file1 file2
|
kitty +kitten diff file1 file2
|
||||||
|
|
||||||
to see the diff between file1 and file2.
|
to see the diff between :file:`file1` and :file:`file2`.
|
||||||
|
|
||||||
Create an alias in your shell's startup file to shorten the command, for example:
|
Create an alias in your shell's startup file to shorten the command, for
|
||||||
|
example:
|
||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
@@ -67,20 +68,20 @@ Keyboard controls
|
|||||||
========================= ===========================
|
========================= ===========================
|
||||||
Action Shortcut
|
Action Shortcut
|
||||||
========================= ===========================
|
========================= ===========================
|
||||||
Quit :kbd:`q`, :kbd:`ctrl+c`, :kbd:`Esc`
|
Quit :kbd:`Q`, :kbd:`Ctrl+C`, :kbd:`Esc`
|
||||||
Scroll line up :kbd:`k`, :kbd:`Up`
|
Scroll line up :kbd:`K`, :kbd:`Up`
|
||||||
Scroll line down :kbd:`j`, :kbd:`Down`
|
Scroll line down :kbd:`J`, :kbd:`Down`
|
||||||
Scroll page up :kbd:`PgUp`
|
Scroll page up :kbd:`PgUp`
|
||||||
Scroll page down :kbd:`PgDn`
|
Scroll page down :kbd:`PgDn`
|
||||||
Scroll to top :kbd:`Home`
|
Scroll to top :kbd:`Home`
|
||||||
Scroll to bottom :kbd:`End`
|
Scroll to bottom :kbd:`End`
|
||||||
Scroll to next page :kbd:`Space`, :kbd:`PgDn`
|
Scroll to next page :kbd:`Space`, :kbd:`PgDn`
|
||||||
Scroll to previous page :kbd:`PgUp`
|
Scroll to previous page :kbd:`PgUp`
|
||||||
Scroll to next change :kbd:`n`
|
Scroll to next change :kbd:`N`
|
||||||
Scroll to previous change :kbd:`p`
|
Scroll to previous change :kbd:`P`
|
||||||
Increase lines of context :kbd:`+`
|
Increase lines of context :kbd:`+`
|
||||||
Decrease lines of context :kbd:`-`
|
Decrease lines of context :kbd:`-`
|
||||||
All lines of context :kbd:`a`
|
All lines of context :kbd:`A`
|
||||||
Restore default context :kbd:`=`
|
Restore default context :kbd:`=`
|
||||||
Search forwards :kbd:`/`
|
Search forwards :kbd:`/`
|
||||||
Search backwards :kbd:`?`
|
Search backwards :kbd:`?`
|
||||||
@@ -93,7 +94,7 @@ Scroll to previous match :kbd:`<`, :kbd:`,`
|
|||||||
Integrating with git
|
Integrating with git
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Add the following to `~/.gitconfig`:
|
Add the following to :file:`~/.gitconfig`:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
@@ -119,24 +120,23 @@ Why does this work only in kitty?
|
|||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
The diff kitten makes use of various features that are :doc:`kitty only
|
The diff kitten makes use of various features that are :doc:`kitty only
|
||||||
</protocol-extensions>`, such as the :doc:`kitty graphics protocol
|
</protocol-extensions>`, such as the :doc:`kitty graphics protocol
|
||||||
</graphics-protocol>`, the :doc:`extended keyboard protocol
|
</graphics-protocol>`, the :doc:`extended keyboard protocol
|
||||||
</keyboard-protocol>`, etc. It also leverages terminal program
|
</keyboard-protocol>`, etc. It also leverages terminal program infrastructure
|
||||||
infrastructure I created for all of kitty's other kittens to reduce the amount
|
I created for all of kitty's other kittens to reduce the amount of code needed
|
||||||
of code needed (the entire implementation is under 2000 lines of code).
|
(the entire implementation is under 2000 lines of code).
|
||||||
|
|
||||||
And fundamentally, it's kitty only because I wrote it for myself, and I am
|
And fundamentally, it's kitty only because I wrote it for myself, and I am
|
||||||
highly unlikely to use any other terminals :)
|
highly unlikely to use any other terminals :)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
You can configure the colors used, keyboard shortcuts, the diff implementation,
|
You can configure the colors used, keyboard shortcuts, the diff implementation,
|
||||||
the default lines of context, etc. by creating a :file:`diff.conf` file in
|
the default lines of context, etc. by creating a :file:`diff.conf` file in your
|
||||||
your :ref:`kitty config folder <confloc>`. See below for the supported
|
:ref:`kitty config folder <confloc>`. See below for the supported configuration
|
||||||
configuration directives.
|
directives.
|
||||||
|
|
||||||
|
|
||||||
.. include:: /generated/conf-kitten-diff.rst
|
.. include:: /generated/conf-kitten-diff.rst
|
||||||
@@ -145,7 +145,6 @@ configuration directives.
|
|||||||
.. include:: /generated/cli-kitten-diff.rst
|
.. include:: /generated/cli-kitten-diff.rst
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Sample diff.conf
|
Sample diff.conf
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
Hints
|
Hints
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|kitty| has a *hints mode* to select and act on arbitrary text snippets currently
|
|kitty| has a *hints mode* to select and act on arbitrary text snippets
|
||||||
visible on the screen. For example, you can press :sc:`open_url`
|
currently visible on the screen. For example, you can press :sc:`open_url`
|
||||||
to choose any URL visible on the screen and then open it using your system
|
to choose any URL visible on the screen and then open it using your default web
|
||||||
browser.
|
browser.
|
||||||
|
|
||||||
.. figure:: ../screenshots/hints_mode.png
|
.. figure:: ../screenshots/hints_mode.png
|
||||||
@@ -13,25 +13,29 @@ browser.
|
|||||||
|
|
||||||
URL hints mode
|
URL hints mode
|
||||||
|
|
||||||
Similarly, you can press :sc:`insert_selected_path` to
|
Similarly, you can press :sc:`insert_selected_path` to select anything that
|
||||||
select anything that looks like a path or filename and then insert it into the
|
looks like a path or filename and then insert it into the terminal, very useful
|
||||||
terminal, very useful for picking files from the output of a ``git`` or ``ls`` command and
|
for picking files from the output of a :program:`git` or :program:`ls` command
|
||||||
adding them to the command line for the next command.
|
and adding them to the command line for the next command.
|
||||||
|
|
||||||
You can also press :sc:`goto_file_line` to select anything that looks
|
You can also press :sc:`goto_file_line` to select anything that looks like a
|
||||||
like a path or filename followed by a colon and a line number and open
|
path or filename followed by a colon and a line number and open the file in
|
||||||
the file in vim at the specified line number. The patterns and editor
|
:program:`vim` at the specified line number. The patterns and editor to be used
|
||||||
to be used can be modified using options passed to the kitten. For example::
|
can be modified using options passed to the kitten. For example::
|
||||||
|
|
||||||
map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} {path}
|
map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} {path}
|
||||||
|
|
||||||
will open the selected file in a new tab inside neovim when you press
|
will open the selected file in a new tab inside `Neovim <https://neovim.io/>`__
|
||||||
:kbd:`ctrl+g`.
|
when you press :kbd:`Ctrl+G`.
|
||||||
|
|
||||||
Pressing :sc:`open_selected_hyperlink` will open hyperlinks, i.e. a URL
|
Pressing :sc:`open_selected_hyperlink` will open :term:`hyperlinks`, i.e. a URL
|
||||||
that has been marked as such by the program running in the terminal,
|
that has been marked as such by the program running in the terminal,
|
||||||
for example, by ``ls --hyperlink=auto``. You can also :doc:`customize what actions are
|
for example, by ``ls --hyperlink=auto``. If :program:`ls` comes with your OS
|
||||||
taken for different types of URLs <../open_actions>`.
|
does not support hyperlink, you may need to install `GNU Coreutils
|
||||||
|
<https://www.gnu.org/software/coreutils/>`__.
|
||||||
|
|
||||||
|
You can also :doc:`customize what actions are taken for different types of URLs
|
||||||
|
<../open_actions>`.
|
||||||
|
|
||||||
.. note:: If there are more hints than letters, hints will use multiple
|
.. note:: If there are more hints than letters, hints will use multiple
|
||||||
letters. In this case, when you press the first letter, only hints
|
letters. In this case, when you press the first letter, only hints
|
||||||
@@ -48,13 +52,12 @@ snippets. See :sc:`insert_selected_path <insert_selected_path>` for examples.
|
|||||||
Completely customizing the matching and actions of the kitten
|
Completely customizing the matching and actions of the kitten
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
|
|
||||||
The hints kitten supports writing simple python scripts that can be used to
|
The hints kitten supports writing simple Python scripts that can be used to
|
||||||
completely customize how it finds matches and what happens when a match is
|
completely customize how it finds matches and what happens when a match is
|
||||||
selected. This allows the hints kitten to provide the user interface, while
|
selected. This allows the hints kitten to provide the user interface, while you
|
||||||
you can provide the logic for finding matches and performing actions on them.
|
can provide the logic for finding matches and performing actions on them. This
|
||||||
This is best illustrated with an example. Create the file
|
is best illustrated with an example. Create the file :file:`custom-hints.py` in
|
||||||
:file:`custom-hints.py` in the kitty config directory with the following
|
the :ref:`kitty config directory <confloc>` with the following contents:
|
||||||
contents:
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@@ -98,9 +101,10 @@ look it up in the Google dictionary.
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
To avoid having to specify the same command line options on every invocation,
|
To avoid having to specify the same command line options on every
|
||||||
you can use the :opt:`action_alias` option in :file:`kitty.conf`, creating aliases
|
invocation, you can use the :opt:`action_alias` option in
|
||||||
that have common sets of options. For example::
|
:file:`kitty.conf`, creating aliases that have common sets of options.
|
||||||
|
For example::
|
||||||
|
|
||||||
action_alias myhints kitten hints --alphabet qfjdkslaureitywovmcxzpq1234567890
|
action_alias myhints kitten hints --alphabet qfjdkslaureitywovmcxzpq1234567890
|
||||||
map f1 myhints --customize-processing custom-hints.py
|
map f1 myhints --customize-processing custom-hints.py
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
Hyperlinked grep
|
Hyperlinked grep
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
|
||||||
This kitten allows you to search your files using `ripgrep
|
This kitten allows you to search your files using `ripgrep
|
||||||
<https://github.com/BurntSushi/ripgrep>`_ and open the results
|
<https://github.com/BurntSushi/ripgrep>`__ and open the results directly in your
|
||||||
directly in your favorite editor in the terminal, at the line containing
|
favorite editor in the terminal, at the line containing the search result,
|
||||||
the search result, simply by clicking on the result you want.
|
simply by clicking on the result you want.
|
||||||
|
|
||||||
.. versionadded:: 0.19.0
|
.. versionadded:: 0.19.0
|
||||||
|
|
||||||
@@ -25,19 +24,19 @@ following contents:
|
|||||||
mime text/*
|
mime text/*
|
||||||
action launch --type=overlay ${EDITOR} ${FILE_PATH}
|
action launch --type=overlay ${EDITOR} ${FILE_PATH}
|
||||||
|
|
||||||
|
|
||||||
Now, run a search with::
|
Now, run a search with::
|
||||||
|
|
||||||
kitty +kitten hyperlinked_grep something
|
kitty +kitten hyperlinked_grep something
|
||||||
|
|
||||||
Hold down the :kbd:`ctrl+shift` keys and click on any of the
|
Hold down the :kbd:`Ctrl+Shift` keys and click on any of the result lines, to
|
||||||
result lines, to open the file in vim at the matching line. If
|
open the file in :program:`vim` at the matching line. If you use some editor
|
||||||
you use some editor other than vim, you should adjust the
|
other than :program:`vim`, you should adjust the :file:`open-actions.conf` file
|
||||||
:file:`open-actions.conf` file accordingly.
|
accordingly.
|
||||||
|
|
||||||
Finally, add an alias to your shell's rc files to invoke the kitten as ``hg``::
|
Finally, add an alias to your shell's rc files to invoke the kitten as
|
||||||
|
:command:`hg`::
|
||||||
|
|
||||||
alias hg='kitty +kitten hyperlinked_grep'
|
alias hg="kitty +kitten hyperlinked_grep"
|
||||||
|
|
||||||
|
|
||||||
You can now run searches with::
|
You can now run searches with::
|
||||||
@@ -45,13 +44,13 @@ You can now run searches with::
|
|||||||
hg some-search-term
|
hg some-search-term
|
||||||
|
|
||||||
If you want to enable completion, for the kitten, you can delegate completion
|
If you want to enable completion, for the kitten, you can delegate completion
|
||||||
to rg. How to do that varies based on the shell:
|
to :program:`rg`. How to do that varies based on the shell:
|
||||||
|
|
||||||
|
|
||||||
.. tab:: zsh
|
.. tab:: zsh
|
||||||
|
|
||||||
Instead of using an alias create a simple wrapper script named
|
Instead of using an alias, create a simple wrapper script named
|
||||||
:file:`hg` somewhere in your ``PATH``:
|
:program:`hg` somewhere in your :envvar:`PATH`:
|
||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
@@ -64,22 +63,23 @@ to rg. How to do that varies based on the shell:
|
|||||||
|
|
||||||
.. tab:: fish
|
.. tab:: fish
|
||||||
|
|
||||||
You can combine both the aliasing/wrapping and pointing fish
|
You can combine both the aliasing/wrapping and pointing fish to ripgrep's
|
||||||
to rg's autocompletion with a fish "wrapper" function in your :file:`config.fish`:
|
autocompletion with a fish wrapper function in your :file:`config.fish`
|
||||||
|
or :file:`~/.config/fish/functions/hg.fish`:
|
||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: fish
|
||||||
|
|
||||||
function hg --wraps rg; kitty +kitten hyperlinked_grep $argv; end
|
function hg --wraps rg; kitty +kitten hyperlinked_grep $argv; end
|
||||||
|
|
||||||
To learn more about kitty's powerful framework for customizing URL click
|
To learn more about kitty's powerful framework for customizing URL click
|
||||||
actions, :doc:`see here </open_actions>`.
|
actions, see :doc:`here </open_actions>`.
|
||||||
|
|
||||||
Hopefully, someday this functionality will make it into some `upstream grep
|
Hopefully, someday this functionality will make it into some `upstream grep
|
||||||
<https://github.com/BurntSushi/ripgrep/issues/665>`_
|
<https://github.com/BurntSushi/ripgrep/issues/665>`__ program directly removing
|
||||||
program directly removing the need for this kitten.
|
the need for this kitten.
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
While you can pass any of ripgrep's comand line options to the kitten and
|
While you can pass any of ripgrep's comand line options to the kitten and
|
||||||
they will be forwarded to rg, do not use options that change the output
|
they will be forwarded to :program:`rg`, do not use options that change the
|
||||||
formatting as the kitten works by parsing the output from ripgrep.
|
output formatting as the kitten works by parsing the output from ripgrep.
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ terminal. Using it is as simple as::
|
|||||||
kitty +kitten icat image.jpeg
|
kitty +kitten icat image.jpeg
|
||||||
|
|
||||||
It supports all image types supported by `ImageMagick
|
It supports all image types supported by `ImageMagick
|
||||||
<https://www.imagemagick.org>`_. It even works over SSH. For details, see
|
<https://www.imagemagick.org>`__. It even works over SSH. For details, see the
|
||||||
the :doc:`kitty graphics protocol </graphics-protocol>`.
|
:doc:`kitty graphics protocol </graphics-protocol>`.
|
||||||
|
|
||||||
You might want to create an alias in your shell's configuration files::
|
You might want to create an alias in your shell's configuration files::
|
||||||
|
|
||||||
@@ -20,14 +20,14 @@ Then you can simply use ``icat image.png`` to view images.
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
`ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to
|
`ImageMagick <https://www.imagemagick.org>`__ must be installed for icat
|
||||||
work.
|
kitten to work.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
kitty's image display protocol may not work when used within a terminal
|
kitty's image display protocol may not work when used within a terminal
|
||||||
multiplexer such as ``screen`` or ``tmux``, depending on whether the
|
multiplexer such as :program:`screen` or :program:`tmux`, depending on
|
||||||
multiplexer has added support for it or not.
|
whether the multiplexer has added support for it or not.
|
||||||
|
|
||||||
|
|
||||||
.. program:: kitty +kitten icat
|
.. program:: kitty +kitten icat
|
||||||
@@ -35,18 +35,19 @@ Then you can simply use ``icat image.png`` to view images.
|
|||||||
|
|
||||||
The ``icat`` kitten has various command line arguments to allow it to be used
|
The ``icat`` kitten has various command line arguments to allow it to be used
|
||||||
from inside other programs to display images. In particular, :option:`--place`,
|
from inside other programs to display images. In particular, :option:`--place`,
|
||||||
:option:`--detect-support`, :option:`--silent` and :option:`--print-window-size`.
|
:option:`--detect-support`, :option:`--silent` and
|
||||||
|
:option:`--print-window-size`.
|
||||||
|
|
||||||
If you are trying to integrate icat into a complex program like a file
|
If you are trying to integrate icat into a complex program like a file manager
|
||||||
manager or editor, there are a few things to keep in mind. icat works by
|
or editor, there are a few things to keep in mind. icat works by communicating
|
||||||
communicating over the TTY device, it both writes to and reads from the TTY.
|
over the TTY device, it both writes to and reads from the TTY. So it is
|
||||||
So it is imperative that while it is running the host program does not do
|
imperative that while it is running the host program does not do any TTY I/O.
|
||||||
any TTY I/O. Any key presses or other input from the user on the TTY device
|
Any key presses or other input from the user on the TTY device will be
|
||||||
will be discarded. At a minimum, you should use the :option:`--silent` and
|
discarded. At a minimum, you should use the :option:`--silent` and
|
||||||
:option:`--transfer-mode` command line arguments. To be
|
:option:`--transfer-mode` command line arguments. To be really robust you should
|
||||||
really robust you should consider writing proper support for the
|
consider writing proper support for the :doc:`kitty graphics protocol
|
||||||
:doc:`../graphics-protocol` in the program instead. Nowadays there are many
|
</graphics-protocol>` in the program instead. Nowadays there are many libraries
|
||||||
libraries that have support for it.
|
that have support for it.
|
||||||
|
|
||||||
|
|
||||||
.. include:: /generated/cli-kitten-icat.rst
|
.. include:: /generated/cli-kitten-icat.rst
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ Draw a GPU accelerated dock panel on your desktop
|
|||||||
.. highlight:: sh
|
.. highlight:: sh
|
||||||
|
|
||||||
|
|
||||||
You can use this kitten to draw a GPU accelerated panel on the edge
|
You can use this kitten to draw a GPU accelerated panel on the edge of your
|
||||||
of your screen, that shows the output from an arbitrary terminal program.
|
screen, that shows the output from an arbitrary terminal program.
|
||||||
|
|
||||||
It is useful for showing status information or notifications on your desktop
|
It is useful for showing status information or notifications on your desktop
|
||||||
using terminal programs instead of GUI toolkits.
|
using terminal programs instead of GUI toolkits.
|
||||||
@@ -32,8 +32,8 @@ Using this kitten is simple, for example::
|
|||||||
kitty +kitten panel sh -c 'printf "\n\n\nHello, world."; sleep 5s'
|
kitty +kitten panel sh -c 'printf "\n\n\nHello, world."; sleep 5s'
|
||||||
|
|
||||||
This will show ``Hello, world.`` at the top edge of your screen for five
|
This will show ``Hello, world.`` at the top edge of your screen for five
|
||||||
seconds. Here the terminal program we are running is ``sh`` with a script to
|
seconds. Here the terminal program we are running is :program:`sh` with a script
|
||||||
print out ``Hello, world!``. You can make the terminal program as complex as
|
to print out ``Hello, world!``. You can make the terminal program as complex as
|
||||||
you like, as demonstrated in the screenshot above.
|
you like, as demonstrated in the screenshot above.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
Query terminal
|
Query terminal
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Used to query kitty from terminal programs about version, values of various
|
This kitten is used to query |kitty| from terminal programs about version, values
|
||||||
runtime options controlling its features, etc.
|
of various runtime options controlling its features, etc.
|
||||||
|
|
||||||
The querying is done using the (*semi*) standard XTGETTCAP escape sequence
|
The querying is done using the (*semi*) standard XTGETTCAP escape sequence
|
||||||
pioneered by XTerm, so it works over SSH as well. The downside is that it
|
pioneered by xterm, so it works over SSH as well. The downside is that it is
|
||||||
is slow, since it requires a roundtrip to the terminal emulator and back.
|
slow, since it requires a roundtrip to the terminal emulator and back.
|
||||||
|
|
||||||
If you want to do some of the same querying in your terminal program without
|
If you want to do some of the same querying in your terminal program without
|
||||||
depending on the kitten, you can do so, by processing the same escape codes.
|
depending on the kitten, you can do so, by processing the same escape codes.
|
||||||
Search `this page <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html>`_
|
Search `this page <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html>`__
|
||||||
for *XTGETTCAP* to see the syntax for the escape code and read the source
|
for *XTGETTCAP* to see the syntax for the escape code and read the source of
|
||||||
of this kitten to find the values of the keys for the various queries.
|
this kitten to find the values of the keys for the various queries.
|
||||||
|
|
||||||
|
|
||||||
.. include:: ../generated/cli-kitten-query_terminal.rst
|
.. include:: ../generated/cli-kitten-query_terminal.rst
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
Remote files
|
Remote files
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|kitty| has the ability to easily *Edit*, *Open* or *Download* files
|
|kitty| has the ability to easily *Edit*, *Open* or *Download* files from a
|
||||||
from a computer into which you are SSHed. In your SSH session run::
|
computer into which you are SSHed. In your SSH session run::
|
||||||
|
|
||||||
ls --hyperlink=auto
|
ls --hyperlink=auto
|
||||||
|
|
||||||
Then hold down :kbd:`ctrl+shift` and click the name of the file.
|
Then hold down :kbd:`Ctrl+Shift` and click the name of the file.
|
||||||
|
|
||||||
.. figure:: ../screenshots/remote_file.png
|
.. figure:: ../screenshots/remote_file.png
|
||||||
:alt: Remote file actions
|
:alt: Remote file actions
|
||||||
@@ -19,23 +19,28 @@ Then hold down :kbd:`ctrl+shift` and click the name of the file.
|
|||||||
to *Edit* it in which case kitty will download it and open it locally in your
|
to *Edit* it in which case kitty will download it and open it locally in your
|
||||||
:envvar:`EDITOR`. As you make changes to the file, they are automatically
|
:envvar:`EDITOR`. As you make changes to the file, they are automatically
|
||||||
transferred to the remote computer. Note that this happens without needing
|
transferred to the remote computer. Note that this happens without needing
|
||||||
to install *any* special software on the server, beyond ``ls`` that supports
|
to install *any* special software on the server, beyond :program:`ls` that
|
||||||
hyperlinks.
|
supports hyperlinks.
|
||||||
|
|
||||||
|
.. seealso:: See the :ref:`edit-in-kitty <edit_file>` command
|
||||||
|
|
||||||
.. seealso:: See the :doc:`transfer` kitten
|
.. seealso:: See the :doc:`transfer` kitten
|
||||||
|
|
||||||
.. versionadded:: 0.19.0
|
.. versionadded:: 0.19.0
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Nested SSH sessions are not supported. The kitten will always try to copy
|
For best results, use this kitten with the :doc:`ssh kitten <./ssh>`.
|
||||||
remote files from the first SSH host. This is because there is no way for
|
Otherwise, nested SSH sessions are not supported. The kitten will always try to copy
|
||||||
|
remote files from the first SSH host. This is because, without the ssh
|
||||||
|
kitten, there is no way for
|
||||||
|kitty| to detect and follow a nested SSH session robustly. Use the
|
|kitty| to detect and follow a nested SSH session robustly. Use the
|
||||||
:doc:`transfer` kitten for such situations.
|
:doc:`transfer` kitten for such situations.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If you have not setup automatic password-less SSH access, then, when
|
If you have not setup automatic password-less SSH access, and are not using
|
||||||
editing starts you will be asked to enter your password just once,
|
the ssh kitten, then, when editing
|
||||||
thereafter the SSH connection will be re-used.
|
starts you will be asked to enter your password just once, thereafter the SSH
|
||||||
|
connection will be re-used.
|
||||||
|
|
||||||
Similarly, you can choose to save the file to the local computer or download
|
Similarly, you can choose to save the file to the local computer or download
|
||||||
and open it in its default file handler.
|
and open it in its default file handler.
|
||||||
|
|||||||
@@ -15,28 +15,27 @@ Truly convenient SSH
|
|||||||
Automatic shell integration, file transfer and reuse of connections
|
Automatic shell integration, file transfer and reuse of connections
|
||||||
|
|
||||||
The ssh kitten allows you to login easily to remote hosts, and automatically
|
The ssh kitten allows you to login easily to remote hosts, and automatically
|
||||||
setup the environment there to be as comfortable as your local shell. You
|
setup the environment there to be as comfortable as your local shell. You can
|
||||||
can specify environment variables to set on the remote host and
|
specify environment variables to set on the remote host and files to copy there,
|
||||||
files to copy there, making your remote experience just like your
|
making your remote experience just like your local shell. Additionally, it
|
||||||
local shell. Additionally, it automatically sets up :ref:`shell_integration` on
|
automatically sets up :ref:`shell_integration` on the remote host and copies the
|
||||||
the remote host and copies the kitty terminfo database there.
|
kitty terminfo database there.
|
||||||
|
|
||||||
The ssh kitten is a thin wrapper around the traditional `ssh <https://man.openbsd.org/ssh>`__
|
The ssh kitten is a thin wrapper around the traditional `ssh <https://man.openbsd.org/ssh>`__
|
||||||
command line program and supports all the same options and arguments and configuration.
|
command line program and supports all the same options and arguments and configuration.
|
||||||
In interactive usage scenarios it is a drop in replacement for ``ssh``. To try it
|
In interactive usage scenarios it is a drop in replacement for :program:`ssh`.
|
||||||
out, simply run:
|
To try it out, simply run:
|
||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
kitty +kitten ssh some-hostname-to-connect-to
|
kitty +kitten ssh some-hostname-to-connect-to
|
||||||
|
|
||||||
You should end up at a shell prompt on the remote host, with shell
|
You should end up at a shell prompt on the remote host, with shell integration
|
||||||
integration enabled. If you like it you can add an alias to it in your shell's
|
enabled. If you like it you can add an alias to it in your shell's rc files:
|
||||||
rc files:
|
|
||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
alias s=kitty +kitten ssh
|
alias s="kitty +kitten ssh"
|
||||||
|
|
||||||
So now you can just type ``s hostname`` to connect.
|
So now you can just type ``s hostname`` to connect.
|
||||||
|
|
||||||
@@ -44,13 +43,12 @@ If you define a mapping in :file:`kitty.conf` such as::
|
|||||||
|
|
||||||
map f1 new_window_with_cwd
|
map f1 new_window_with_cwd
|
||||||
|
|
||||||
Then, pressing :kbd:`F1` will open a new window automatically logged
|
Then, pressing :kbd:`F1` will open a new window automatically logged into the
|
||||||
into the same host using the ssh kitten, at the same directory.
|
same host using the ssh kitten, at the same directory.
|
||||||
|
|
||||||
The ssh kitten can be configured using the :file:`~/.config/kitty/ssh.conf`
|
The ssh kitten can be configured using the :file:`~/.config/kitty/ssh.conf` file
|
||||||
file where you can specify environment variables to set on the remote host
|
where you can specify environment variables to set on the remote host and files
|
||||||
and files to copy from the local to the remote host. Let's see a
|
to copy from the local to the remote host. Let's see a quick example:
|
||||||
quick example:
|
|
||||||
|
|
||||||
.. code-block:: conf
|
.. code-block:: conf
|
||||||
|
|
||||||
@@ -80,8 +78,9 @@ Additionally, you can pass config options on the command line:
|
|||||||
|
|
||||||
The :code:`--kitten` argument can be specified multiple times, with directives
|
The :code:`--kitten` argument can be specified multiple times, with directives
|
||||||
from :file:`ssh.conf`. These are merged with :file:`ssh.conf` as if they were
|
from :file:`ssh.conf`. These are merged with :file:`ssh.conf` as if they were
|
||||||
appended to the end of that file. They apply only to the host being SSHed to
|
appended to the end of that file. They apply only to the host being SSHed to by
|
||||||
by this invocation, so any :opt:`hostname <kitten-ssh.hostname>` directives are ignored.
|
this invocation, so any :opt:`hostname <kitten-ssh.hostname>` directives are
|
||||||
|
ignored.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
@@ -98,8 +97,8 @@ A real world example
|
|||||||
Suppose you often SSH into a production server, and you would like to setup
|
Suppose you often SSH into a production server, and you would like to setup
|
||||||
your shell and editor there using your custom settings. However, other people
|
your shell and editor there using your custom settings. However, other people
|
||||||
could SSH in as well and you don't want to clobber their settings. Here is how
|
could SSH in as well and you don't want to clobber their settings. Here is how
|
||||||
this could be achieved using the ssh kitten with zsh and vim as the shell and
|
this could be achieved using the ssh kitten with :program:`zsh` and
|
||||||
editor, respectively:
|
:program:`vim` as the shell and editor, respectively:
|
||||||
|
|
||||||
.. code-block:: conf
|
.. code-block:: conf
|
||||||
|
|
||||||
@@ -125,23 +124,24 @@ How it works
|
|||||||
The ssh kitten works by having SSH transmit and execute a POSIX sh (or
|
The ssh kitten works by having SSH transmit and execute a POSIX sh (or
|
||||||
:opt:`optionally <kitten-ssh.interpreter>` Python) bootstrap script on the
|
:opt:`optionally <kitten-ssh.interpreter>` Python) bootstrap script on the
|
||||||
remote host using an :opt:`interpreter <kitten-ssh.interpreter>`. This script
|
remote host using an :opt:`interpreter <kitten-ssh.interpreter>`. This script
|
||||||
reads setup data over the tty device, which kitty sends as a base64 encoded
|
reads setup data over the TTY device, which kitty sends as a Base64 encoded
|
||||||
compressed tarball. The script extracts it and places the :opt:`files <kitten-ssh.copy>`
|
compressed tarball. The script extracts it and places the :opt:`files <kitten-ssh.copy>`
|
||||||
and sets the :opt:`environment variables <kitten-ssh.env>` before finally
|
and sets the :opt:`environment variables <kitten-ssh.env>` before finally
|
||||||
launching the :opt:`login shell <kitten-ssh.login_shell>` with :opt:`shell
|
launching the :opt:`login shell <kitten-ssh.login_shell>` with :opt:`shell
|
||||||
integration <kitten-ssh.shell_integration>` enabled. The data is requested by
|
integration <kitten-ssh.shell_integration>` enabled. The data is requested by
|
||||||
the kitten over the TTY with a random one time password. kitty reads the request
|
the kitten over the TTY with a random one time password. kitty reads the request
|
||||||
and if the password matches a password pre-stored in shared memory on the
|
and if the password matches a password pre-stored in shared memory on the
|
||||||
localhost by the kitten, the transmission is allowed. If your OpenSSH version is
|
localhost by the kitten, the transmission is allowed. If your local
|
||||||
>= 8.4 then the data is transmitted instantly without any roundtrip delay.
|
`OpenSSH <https://www.openssh.com/>`__ version is >= 8.4 then the data is
|
||||||
|
transmitted instantly without any roundtrip delay.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
When connecting to BSD hosts, it is possible the bootstrap script will
|
When connecting to BSD hosts, it is possible the bootstrap script will fail
|
||||||
fail or run slowly, because the default shells are crippled in various ways.
|
or run slowly, because the default shells are crippled in various ways.
|
||||||
Your best bet is to install Python on the remote, make sure the login shell
|
Your best bet is to install Python on the remote, make sure the login shell
|
||||||
is something POSIX sh compliant, and use :code:`python` as the :opt:`interpreter
|
is something POSIX sh compliant, and use :code:`python` as the
|
||||||
<kitten-ssh.interpreter>` in :file:`ssh.conf`.
|
:opt:`interpreter <kitten-ssh.interpreter>` in :file:`ssh.conf`.
|
||||||
|
|
||||||
.. include:: /generated/conf-kitten-ssh.rst
|
.. include:: /generated/conf-kitten-ssh.rst
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
Changing kitty colors
|
Changing kitty colors
|
||||||
========================
|
========================
|
||||||
|
|
||||||
The themes kitten allows you to easily change color themes, from a collection
|
The themes kitten allows you to easily change color themes, from a collection of
|
||||||
of almost two hundred pre-built themes available at `kitty-themes
|
over two hundred pre-built themes available at `kitty-themes
|
||||||
<https://github.com/kovidgoyal/kitty-themes>`_. To use it, simply run::
|
<https://github.com/kovidgoyal/kitty-themes>`_. To use it, simply run::
|
||||||
|
|
||||||
kitty +kitten themes
|
kitty +kitten themes
|
||||||
@@ -12,9 +12,9 @@ of almost two hundred pre-built themes available at `kitty-themes
|
|||||||
:alt: The themes kitten in action
|
:alt: The themes kitten in action
|
||||||
:width: 600
|
:width: 600
|
||||||
|
|
||||||
The kitten allows you to pick a theme, with live previews of the colors. You
|
The kitten allows you to pick a theme, with live previews of the colors. You can
|
||||||
can choose between light and dark themes and search by theme name by just
|
choose between light and dark themes and search by theme name by just typing a
|
||||||
typing a few characters from the name.
|
few characters from the name.
|
||||||
|
|
||||||
The kitten maintains a list of recently used themes to allow quick switching.
|
The kitten maintains a list of recently used themes to allow quick switching.
|
||||||
|
|
||||||
@@ -24,14 +24,15 @@ If you want to restore the colors to default, you can do so by choosing the
|
|||||||
.. versionadded:: 0.23.0
|
.. versionadded:: 0.23.0
|
||||||
The themes kitten
|
The themes kitten
|
||||||
|
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
A theme in kitty is just a :file:`.conf` file containing kitty settings.
|
A theme in kitty is just a :file:`.conf` file containing kitty settings.
|
||||||
When you select a theme, the kitten simply copies the :file:`.conf` file
|
When you select a theme, the kitten simply copies the :file:`.conf` file
|
||||||
to :file:`~/.config/kitty/current-theme.conf` and adds an include for
|
to :file:`~/.config/kitty/current-theme.conf` and adds an include for
|
||||||
:file:`current-theme.conf` to :file:`kitty.conf`. It also comments out
|
:file:`current-theme.conf` to :file:`kitty.conf`. It also comments out any
|
||||||
any existing color settings in :file:`kitty.conf` so they do not interfere.
|
existing color settings in :file:`kitty.conf` so they do not interfere.
|
||||||
|
|
||||||
Once that's done, the kitten sends kitty a signal to make it reload its config.
|
Once that's done, the kitten sends kitty a signal to make it reload its config.
|
||||||
|
|
||||||
@@ -39,9 +40,9 @@ Using your own themes
|
|||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
You can also create your own themes as :file:`.conf` files. Put them in the
|
You can also create your own themes as :file:`.conf` files. Put them in the
|
||||||
:file:`themes` sub-directory of the kitty config directory, usually,
|
:file:`themes` sub-directory of the :ref:`kitty config directory <confloc>`,
|
||||||
:file:`~/.config/kitty/themes` and the kitten will automatically add them to
|
usually, :file:`~/.config/kitty/themes`. The kitten will automatically add them
|
||||||
the list of themes. You can use this to modify the builtin themes, by giving
|
to the list of themes. You can use this to modify the builtin themes, by giving
|
||||||
the conf file the name :file:`Some theme name.conf` to override the builtin
|
the conf file the name :file:`Some theme name.conf` to override the builtin
|
||||||
theme of that name. Note that after doing so you have to run the kitten and
|
theme of that name. Note that after doing so you have to run the kitten and
|
||||||
choose that theme once for your changes to be applied.
|
choose that theme once for your changes to be applied.
|
||||||
@@ -52,13 +53,13 @@ Contributing new themes
|
|||||||
|
|
||||||
If you wish to contribute a new theme to the kitty theme repository, start by
|
If you wish to contribute a new theme to the kitty theme repository, start by
|
||||||
going to the `kitty-themes <https://github.com/kovidgoyal/kitty-themes>`__
|
going to the `kitty-themes <https://github.com/kovidgoyal/kitty-themes>`__
|
||||||
repository. `Fork it
|
repository. `Fork it
|
||||||
<https://docs.github.com/en/get-started/quickstart/fork-a-repo>`_, and use the
|
<https://docs.github.com/en/get-started/quickstart/fork-a-repo>`__, and use the
|
||||||
file :download:`template.conf
|
file :download:`template.conf
|
||||||
<https://github.com/kovidgoyal/kitty-themes/raw/master/template.conf>` as a
|
<https://github.com/kovidgoyal/kitty-themes/raw/master/template.conf>` as a
|
||||||
template when creating your theme. Once you are satisfied with how it looks,
|
template when creating your theme. Once you are satisfied with how it looks,
|
||||||
`submit a pull request
|
`submit a pull request
|
||||||
<https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request>`_
|
<https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request>`__
|
||||||
to have your theme merged into the `kitty-themes
|
to have your theme merged into the `kitty-themes
|
||||||
<https://github.com/kovidgoyal/kitty-themes>`__ repository, which will make it
|
<https://github.com/kovidgoyal/kitty-themes>`__ repository, which will make it
|
||||||
available in this kitten automatically.
|
available in this kitten automatically.
|
||||||
@@ -67,12 +68,12 @@ available in this kitten automatically.
|
|||||||
Changing the theme non-interactively
|
Changing the theme non-interactively
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
You can specify the theme name as an argument when invoking the kitten
|
You can specify the theme name as an argument when invoking the kitten to have
|
||||||
to have it change to that theme instantly. For example::
|
it change to that theme instantly. For example::
|
||||||
|
|
||||||
kitty +kitten themes --reload-in=all Dimmed Monokai
|
kitty +kitten themes --reload-in=all Dimmed Monokai
|
||||||
|
|
||||||
Will change the theme to ``Dimmed Monokai`` in all running kitty
|
Will change the theme to ``Dimmed Monokai`` in all running kitty instances. See
|
||||||
instances. See below for more details on non-interactive operation.
|
below for more details on non-interactive operation.
|
||||||
|
|
||||||
.. include:: ../generated/cli-kitten-themes.rst
|
.. include:: ../generated/cli-kitten-themes.rst
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ etc. Anywhere you have a terminal device, you can transfer files.
|
|||||||
:alt: The transfer kitten at work
|
:alt: The transfer kitten at work
|
||||||
|
|
||||||
This kitten supports transferring entire directory trees, preserving soft and
|
This kitten supports transferring entire directory trees, preserving soft and
|
||||||
hard links, file permissions, times, etc. It even supports the rsync_
|
hard links, file permissions, times, etc. It even supports the rsync_ protocol
|
||||||
protocol to transfer only changes to large files.
|
to transfer only changes to large files.
|
||||||
|
|
||||||
.. seealso:: See the :doc:`remote_file` kitten
|
.. seealso:: See the :doc:`remote_file` kitten
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
This kitten (which practically means kitty) must be installed on the other
|
This kitten (which practically means kitty) must be installed on the other
|
||||||
machine as well. If that is not possible you can use the :doc:`remote_file`
|
machine as well. If that is not possible you can use the :doc:`remote_file`
|
||||||
kitten instead. Or write your own script to use the underlying :doc:`file transfer
|
kitten instead. Or write your own script to use the underlying
|
||||||
protocol </file-transfer-protocol>`.
|
:doc:`file transfer protocol </file-transfer-protocol>`.
|
||||||
|
|
||||||
.. versionadded:: 0.24.0
|
.. versionadded:: 0.24.0
|
||||||
|
|
||||||
@@ -32,7 +32,8 @@ Basic usage
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
In what follows, the *local computer* is the computer running this kitten and
|
In what follows, the *local computer* is the computer running this kitten and
|
||||||
the *remote computer* is the computer connected to the other end of the TTY pipe.
|
the *remote computer* is the computer connected to the other end of the TTY
|
||||||
|
pipe.
|
||||||
|
|
||||||
To send a file from the local computer to the remote computer, simply run::
|
To send a file from the local computer to the remote computer, simply run::
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ You will be prompted by kitty for confirmation on allowing the transfer, and if
|
|||||||
you grant permission, the file will be copied.
|
you grant permission, the file will be copied.
|
||||||
|
|
||||||
Similarly, to get a file from the remote computer to the local computer, use
|
Similarly, to get a file from the remote computer to the local computer, use
|
||||||
the :option:`kitty +kitten transfer --direction` option::
|
the :option:`--direction <kitty +kitten transfer --direction>` option::
|
||||||
|
|
||||||
kitty +kitten transfer --direction=receive /path/to/remote/file /path/to/destination/on/local/computer
|
kitty +kitten transfer --direction=receive /path/to/remote/file /path/to/destination/on/local/computer
|
||||||
|
|
||||||
@@ -58,25 +59,25 @@ the fact that you are copying multiple things) it is good practice to always
|
|||||||
use a trailing slash when the destination is supposed to be a directory.
|
use a trailing slash when the destination is supposed to be a directory.
|
||||||
|
|
||||||
Also, when transferring multiple files/directories it is a good idea to
|
Also, when transferring multiple files/directories it is a good idea to
|
||||||
use the :option:`kitty +kitten transfer --confirm-paths` option which will give
|
use the :option:`--confirm-paths <kitty +kitten transfer --confirm-paths>`
|
||||||
you an opportunity to review and confirm the files that will be touched.
|
option which will give you an opportunity to review and confirm the files that
|
||||||
|
will be touched.
|
||||||
|
|
||||||
|
|
||||||
Avoiding the confirmation prompt
|
Avoiding the confirmation prompt
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
Normally, when you start a file transfer kitty will prompt you for
|
Normally, when you start a file transfer kitty will prompt you for confirmation.
|
||||||
confirmation. This is to ensure that hostile programs running on a remote
|
This is to ensure that hostile programs running on a remote machine cannot
|
||||||
machine cannot read/write files on your computer without your permission.
|
read/write files on your computer without your permission. If the remote machine
|
||||||
If the remote machine is trusted and the connection between your computer
|
is trusted and the connection between your computer and the remote machine is
|
||||||
and the remote machine is secure, then you can disable the confirmation prompt
|
secure, then you can disable the confirmation prompt by:
|
||||||
by:
|
|
||||||
|
|
||||||
#. Setting the :opt:`file_transfer_confirmation_bypass` option to some
|
#. Setting the :opt:`file_transfer_confirmation_bypass` option to some password.
|
||||||
password.
|
|
||||||
|
|
||||||
#. When invoking the kitten use the :option:`kitty +kitten transfer --permissions-bypass`
|
#. When invoking the kitten use the :option:`--permissions-bypass
|
||||||
to supply the password you set in step one.
|
<kitty +kitten transfer --permissions-bypass>` to supply the password you set
|
||||||
|
in step one.
|
||||||
|
|
||||||
.. warning:: Using a password to bypass confirmation means any software running
|
.. warning:: Using a password to bypass confirmation means any software running
|
||||||
on the remote machine could potentially learn that password and use it to
|
on the remote machine could potentially learn that password and use it to
|
||||||
@@ -89,9 +90,10 @@ Delta transfers
|
|||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
This kitten has the ability to use the rsync_ protocol to only transfer the
|
This kitten has the ability to use the rsync_ protocol to only transfer the
|
||||||
differences between files. To turn it on use the :option:`kitty +kitten
|
differences between files. To turn it on use the :option:`--transmit-deltas
|
||||||
transfer --transmit-deltas` option. Note that this will actually be slower when
|
<kitty +kitten transfer --transmit-deltas>` option. Note that this will actually
|
||||||
transferring small files because of round trip overhead, so use with care.
|
be slower when transferring small files because of round trip overhead, so use
|
||||||
|
with care.
|
||||||
|
|
||||||
|
|
||||||
.. include:: ../generated/cli-kitten-transfer.rst
|
.. include:: ../generated/cli-kitten-transfer.rst
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
Unicode input
|
Unicode input
|
||||||
================
|
================
|
||||||
|
|
||||||
You can input unicode characters by name, hex code, recently used and even an editable favorites list.
|
You can input Unicode characters by name, hex code, recently used and even an
|
||||||
Press :sc:`input_unicode_character` to start the unicode input widget, shown below.
|
editable favorites list. Press :sc:`input_unicode_character` to start the
|
||||||
|
unicode input kitten, shown below.
|
||||||
|
|
||||||
.. figure:: ../screenshots/unicode.png
|
.. figure:: ../screenshots/unicode.png
|
||||||
:alt: A screenshot of the unicode input widget
|
:alt: A screenshot of the unicode input kitten
|
||||||
:align: center
|
:align: center
|
||||||
:width: 100%
|
:width: 100%
|
||||||
|
|
||||||
A screenshot of the unicode input widget
|
A screenshot of the unicode input kitten
|
||||||
|
|
||||||
In :guilabel:`Code` mode, you enter a unicode character by typing in the hex code for the
|
In :guilabel:`Code` mode, you enter a Unicode character by typing in the hex
|
||||||
character and pressing enter, for example, type in ``2716`` and press enter to get
|
code for the character and pressing :kbd:`Enter`. For example, type in ``2716``
|
||||||
✖. You can also choose a character from the list of recently used characters by
|
and press :kbd:`Enter` to get ``✖``. You can also choose a character from the
|
||||||
typing a leading period and then the two character index and pressing Enter.
|
list of recently used characters by typing a leading period ``.`` and then the
|
||||||
The up and down arrow keys can be used to choose the previous and next unicode
|
two character index and pressing :kbd:`Enter`.
|
||||||
symbol respectively.
|
The :kbd:`Up` and :kbd:`Down` arrow keys can be used to choose the previous and
|
||||||
|
next Unicode symbol respectively.
|
||||||
|
|
||||||
In :guilabel:`Name` mode you instead type words from the character name and use
|
In :guilabel:`Name` mode you instead type words from the character name and use
|
||||||
the arrow keys/tab to select the character from the displayed matches. You can
|
the :kbd:`ArrowKeys` / :kbd:`Tab` to select the character from the displayed
|
||||||
also type a space followed by a period and the index for the match if you don't
|
matches. You can also type a space followed by a period and the index for the
|
||||||
like to use arrow keys.
|
match if you don't like to use arrow keys.
|
||||||
|
|
||||||
You can switch between modes using either the keys :kbd:`F1` ... :kbd:`F4` or
|
You can switch between modes using either the keys :kbd:`F1` ... :kbd:`F4` or
|
||||||
:kbd:`Ctrl+1` ... :kbd:`Ctrl+4` or by pressing :kbd:`Ctrl+[` and :kbd:`Ctrl+]`
|
:kbd:`Ctrl+1` ... :kbd:`Ctrl+4` or by pressing :kbd:`Ctrl+[` and :kbd:`Ctrl+]`
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ Extend with kittens
|
|||||||
kittens/*
|
kittens/*
|
||||||
|
|
||||||
|kitty| has a framework for easily creating terminal programs that make use of
|
|kitty| has a framework for easily creating terminal programs that make use of
|
||||||
its advanced features. These programs are called kittens. They are used both
|
its advanced features. These programs are called kittens. They are used both to
|
||||||
to add features to |kitty| itself and to create useful standalone programs.
|
add features to |kitty| itself and to create useful standalone programs.
|
||||||
Some prominent kittens:
|
Some prominent kittens:
|
||||||
|
|
||||||
:doc:`icat <kittens/icat>`
|
:doc:`icat <kittens/icat>`
|
||||||
@@ -33,8 +33,8 @@ Some prominent kittens:
|
|||||||
images
|
images
|
||||||
|
|
||||||
|
|
||||||
:doc:`Unicode Input <kittens/unicode_input>`
|
:doc:`Unicode input <kittens/unicode_input>`
|
||||||
Easily input arbitrary unicode characters in |kitty| by name or hex code.
|
Easily input arbitrary Unicode characters in |kitty| by name or hex code.
|
||||||
|
|
||||||
|
|
||||||
:doc:`Hints <kittens/hints>`
|
:doc:`Hints <kittens/hints>`
|
||||||
@@ -48,29 +48,31 @@ Some prominent kittens:
|
|||||||
|
|
||||||
|
|
||||||
:doc:`Transfer files <kittens/transfer>`
|
:doc:`Transfer files <kittens/transfer>`
|
||||||
Transfer files and directories seamlessly and easily from remote machines over your existing
|
Transfer files and directories seamlessly and easily from remote machines
|
||||||
SSH sessions with a simple command.
|
over your existing SSH sessions with a simple command.
|
||||||
|
|
||||||
|
|
||||||
:doc:`Hyperlinked grep <kittens/hyperlinked_grep>`
|
:doc:`Hyperlinked grep <kittens/hyperlinked_grep>`
|
||||||
Search your files using `ripgrep <https://github.com/BurntSushi/ripgrep>`_
|
Search your files using `ripgrep <https://github.com/BurntSushi/ripgrep>`__
|
||||||
and open the results directly in your favorite editor in the terminal,
|
and open the results directly in your favorite editor in the terminal,
|
||||||
at the line containing the search result, simply by clicking on the result you want.
|
at the line containing the search result, simply by clicking on the result
|
||||||
|
you want.
|
||||||
|
|
||||||
|
|
||||||
:doc:`Broadcast <kittens/broadcast>`
|
:doc:`Broadcast <kittens/broadcast>`
|
||||||
Type in one :term:`kitty window <window>` and have it broadcast to all (or a subset) of
|
Type in one :term:`kitty window <window>` and have it broadcast to all (or a
|
||||||
other :term:`kitty windows <window>`.
|
subset) of other :term:`kitty windows <window>`.
|
||||||
|
|
||||||
|
|
||||||
:doc:`SSH <kittens/ssh>`
|
:doc:`SSH <kittens/ssh>`
|
||||||
SSH with automatic shell integration, connection re-use for low latency and easy cloning
|
SSH with automatic :ref:`shell integration <shell_integration>`, connection
|
||||||
of local shell and editor configuration to the remote machine.
|
re-use for low latency and easy cloning of local shell and editor
|
||||||
|
configuration to the remote host.
|
||||||
|
|
||||||
|
|
||||||
:doc:`Panel <kittens/panel>`
|
:doc:`Panel <kittens/panel>`
|
||||||
Draw a GPU accelerated dock panel on your desktop showing the output
|
Draw a GPU accelerated dock panel on your desktop showing the output from an
|
||||||
from an arbitrary terminal program.
|
arbitrary terminal program.
|
||||||
|
|
||||||
|
|
||||||
:doc:`Clipboard <kittens/clipboard>`
|
:doc:`Clipboard <kittens/clipboard>`
|
||||||
|
|||||||
@@ -5,22 +5,21 @@ The :command:`launch` command
|
|||||||
|
|
||||||
|
|
||||||
|kitty| has a :code:`launch` action that can be used to run arbitrary programs
|
|kitty| has a :code:`launch` action that can be used to run arbitrary programs
|
||||||
in new windows/tabs. It can be mapped to user defined shortcuts in kitty.conf.
|
in new windows/tabs. It can be mapped to user defined shortcuts in
|
||||||
It is very powerful and allows sending the contents of
|
:file:`kitty.conf`. It is very powerful and allows sending the contents of the
|
||||||
the current window to the launched program, as well as many other options.
|
current window to the launched program, as well as many other options.
|
||||||
|
|
||||||
In the simplest form, you can use it to open a new kitty window running the
|
In the simplest form, you can use it to open a new kitty window running the
|
||||||
shell, as shown below::
|
shell, as shown below::
|
||||||
|
|
||||||
map f1 launch
|
map f1 launch
|
||||||
|
|
||||||
To run a different program simply pass the command line as arguments to
|
To run a different program simply pass the command line as arguments to launch::
|
||||||
launch::
|
|
||||||
|
|
||||||
map f1 launch vim path/to/some/file
|
map f1 launch vim path/to/some/file
|
||||||
|
|
||||||
To open a new window with the same working directory as the currently
|
To open a new window with the same working directory as the currently active
|
||||||
active window::
|
window::
|
||||||
|
|
||||||
map f1 launch --cwd=current
|
map f1 launch --cwd=current
|
||||||
|
|
||||||
@@ -30,9 +29,10 @@ To open the new window in a new tab::
|
|||||||
|
|
||||||
To run multiple commands in a shell, use::
|
To run multiple commands in a shell, use::
|
||||||
|
|
||||||
map f1 launch sh -c "ls && zsh"
|
map f1 launch sh -c "ls && exec zsh"
|
||||||
|
|
||||||
To pass the contents of the current screen and scrollback to the started process::
|
To pass the contents of the current screen and scrollback to the started
|
||||||
|
process::
|
||||||
|
|
||||||
map f1 launch --stdin-source=@screen_scrollback less
|
map f1 launch --stdin-source=@screen_scrollback less
|
||||||
|
|
||||||
@@ -46,16 +46,16 @@ There are many more powerful options, refer to the complete list below.
|
|||||||
map f1 launch_tab vim
|
map f1 launch_tab vim
|
||||||
map f2 launch_tab emacs
|
map f2 launch_tab emacs
|
||||||
|
|
||||||
The :kbd:`F1` key will now open vim in a new tab with the current windows
|
The :kbd:`F1` key will now open :program:`vim` in a new tab with the current
|
||||||
working directory
|
windows working directory.
|
||||||
|
|
||||||
|
|
||||||
The piping environment
|
The piping environment
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
When using :option:`launch --stdin-source`, the program to which the data is
|
When using :option:`launch --stdin-source`, the program to which the data is
|
||||||
piped has a special environment variable declared, :envvar:`KITTY_PIPE_DATA` whose
|
piped has a special environment variable declared, :envvar:`KITTY_PIPE_DATA`
|
||||||
contents are::
|
whose contents are::
|
||||||
|
|
||||||
KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}
|
KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}
|
||||||
|
|
||||||
@@ -73,33 +73,33 @@ the command line:
|
|||||||
|
|
||||||
|
|
||||||
``@selection``
|
``@selection``
|
||||||
replaced by the currently selected text
|
Replaced by the currently selected text.
|
||||||
|
|
||||||
``@active-kitty-window-id``
|
``@active-kitty-window-id``
|
||||||
replaced by the id of the currently active kitty window
|
Replaced by the id of the currently active kitty window.
|
||||||
|
|
||||||
``@line-count``
|
``@line-count``
|
||||||
replaced by the number of lines in STDIN. Only present when passing some
|
Replaced by the number of lines in STDIN. Only present when passing some
|
||||||
data to STDIN
|
data to STDIN.
|
||||||
|
|
||||||
``@input-line-number``
|
``@input-line-number``
|
||||||
replaced the number of lines a pager should scroll to match the current
|
Replaced the number of lines a pager should scroll to match the current
|
||||||
scroll position in kitty. See :opt:`scrollback_pager` for details
|
scroll position in kitty. See :opt:`scrollback_pager` for details.
|
||||||
|
|
||||||
``@scrolled-by``
|
``@scrolled-by``
|
||||||
replaced by the number of lines kitty is currently scrolled by
|
Replaced by the number of lines kitty is currently scrolled by.
|
||||||
|
|
||||||
``@cursor-x``
|
``@cursor-x``
|
||||||
replaced by the current cursor x position with 1 being the leftmost cell
|
Replaced by the current cursor x position with 1 being the leftmost cell.
|
||||||
|
|
||||||
``@cursor-y``
|
``@cursor-y``
|
||||||
replaced by the current cursor y position with 1 being the topmost cell
|
Replaced by the current cursor y position with 1 being the topmost cell.
|
||||||
|
|
||||||
``@first-line-on-screen``
|
``@first-line-on-screen``
|
||||||
replaced by the first line on screen. Can be used for pager positioning.
|
Replaced by the first line on screen. Can be used for pager positioning.
|
||||||
|
|
||||||
``@last-line-on-screen``
|
``@last-line-on-screen``
|
||||||
replaced by the last line on screen. Can be used for pager positioning.
|
Replaced by the last line on screen. Can be used for pager positioning.
|
||||||
|
|
||||||
|
|
||||||
For example::
|
For example::
|
||||||
@@ -112,44 +112,51 @@ For example::
|
|||||||
Watching launched windows
|
Watching launched windows
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
The :option:`launch --watcher` option allows you to specify python functions
|
The :option:`launch --watcher` option allows you to specify Python functions
|
||||||
that will be called at specific events, such as when the window is resized or
|
that will be called at specific events, such as when the window is resized or
|
||||||
closed. Simply specify the path to a python module that specifies callback
|
closed. Simply specify the path to a Python module that specifies callback
|
||||||
functions for the events you are interested in, for example:
|
functions for the events you are interested in, for example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def on_resize(boss, window, data):
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from kitty.boss import Boss
|
||||||
|
from kitty.window import Window
|
||||||
|
|
||||||
|
|
||||||
|
def on_resize(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
|
||||||
# Here data will contain old_geometry and new_geometry
|
# Here data will contain old_geometry and new_geometry
|
||||||
|
|
||||||
def on_focus_change(boss, window, data):
|
def on_focus_change(boss: Boss, window: Window, data: Dict[str, Any])-> None:
|
||||||
# Here data will contain focused
|
# Here data will contain focused
|
||||||
|
|
||||||
def on_close(boss, window, data):
|
def on_close(boss: Boss, window: Window, data: Dict[str, Any])-> None:
|
||||||
# called when window is closed, typically when the program running in
|
# called when window is closed, typically when the program running in
|
||||||
# it exits.
|
# it exits.
|
||||||
|
|
||||||
|
|
||||||
Every callback is passed a reference to the global ``Boss`` object as well as
|
Every callback is passed a reference to the global ``Boss`` object as well as
|
||||||
the ``Window`` object the action is occurring on. The ``data`` object is
|
the ``Window`` object the action is occurring on. The ``data`` object is a dict
|
||||||
a dict that contains event dependent data. Some useful methods and attributes
|
that contains event dependent data. Some useful methods and attributes for the
|
||||||
for the ``Window`` object are: ``as_text(as_ans=False, add_history=False,
|
``Window`` object are: ``as_text(as_ans=False, add_history=False,
|
||||||
add_wrap_markers=False, alternate_screen=False)`` with which you can get the
|
add_wrap_markers=False, alternate_screen=False)`` with which you can get the
|
||||||
contents of the window and its scrollback buffer. Similarly,
|
contents of the window and its scrollback buffer. Similarly,
|
||||||
``window.child.pid`` is the PID of the processes that was launched
|
``window.child.pid`` is the PID of the processes that was launched
|
||||||
in the window and ``window.id`` is the internal kitty ``id`` of the
|
in the window and ``window.id`` is the internal kitty ``id`` of the window.
|
||||||
window.
|
|
||||||
|
|
||||||
|
|
||||||
Finding executables
|
Finding executables
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
When you specify a command to run as just a name rather than an absolute path,
|
When you specify a command to run as just a name rather than an absolute path,
|
||||||
it is searched for in the system-wide ``PATH`` environment variable. Note that
|
it is searched for in the system-wide :envvar:`PATH` environment variable. Note
|
||||||
this **may not** be the value of ``PATH`` inside a shell, as shell startup scripts
|
that this **may not** be the value of :envvar:`PATH` inside a shell, as shell
|
||||||
often change the value of this variable. If it is not found there, then a
|
startup scripts often change the value of this variable. If it is not found
|
||||||
system specific list of default paths is searched. If it is still not found,
|
there, then a system specific list of default paths is searched. If it is still
|
||||||
then your shell is run and the value of ``PATH`` inside the shell is used.
|
not found, then your shell is run and the value of :envvar:`PATH` inside the
|
||||||
|
shell is used.
|
||||||
|
|
||||||
See :opt:`exe_search_path` for details and how to control this.
|
See :opt:`exe_search_path` for details and how to control this.
|
||||||
|
|
||||||
Syntax reference
|
Syntax reference
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ other in arbitrary arrangements, based on *Layouts*, see below for examples:
|
|||||||
:align: center
|
:align: center
|
||||||
:width: 100%
|
:width: 100%
|
||||||
|
|
||||||
Screenshot, showing vim, tig and git running in |kitty| with the 'Tall' layout
|
Screenshot, showing :program:`vim`, :program:`tig` and :program:`git`
|
||||||
|
running in |kitty| with the *Tall* layout
|
||||||
|
|
||||||
|
|
||||||
.. figure:: screenshots/splits.png
|
.. figure:: screenshots/splits.png
|
||||||
@@ -18,21 +19,22 @@ other in arbitrary arrangements, based on *Layouts*, see below for examples:
|
|||||||
:align: center
|
:align: center
|
||||||
:width: 100%
|
:width: 100%
|
||||||
|
|
||||||
Screenshot, showing windows with arbitrary arrangement in the 'Splits'
|
Screenshot, showing windows with arbitrary arrangement in the *Splits*
|
||||||
layout
|
layout
|
||||||
|
|
||||||
|
|
||||||
There are many different layouts available. They are all enabled by default,
|
There are many different layouts available. They are all enabled by default, you
|
||||||
you can switch layouts using :sc:`next_layout`. To control which layouts
|
can switch layouts using :ac:`next_layout` (:sc:`next_layout` by default). To
|
||||||
are available use :opt:`enabled_layouts`, the first listed layout becomes
|
control which layouts are available use :opt:`enabled_layouts`, the first listed
|
||||||
the default. Individual layouts and how to use them are described below.
|
layout becomes the default. Individual layouts and how to use them are described
|
||||||
|
below.
|
||||||
|
|
||||||
|
|
||||||
The Stack Layout
|
The Stack Layout
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
This is the simplest layout it displays a single window using all available
|
This is the simplest layout. It displays a single window using all available
|
||||||
space, other windows are hidden behind it. It has no options::
|
space, other windows are hidden behind it. This layout has no options::
|
||||||
|
|
||||||
enabled_layouts stack
|
enabled_layouts stack
|
||||||
|
|
||||||
@@ -40,14 +42,14 @@ space, other windows are hidden behind it. It has no options::
|
|||||||
The Tall Layout
|
The Tall Layout
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Displays one (or optionally more) full height windows on the left half of the
|
Displays one (or optionally more) full-height windows on the left half of the
|
||||||
screen. Remaining windows are tiled vertically on the right half of the screen.
|
screen. Remaining windows are tiled vertically on the right half of the screen.
|
||||||
There are options to control how the screen is split horizontally ``bias``
|
There are options to control how the screen is split horizontally ``bias``
|
||||||
(an integer between ``10`` and ``90``) and options to control how many
|
(an integer between ``10`` and ``90``) and options to control how many
|
||||||
full-height windows there are ``full_size`` (a positive integer). The
|
full-height windows there are ``full_size`` (a positive integer). The
|
||||||
``mirrored`` option when set to ``true`` will cause the short windows to be
|
``mirrored`` option when set to ``true`` will cause the full-height windows to
|
||||||
on the left side of the screen instead of the right. The syntax
|
be on the right side of the screen instead of the left. The syntax
|
||||||
for the options is shown below::
|
for the options is::
|
||||||
|
|
||||||
enabled_layouts tall:bias=50;full_size=1;mirrored=false
|
enabled_layouts tall:bias=50;full_size=1;mirrored=false
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ for the options is shown below::
|
|||||||
│ │ │
|
│ │ │
|
||||||
└──────────────┴───────────────┘
|
└──────────────┴───────────────┘
|
||||||
|
|
||||||
In addition, you can map keys to increase or decrease the number of full size
|
In addition, you can map keys to increase or decrease the number of full-height
|
||||||
windows, for example::
|
windows, for example::
|
||||||
|
|
||||||
map ctrl+[ layout_action decrease_num_full_size_windows
|
map ctrl+[ layout_action decrease_num_full_size_windows
|
||||||
@@ -75,14 +77,13 @@ windows, for example::
|
|||||||
The Fat Layout
|
The Fat Layout
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
Displays one (or optionally more) full width windows on the top half of the
|
Displays one (or optionally more) full-width windows on the top half of the
|
||||||
screen. Remaining windows are tiled horizontally on the bottom half of the screen.
|
screen. Remaining windows are tiled horizontally on the bottom half of the
|
||||||
There are options to control how the screen is split vertically ``bias``
|
screen. There are options to control how the screen is split vertically ``bias``
|
||||||
(an integer between ``10`` and ``90``) and options to control how many
|
(an integer between ``10`` and ``90``) and options to control how many
|
||||||
full-height windows there are ``full_size`` (a positive integer). The
|
full-width windows there are ``full_size`` (a positive integer). The
|
||||||
``mirrored`` option when set to ``true`` will cause the narrow windows to be
|
``mirrored`` option when set to ``true`` will cause the full-width windows to be
|
||||||
on the top of the screen instead of the bottom. The syntax for the options is
|
on the bottom of the screen instead of the top. The syntax for the options is::
|
||||||
shown below::
|
|
||||||
|
|
||||||
enabled_layouts fat:bias=50;full_size=1;mirrored=false
|
enabled_layouts fat:bias=50;full_size=1;mirrored=false
|
||||||
|
|
||||||
@@ -100,11 +101,16 @@ shown below::
|
|||||||
└─────────┴──────────┴─────────┘
|
└─────────┴──────────┴─────────┘
|
||||||
|
|
||||||
|
|
||||||
|
This layout also supports ``decrease_num_full_size_windows`` layout action like
|
||||||
|
the *Tall* layout, shown above.
|
||||||
|
|
||||||
|
|
||||||
The Grid Layout
|
The Grid Layout
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Display windows in a balanced grid with all windows the same size except the
|
Display windows in a balanced grid with all windows the same size except the
|
||||||
last column if there are not enough windows to fill the grid. Has no options::
|
last column if there are not enough windows to fill the grid. This layout has no
|
||||||
|
options::
|
||||||
|
|
||||||
enabled_layouts grid
|
enabled_layouts grid
|
||||||
|
|
||||||
@@ -132,20 +138,20 @@ define a few extra key bindings in :file:`kitty.conf`::
|
|||||||
|
|
||||||
# Create a new window splitting the space used by the existing one so that
|
# Create a new window splitting the space used by the existing one so that
|
||||||
# the two windows are placed one above the other
|
# the two windows are placed one above the other
|
||||||
map F5 launch --location=hsplit
|
map f5 launch --location=hsplit
|
||||||
|
|
||||||
# Create a new window splitting the space used by the existing one so that
|
# Create a new window splitting the space used by the existing one so that
|
||||||
# the two windows are placed side by side
|
# the two windows are placed side by side
|
||||||
map F6 launch --location=vsplit
|
map f6 launch --location=vsplit
|
||||||
|
|
||||||
# Create a new window splitting the space used by the existing one so that
|
# Create a new window splitting the space used by the existing one so that
|
||||||
# the two windows are placed side by side if the existing window is wide or
|
# the two windows are placed side by side if the existing window is wide or
|
||||||
# one above the other if the existing window is tall.
|
# one above the other if the existing window is tall.
|
||||||
map F4 launch --location=split
|
map f4 launch --location=split
|
||||||
|
|
||||||
# Rotate the current split, chaging its split axis from vertical to
|
# Rotate the current split, chaging its split axis from vertical to
|
||||||
# horizontal or vice versa
|
# horizontal or vice versa
|
||||||
map F7 layout_action rotate
|
map f7 layout_action rotate
|
||||||
|
|
||||||
# Move the active window in the indicated direction
|
# Move the active window in the indicated direction
|
||||||
map shift+up move_window up
|
map shift+up move_window up
|
||||||
@@ -159,16 +165,16 @@ define a few extra key bindings in :file:`kitty.conf`::
|
|||||||
map ctrl+up neighboring_window up
|
map ctrl+up neighboring_window up
|
||||||
map ctrl+down neighboring_window down
|
map ctrl+down neighboring_window down
|
||||||
|
|
||||||
Windows can be resized using :ref:`window_resizing`. You can swap the windows
|
Windows can be resized using :ref:`window_resizing`. You can swap the windows
|
||||||
in a split using the ``rotate`` action with an argument of ``180`` and rotate
|
in a split using the ``rotate`` action with an argument of ``180`` and rotate
|
||||||
and swap with an argument of ``270``.
|
and swap with an argument of ``270``.
|
||||||
|
|
||||||
This layout takes one option, ``split_axis`` that controls whether new windows
|
This layout takes one option, ``split_axis`` that controls whether new windows
|
||||||
are placed into vertical or horizontal splits when a ``--location`` is not
|
are placed into vertical or horizontal splits when a :option:`--location <launch
|
||||||
specified. A value of ``horizontal`` (same as ``--location=vsplit``)
|
--location>` is not specified. A value of ``horizontal`` (same as
|
||||||
means when a new split is created the two windows will be placed side by side
|
``--location=vsplit``) means when a new split is created the two windows will be
|
||||||
and a value of ``vertical`` (same as ``--location=hsplit``) means the two
|
placed side by side and a value of ``vertical`` (same as ``--location=hsplit``)
|
||||||
windows will be placed one on top of the other. By default::
|
means the two windows will be placed one on top of the other. By default::
|
||||||
|
|
||||||
enabled_layouts splits:split_axis=horizontal
|
enabled_layouts splits:split_axis=horizontal
|
||||||
|
|
||||||
@@ -193,7 +199,7 @@ windows will be placed one on top of the other. By default::
|
|||||||
The Horizontal Layout
|
The Horizontal Layout
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
All windows are shown side by side. Has no options::
|
All windows are shown side by side. This layout has no options::
|
||||||
|
|
||||||
enabled_layouts horizontal
|
enabled_layouts horizontal
|
||||||
|
|
||||||
@@ -213,7 +219,7 @@ All windows are shown side by side. Has no options::
|
|||||||
The Vertical Layout
|
The Vertical Layout
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
All windows are shown one below the other. Has no options::
|
All windows are shown one below the other. This layout has no options::
|
||||||
|
|
||||||
enabled_layouts vertical
|
enabled_layouts vertical
|
||||||
|
|
||||||
@@ -239,14 +245,14 @@ Resizing windows
|
|||||||
|
|
||||||
You can resize windows inside layouts. Press :sc:`start_resizing_window` (also
|
You can resize windows inside layouts. Press :sc:`start_resizing_window` (also
|
||||||
:kbd:`⌘+r` on macOS) to enter resizing mode and follow the on-screen
|
:kbd:`⌘+r` on macOS) to enter resizing mode and follow the on-screen
|
||||||
instructions. In a given window layout only some operations may be possible
|
instructions. In a given window layout only some operations may be possible for
|
||||||
for a particular window. For example, in the Tall layout you can make the first
|
a particular window. For example, in the *Tall* layout you can make the first
|
||||||
window wider/narrower, but not taller/shorter. Note that what you are resizing
|
window wider/narrower, but not taller/shorter. Note that what you are resizing
|
||||||
is actually not a window, but a row/column in the layout, all windows in that
|
is actually not a window, but a row/column in the layout, all windows in that
|
||||||
row/column will be resized.
|
row/column will be resized.
|
||||||
|
|
||||||
You can also define shortcuts in :file:`kitty.conf` to make the active window
|
You can also define shortcuts in :file:`kitty.conf` to make the active window
|
||||||
wider, narrower, taller, or shorter by mapping to the ``resize_window``
|
wider, narrower, taller, or shorter by mapping to the :ac:`resize_window`
|
||||||
action, for example::
|
action, for example::
|
||||||
|
|
||||||
map ctrl+left resize_window narrower
|
map ctrl+left resize_window narrower
|
||||||
@@ -256,22 +262,21 @@ action, for example::
|
|||||||
# reset all windows in the tab to default sizes
|
# reset all windows in the tab to default sizes
|
||||||
map ctrl+home resize_window reset
|
map ctrl+home resize_window reset
|
||||||
|
|
||||||
The ``resize_window`` action has a second, optional argument to control
|
The :ac:`resize_window` action has a second optional argument to control
|
||||||
the resizing increment (a positive integer that defaults to 1).
|
the resizing increment (a positive integer that defaults to 1).
|
||||||
|
|
||||||
|
Some layouts take options to control their behavior. For example, the *Fat*
|
||||||
Some layouts take options to control their behavior. For example, the ``fat``
|
and *Tall* layouts accept the ``bias`` and ``full_size`` options to control
|
||||||
and ``tall`` layouts accept the ``bias`` and ``full_size`` options to control
|
how the available space is split up. To specify the option, in :opt:`kitty.conf
|
||||||
how the available space is split up.
|
<enabled_layouts>` use::
|
||||||
To specify the option, in :opt:`kitty.conf <enabled_layouts>` use::
|
|
||||||
|
|
||||||
enabled_layouts tall:bias=70;full_size=2
|
enabled_layouts tall:bias=70;full_size=2
|
||||||
|
|
||||||
This will have ``2`` instead of a single tall window, that occupy ``70%``
|
This will have ``2`` instead of a single tall window, that occupy ``70%``
|
||||||
instead of ``50%`` of available width. ``bias`` can be any number between 10
|
instead of ``50%`` of available width. ``bias`` can be any number between ``10``
|
||||||
and 90.
|
and ``90``.
|
||||||
|
|
||||||
Writing a new layout only requires about two hundred lines of code, so if there
|
Writing a new layout only requires about two hundred lines of code, so if there
|
||||||
is some layout you want, take a look at one of the existing layouts in the
|
is some layout you want, take a look at one of the existing layouts in the
|
||||||
`layout <https://github.com/kovidgoyal/kitty/tree/master/kitty/layout>`_
|
`layout <https://github.com/kovidgoyal/kitty/tree/master/kitty/layout>`__
|
||||||
package and submit a pull request!
|
package and submit a pull request!
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ running programs or similar. Lets start with a few examples:
|
|||||||
Examples
|
Examples
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Suppose we want to be able to highlight the word ERROR in the current window.
|
Suppose we want to be able to highlight the word :code:`ERROR` in the current
|
||||||
Add the following to :file:`kitty.conf`::
|
window. Add the following to :file:`kitty.conf`::
|
||||||
|
|
||||||
map f1 toggle_marker text 1 ERROR
|
map f1 toggle_marker text 1 ERROR
|
||||||
|
|
||||||
Now when you press :kbd:`F1` all instances of the word :code:`ERROR` will be
|
Now when you press :kbd:`F1`, all instances of the word :code:`ERROR` will be
|
||||||
highlighted. To turn off the highlighting, press :kbd:`F1` again.
|
highlighted. To turn off the highlighting, press :kbd:`F1` again.
|
||||||
If you want to make it case-insensitive, use::
|
If you want to make it case-insensitive, use::
|
||||||
|
|
||||||
@@ -39,41 +39,40 @@ can control the colors used for these groups in :file:`kitty.conf` with::
|
|||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
For performance reasons, matching is done per line only, and only when that line is
|
For performance reasons, matching is done per line only, and only when that
|
||||||
altered in any way. So you cannot match text that stretches across multiple
|
line is altered in any way. So you cannot match text that stretches across
|
||||||
lines.
|
multiple lines.
|
||||||
|
|
||||||
|
|
||||||
Creating markers dynamically
|
Creating markers dynamically
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
If you want to create markers dynamically rather than pre-defining them in
|
If you want to create markers dynamically rather than pre-defining them in
|
||||||
:file:`kitty.conf` you can do so as follows::
|
:file:`kitty.conf`, you can do so as follows::
|
||||||
|
|
||||||
map f1 create_marker
|
map f1 create_marker
|
||||||
map f2 remove_marker
|
map f2 remove_marker
|
||||||
|
|
||||||
Then pressing :kbd:`F1` will allow you to enter the marker definition and set
|
Then pressing :kbd:`F1` will allow you to enter the marker definition and set it
|
||||||
it and pressing :kbd:`F2` will remove the marker. ``create_marker`` accepts
|
and pressing :kbd:`F2` will remove the marker. :ac:`create_marker` accepts the
|
||||||
the same syntax as ``toggle_marker`` above. Note that while creating
|
same syntax as :ac:`toggle_marker` above. Note that while creating markers, the
|
||||||
markers, the prompt has history so you can easily re-use previous marker
|
prompt has history so you can easily re-use previous marker expressions.
|
||||||
expressions.
|
|
||||||
|
|
||||||
You can also use the facilities for :doc:`remote-control` to dynamically
|
You can also use the facilities for :doc:`remote-control` to dynamically add or
|
||||||
add/remove markers.
|
remove markers.
|
||||||
|
|
||||||
|
|
||||||
Scrolling to marks
|
Scrolling to marks
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
kitty has an action to scroll to the next line that contains a mark. You can
|
kitty has a :ac:`scroll_to_mark` action to scroll to the next line that contains
|
||||||
use it by mapping it to some shortcut in :file:`kitty.conf`::
|
a mark. You can use it by mapping it to some shortcut in :file:`kitty.conf`::
|
||||||
|
|
||||||
map ctrl+p scroll_to_mark prev
|
map ctrl+p scroll_to_mark prev
|
||||||
map ctrl+n scroll_to_mark next
|
map ctrl+n scroll_to_mark next
|
||||||
|
|
||||||
Then pressing :kbd:`ctrl+p` will scroll to the first line in the scrollback
|
Then pressing :kbd:`Ctrl+P` will scroll to the first line in the scrollback
|
||||||
buffer above the current top line that contains a mark. Pressing :kbd:`ctrl+n`
|
buffer above the current top line that contains a mark. Pressing :kbd:`Ctrl+N`
|
||||||
will scroll to show the first line below the current last line that contains
|
will scroll to show the first line below the current last line that contains
|
||||||
a mark. If you wish to jump to a mark of a specific type, you can add that to
|
a mark. If you wish to jump to a mark of a specific type, you can add that to
|
||||||
the mapping::
|
the mapping::
|
||||||
@@ -86,26 +85,26 @@ Which will scroll only to marks of type 1.
|
|||||||
The full syntax for creating marks
|
The full syntax for creating marks
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
The syntax of the :code:`toggle_marker` command is::
|
The syntax of the :ac:`toggle_marker` action is::
|
||||||
|
|
||||||
toggle_marker <marker-type> <specification>
|
toggle_marker <marker-type> <specification>
|
||||||
|
|
||||||
Here :code:`marker-type` is one of:
|
Here :code:`marker-type` is one of:
|
||||||
|
|
||||||
* :code:`text` - simple substring matching
|
* :code:`text` - Simple substring matching
|
||||||
* :code:`itext` - case-insensitive substring matching
|
* :code:`itext` - Case-insensitive substring matching
|
||||||
* :code:`regex` - A python regular expression
|
* :code:`regex` - A Python regular expression
|
||||||
* :code:`iregex` - A case-insensitive python regular expression
|
* :code:`iregex` - A case-insensitive Python regular expression
|
||||||
* :code:`function` - An arbitrary function defined in a python file, see :ref:`marker_funcs`.
|
* :code:`function` - An arbitrary function defined in a Python file, see :ref:`marker_funcs`.
|
||||||
|
|
||||||
.. _marker_funcs:
|
.. _marker_funcs:
|
||||||
|
|
||||||
Arbitrary marker functions
|
Arbitrary marker functions
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
You can create your own marker functions. Create a python file named
|
You can create your own marker functions. Create a Python file named
|
||||||
:file:`mymarker.py` and in it create a :code:`marker` function. This
|
:file:`mymarker.py` and in it create a :code:`marker` function. This function
|
||||||
function receives the text of the line as input and must yield three numbers,
|
receives the text of the line as input and must yield three numbers,
|
||||||
the starting character position, the ending character position and the mark
|
the starting character position, the ending character position and the mark
|
||||||
group (1-3). For example:
|
group (1-3). For example:
|
||||||
|
|
||||||
@@ -122,6 +121,7 @@ Save this file somewhere and in :file:`kitty.conf`, use::
|
|||||||
|
|
||||||
map f1 toggle_marker function /path/to/mymarker.py
|
map f1 toggle_marker function /path/to/mymarker.py
|
||||||
|
|
||||||
If you save the file in the kitty config directory, you can use::
|
If you save the file in the :ref:`kitty config directory <confloc>`, you can
|
||||||
|
use::
|
||||||
|
|
||||||
map f1 toggle_marker function mymarker.py
|
map f1 toggle_marker function mymarker.py
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
Scripting the mouse click
|
Scripting the mouse click
|
||||||
======================================================
|
======================================================
|
||||||
|
|
||||||
|kitty| has support for `terminal hyperlinks
|
|kitty| has support for :term:`terminal hyperlinks <hyperlinks>`. These are
|
||||||
<https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`_. These
|
generated by many terminal programs, such as ``ls``, ``gcc``, ``systemd``,
|
||||||
are generated by many terminal programs, such as ``ls``, ``gcc``, ``systemd``,
|
:ref:`tool_mdcat`, etc. You can customize exactly what happens when clicking on
|
||||||
:ref:`tool_mdcat`, etc. You can customize exactly what happens when clicking on these
|
these hyperlinks in |kitty|.
|
||||||
hyperlinks in |kitty|.
|
|
||||||
|
|
||||||
You can tell kitty to take arbitrarily many, complex actions
|
You can tell kitty to take arbitrarily many, complex actions when a link is
|
||||||
when a link is clicked. Let us illustrate with some examples, first. Create
|
clicked. Let us illustrate with some examples, first. Create the file
|
||||||
the file :file:`~/.config/kitty/open-actions.conf` with the following:
|
:file:`~/.config/kitty/open-actions.conf` with the following:
|
||||||
|
|
||||||
.. code:: conf
|
.. code:: conf
|
||||||
|
|
||||||
@@ -22,6 +21,13 @@ Now, run ``ls --hyperlink=auto`` in kitty and click on the filename of an
|
|||||||
image, holding down :kbd:`ctrl+shift`. It will be opened over the current
|
image, holding down :kbd:`ctrl+shift`. It will be opened over the current
|
||||||
window. Press any key to close it.
|
window. Press any key to close it.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The :program:`ls` comes with macOS does not support hyperlink, you need to
|
||||||
|
install `GNU Coreutils <https://www.gnu.org/software/coreutils/>`__. If you
|
||||||
|
install it via `Homebrew <https://formulae.brew.sh/formula/coreutils>`__, it
|
||||||
|
will be :program:`gls`.
|
||||||
|
|
||||||
Each entry in :file:`open-actions.conf` consists of one or more
|
Each entry in :file:`open-actions.conf` consists of one or more
|
||||||
:ref:`matching_criteria`, such as ``protocol`` and ``mime`` and one or more
|
:ref:`matching_criteria`, such as ``protocol`` and ``mime`` and one or more
|
||||||
``action`` entries. In the example above kitty uses the :doc:`launch <launch>`
|
``action`` entries. In the example above kitty uses the :doc:`launch <launch>`
|
||||||
@@ -29,8 +35,8 @@ action which can be used to run external programs. Entries are separated by
|
|||||||
blank lines.
|
blank lines.
|
||||||
|
|
||||||
Actions are very powerful, anything that you can map to a key combination in
|
Actions are very powerful, anything that you can map to a key combination in
|
||||||
`kitty.conf` can be used as an action. You can specify more than one action per
|
:file:`kitty.conf` can be used as an action. You can specify more than one
|
||||||
entry if you like, for example:
|
action per entry if you like, for example:
|
||||||
|
|
||||||
|
|
||||||
.. code:: conf
|
.. code:: conf
|
||||||
@@ -60,7 +66,7 @@ some special variables, documented below:
|
|||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
You can use the :opt:`action_alias` option just as in kitty.conf to
|
You can use the :opt:`action_alias` option just as in :file:`kitty.conf` to
|
||||||
define aliases for frequently used actions.
|
define aliases for frequently used actions.
|
||||||
|
|
||||||
|
|
||||||
@@ -77,7 +83,7 @@ lines. The various available criteria are:
|
|||||||
|
|
||||||
``protocol``
|
``protocol``
|
||||||
A comma separated list of protocols, for example: ``http, https``. If
|
A comma separated list of protocols, for example: ``http, https``. If
|
||||||
absent, there is no constraint on protocol
|
absent, there is no constraint on protocol.
|
||||||
|
|
||||||
``url``
|
``url``
|
||||||
A regular expression that must match against the entire (unquoted) URL
|
A regular expression that must match against the entire (unquoted) URL
|
||||||
@@ -88,11 +94,12 @@ lines. The various available criteria are:
|
|||||||
|
|
||||||
``mime``
|
``mime``
|
||||||
A comma separated list of MIME types, for example: ``text/*, image/*,
|
A comma separated list of MIME types, for example: ``text/*, image/*,
|
||||||
application/pdf``. You can add MIME types to kitty by creating the
|
application/pdf``. You can add MIME types to kitty by creating a file named
|
||||||
:file:`mime.types` in the kitty configuration directory. Useful if your
|
:file:`mime.types` in the :ref:`kitty configuration directory <confloc>`.
|
||||||
system MIME database does not have definitions you need. This file is
|
Useful if your system MIME database does not have definitions you need. This
|
||||||
in the standard format of one definition per line, like: ``text/plain rst
|
file is in the standard format of one definition per line, like:
|
||||||
md``. Note that the MIME type for directories is ``inode/directory``.
|
``text/plain rst md``. Note that the MIME type for directories is
|
||||||
|
``inode/directory``.
|
||||||
|
|
||||||
``ext``
|
``ext``
|
||||||
A comma separated list of file extensions, for example: ``jpeg, tar.gz``
|
A comma separated list of file extensions, for example: ``jpeg, tar.gz``
|
||||||
@@ -116,9 +123,10 @@ URLs onto the kitty dock icon to open them with kitty. The default actions are:
|
|||||||
|
|
||||||
These actions can also be executed from the command line by running::
|
These actions can also be executed from the command line by running::
|
||||||
|
|
||||||
open -a kitty.app file_or_url ... (on macOS only)
|
kitty +open file_or_url another_url ...
|
||||||
or
|
|
||||||
kitty +open file_or_url ...
|
# macOS only
|
||||||
|
open -a kitty.app file_or_url another_url ...
|
||||||
|
|
||||||
Since macOS lacks an official interface to set default URL scheme handlers,
|
Since macOS lacks an official interface to set default URL scheme handlers,
|
||||||
kitty has a command you can use for it. The first argument for is the URL
|
kitty has a command you can use for it. The first argument for is the URL
|
||||||
@@ -133,36 +141,10 @@ defaults to kitty, if not specified. For example:
|
|||||||
kitty +runpy 'from kitty.fast_data_types import cocoa_set_url_handler; import sys; cocoa_set_url_handler(*sys.argv[1:]); print("OK")' xyz someapp.bundle.identifier
|
kitty +runpy 'from kitty.fast_data_types import cocoa_set_url_handler; import sys; cocoa_set_url_handler(*sys.argv[1:]); print("OK")' xyz someapp.bundle.identifier
|
||||||
|
|
||||||
You can customize these actions by creating a :file:`launch-actions.conf` file
|
You can customize these actions by creating a :file:`launch-actions.conf` file
|
||||||
in the kitty config directory, just like
|
in the :ref:`kitty config directory <confloc>`, just like the
|
||||||
the :file:`open-actions.conf` file above. For example:
|
:file:`open-actions.conf` file above. For example:
|
||||||
|
|
||||||
.. code:: conf
|
.. literalinclude:: ../kitty/open_actions.py
|
||||||
|
:language: conf
|
||||||
# Open script files based on their shebangs
|
:start-at: # Open script files
|
||||||
protocol file
|
:end-before: '''.splitlines()))
|
||||||
ext sh,command,tool
|
|
||||||
action launch --hold --type=os-window kitty +shebang $FILE_PATH {SHELL}
|
|
||||||
|
|
||||||
# Open shell specific script files
|
|
||||||
protocol file
|
|
||||||
ext fish,bash,zsh
|
|
||||||
action launch --hold --type=os-window kitty +shebang $FILE_PATH __ext__
|
|
||||||
|
|
||||||
# Open directories
|
|
||||||
protocol file
|
|
||||||
mime inode/directory
|
|
||||||
action launch --type=os-window --cwd $FILE_PATH
|
|
||||||
|
|
||||||
# Open text files without fragments in the editor
|
|
||||||
protocol file
|
|
||||||
mime text/*
|
|
||||||
action launch --type=os-window $EDITOR $FILE_PATH
|
|
||||||
|
|
||||||
# Open image files with icat
|
|
||||||
protocol file
|
|
||||||
mime image/*
|
|
||||||
action launch --type=os-window kitty +kitten icat --hold $FILE_PATH
|
|
||||||
|
|
||||||
# Open ssh URLs with ssh command
|
|
||||||
protocol ssh
|
|
||||||
action launch --type=os-window ssh $URL
|
|
||||||
|
|||||||
@@ -4,23 +4,22 @@ Overview
|
|||||||
Design philosophy
|
Design philosophy
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|kitty| is designed for power keyboard users. To that end all its controls
|
|kitty| is designed for power keyboard users. To that end all its controls work
|
||||||
work with the keyboard (although it fully supports mouse interactions as
|
with the keyboard (although it fully supports mouse interactions as well). Its
|
||||||
well). Its configuration is a simple, human editable, single file for
|
configuration is a simple, human editable, single file for easy reproducibility
|
||||||
easy reproducibility (I like to store configuration in source control).
|
(I like to store configuration in source control).
|
||||||
|
|
||||||
The code in |kitty| is designed to be simple, modular and hackable. It is
|
The code in |kitty| is designed to be simple, modular and hackable. It is
|
||||||
written in a mix of C (for performance sensitive parts) and Python (for
|
written in a mix of C (for performance sensitive parts) and Python (for easy
|
||||||
easy hackability of the UI). It does not depend on any large and complex
|
hackability of the UI). It does not depend on any large and complex UI toolkit,
|
||||||
UI toolkit, using only OpenGL for rendering everything.
|
using only OpenGL for rendering everything.
|
||||||
|
|
||||||
Finally, |kitty| is designed from the ground up to support all modern
|
Finally, |kitty| is designed from the ground up to support all modern terminal
|
||||||
terminal features, such as unicode, true color, bold/italic fonts, text
|
features, such as Unicode, true color, bold/italic fonts, text formatting, etc.
|
||||||
formatting, etc. It even extends existing text formatting escape codes,
|
It even extends existing text formatting escape codes, to add support for
|
||||||
to add support for features not available elsewhere, such as colored and
|
features not available elsewhere, such as colored and styled (curly) underlines.
|
||||||
styled (curly) underlines. One of the design goals of |kitty| is to be
|
One of the design goals of |kitty| is to be easily extensible so that new
|
||||||
easily extensible so that new features can be added in the future with
|
features can be added in the future with relatively little effort.
|
||||||
relatively little effort.
|
|
||||||
|
|
||||||
.. include:: basic.rst
|
.. include:: basic.rst
|
||||||
|
|
||||||
@@ -28,10 +27,10 @@ relatively little effort.
|
|||||||
Configuring kitty
|
Configuring kitty
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|kitty| is highly configurable, everything from keyboard shortcuts to
|
|kitty| is highly configurable, everything from keyboard shortcuts to painting
|
||||||
painting frames-per-second. Press :sc:`edit_config_file` in kitty
|
frames-per-second. Press :sc:`edit_config_file` in kitty to open its fully
|
||||||
to open its fully commented sample config file in your text editor.
|
commented sample config file in your text editor. For details see the
|
||||||
For details see the :doc:`configuration docs <conf>`.
|
:doc:`configuration docs <conf>`.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:hidden:
|
:hidden:
|
||||||
@@ -51,17 +50,26 @@ windows automatically, resizing and moving them as needed. You can create a new
|
|||||||
|
|
||||||
Currently, there are seven layouts available:
|
Currently, there are seven layouts available:
|
||||||
|
|
||||||
* **Fat** -- One (or optionally more) windows are shown full width on the top, the rest of the windows are shown side-by-side on the bottom
|
* **Fat** -- One (or optionally more) windows are shown full width on the top,
|
||||||
|
the rest of the windows are shown side-by-side on the bottom
|
||||||
|
|
||||||
* **Grid** -- All windows are shown in a grid
|
* **Grid** -- All windows are shown in a grid
|
||||||
|
|
||||||
* **Horizontal** -- All windows are shown side-by-side
|
* **Horizontal** -- All windows are shown side-by-side
|
||||||
* **Splits** -- Windows arranged in arbitrary patterns created using horizontal and vertical splits
|
|
||||||
|
* **Splits** -- Windows arranged in arbitrary patterns created using horizontal
|
||||||
|
and vertical splits
|
||||||
|
|
||||||
* **Stack** -- Only a single maximized window is shown at a time
|
* **Stack** -- Only a single maximized window is shown at a time
|
||||||
* **Tall** -- One (or optionally more) windows are shown full height on the left, the rest of the windows are shown one below the other on the right
|
|
||||||
|
* **Tall** -- One (or optionally more) windows are shown full height on the
|
||||||
|
left, the rest of the windows are shown one below the other on the right
|
||||||
|
|
||||||
* **Vertical** -- All windows are shown one below the other
|
* **Vertical** -- All windows are shown one below the other
|
||||||
|
|
||||||
By default, all layouts are enabled and you can switch between layouts using
|
By default, all layouts are enabled and you can switch between layouts using
|
||||||
the :sc:`next_layout` key combination. You can also create shortcuts to select
|
the :sc:`next_layout` key combination. You can also create shortcuts to select
|
||||||
particular layouts, and choose which layouts you want to enable/disable, see
|
particular layouts, and choose which layouts you want to enable, see
|
||||||
:ref:`conf-kitty-shortcuts.layout` for examples. The first layout listed in
|
:ref:`conf-kitty-shortcuts.layout` for examples. The first layout listed in
|
||||||
:opt:`enabled_layouts` becomes the default layout.
|
:opt:`enabled_layouts` becomes the default layout.
|
||||||
|
|
||||||
@@ -79,9 +87,9 @@ Extending kitty
|
|||||||
kitty has a powerful framework for scripting. You can create small terminal
|
kitty has a powerful framework for scripting. You can create small terminal
|
||||||
programs called :doc:`kittens <kittens_intro>`. These can used to add features
|
programs called :doc:`kittens <kittens_intro>`. These can used to add features
|
||||||
to kitty, for example, :doc:`editing remote files <kittens/remote_file>` or
|
to kitty, for example, :doc:`editing remote files <kittens/remote_file>` or
|
||||||
:doc:`inputting unicode characters <kittens/unicode_input>`. They can also be
|
:doc:`inputting Unicode characters <kittens/unicode_input>`. They can also be
|
||||||
used to create programs that leverage kitty's powerful features, for example,
|
used to create programs that leverage kitty's powerful features, for example,
|
||||||
:doc:`viewing images <kittens/icat>` or :doc:`diffing files with images
|
:doc:`viewing images <kittens/icat>` or :doc:`diffing files with image support
|
||||||
<kittens/diff>`.
|
<kittens/diff>`.
|
||||||
|
|
||||||
You can :doc:`create your own kittens to scratch your own itches
|
You can :doc:`create your own kittens to scratch your own itches
|
||||||
@@ -101,9 +109,9 @@ Remote control
|
|||||||
|kitty| has a very powerful system that allows you to control it from the
|
|kitty| has a very powerful system that allows you to control it from the
|
||||||
:doc:`shell prompt, even over SSH <remote-control>`. You can change colors,
|
:doc:`shell prompt, even over SSH <remote-control>`. You can change colors,
|
||||||
fonts, open new :term:`windows <window>`, :term:`tabs <tab>`, set their titles,
|
fonts, open new :term:`windows <window>`, :term:`tabs <tab>`, set their titles,
|
||||||
change window layout, get text
|
change window layout, get text from one window and send text to another, etc.
|
||||||
from one window and send text to another, etc, etc. The possibilities are
|
The possibilities are endless. See the :doc:`tutorial <remote-control>` to get
|
||||||
endless. See the :doc:`tutorial <remote-control>` to get started.
|
started.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:hidden:
|
:hidden:
|
||||||
@@ -117,10 +125,9 @@ Startup Sessions
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
You can control the :term:`tabs <tab>`, :term:`kitty window <window>` layout,
|
You can control the :term:`tabs <tab>`, :term:`kitty window <window>` layout,
|
||||||
working directory, startup programs,
|
working directory, startup programs, etc. by creating a *session* file and using
|
||||||
etc. by creating a "session" file and using the :option:`kitty --session`
|
the :option:`kitty --session` command line flag or the :opt:`startup_session`
|
||||||
command line flag or the :opt:`startup_session` option in :file:`kitty.conf`.
|
option in :file:`kitty.conf`. For example:
|
||||||
For example:
|
|
||||||
|
|
||||||
.. code-block:: session
|
.. code-block:: session
|
||||||
|
|
||||||
@@ -130,28 +137,28 @@ For example:
|
|||||||
cd ~
|
cd ~
|
||||||
# Create a window and run the specified command in it
|
# Create a window and run the specified command in it
|
||||||
launch zsh
|
launch zsh
|
||||||
# Create a window with some environment variables set and run
|
# Create a window with some environment variables set and run vim in it
|
||||||
# vim in it
|
|
||||||
launch --env FOO=BAR vim
|
launch --env FOO=BAR vim
|
||||||
# Set the title for the next window
|
# Set the title for the next window
|
||||||
launch --title "Chat with x" irssi --profile x
|
launch --title "Chat with x" irssi --profile x
|
||||||
|
|
||||||
# Create a new tab (the part after new_tab is the optional tab
|
# Create a new tab
|
||||||
# name which will be displayed in the tab bar, if omitted, the
|
# The part after new_tab is the optional tab title which will be displayed in
|
||||||
# title of the active window will be used instead)
|
# the tab bar, if omitted, the title of the active window will be used instead.
|
||||||
new_tab my tab
|
new_tab my tab
|
||||||
cd ~/somewhere
|
cd ~/somewhere
|
||||||
# Set the layouts allowed in this tab
|
# Set the layouts allowed in this tab
|
||||||
enabled_layouts tall, stack
|
enabled_layouts tall,stack
|
||||||
# Set the current layout
|
# Set the current layout
|
||||||
layout stack
|
layout stack
|
||||||
launch zsh
|
launch zsh
|
||||||
|
|
||||||
# Create a new OS window
|
# Create a new OS window
|
||||||
|
# Any definitions specifed before the first new_os_window will apply to first OS window.
|
||||||
new_os_window
|
new_os_window
|
||||||
# set new window size to 80x25 cells
|
# Set new window size to 80x24 cells
|
||||||
os_window_size 80c 25c
|
os_window_size 80c 24c
|
||||||
# set the --class for the new OS window
|
# Set the --class for the new OS window
|
||||||
os_window_class mywindow
|
os_window_class mywindow
|
||||||
launch sh
|
launch sh
|
||||||
# Make the current window the active (focused) window
|
# Make the current window the active (focused) window
|
||||||
@@ -159,8 +166,8 @@ For example:
|
|||||||
launch emacs
|
launch emacs
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The :doc:`launch <launch>` command when used in a session file
|
The :doc:`launch <launch>` command when used in a session file cannot create
|
||||||
cannot create new OS windows, or tabs.
|
new OS windows, or tabs.
|
||||||
|
|
||||||
|
|
||||||
Creating tabs/windows
|
Creating tabs/windows
|
||||||
@@ -182,25 +189,25 @@ Mouse features
|
|||||||
* You can click on a URL to open it in a browser.
|
* You can click on a URL to open it in a browser.
|
||||||
* You can double click to select a word and then drag to select more words.
|
* You can double click to select a word and then drag to select more words.
|
||||||
* You can triple click to select a line and then drag to select more lines.
|
* You can triple click to select a line and then drag to select more lines.
|
||||||
* You can triple click while holding :kbd:`ctrl+alt` to select from clicked
|
* You can triple click while holding :kbd:`Ctrl+Alt` to select from clicked
|
||||||
point to end of line.
|
point to end of line.
|
||||||
* You can right click to extend a previous selection.
|
* You can right click to extend a previous selection.
|
||||||
* You can hold down :kbd:`ctrl+alt` and drag with the mouse to select in
|
* You can hold down :kbd:`Ctrl+Alt` and drag with the mouse to select in
|
||||||
columns.
|
columns.
|
||||||
* Selecting text automatically copies it to the primary clipboard (on
|
* Selecting text automatically copies it to the primary clipboard (on platforms
|
||||||
platforms with a primary clipboard).
|
|
||||||
* You can middle click to paste from the primary clipboard (on platforms
|
|
||||||
with a primary clipboard).
|
with a primary clipboard).
|
||||||
* You can right click while holding :kbd:`ctrl+shift` to open the output
|
* You can middle click to paste from the primary clipboard (on platforms with a
|
||||||
of the clicked on command in a pager (requires :ref:`shell_integration`)
|
primary clipboard).
|
||||||
* You can select text with kitty even when a terminal program has grabbed
|
* You can right click while holding :kbd:`Ctrl+Shift` to open the output of the
|
||||||
the mouse by holding down the :kbd:`shift` key
|
clicked on command in a pager (requires :ref:`shell_integration`)
|
||||||
|
* You can select text with kitty even when a terminal program has grabbed the
|
||||||
|
mouse by holding down the :kbd:`Shift` key
|
||||||
|
|
||||||
All these actions can be customized in :file:`kitty.conf` as described
|
All these actions can be customized in :file:`kitty.conf` as described
|
||||||
:ref:`here <conf-kitty-mouse.mousemap>`.
|
:ref:`here <conf-kitty-mouse.mousemap>`.
|
||||||
|
|
||||||
You can also customize what happens when clicking on :term:`hyperlinks` in kitty,
|
You can also customize what happens when clicking on :term:`hyperlinks` in
|
||||||
having it open files in your editor, download remote files, open things
|
kitty, having it open files in your editor, download remote files, open things
|
||||||
in your browser, etc.
|
in your browser, etc.
|
||||||
|
|
||||||
For details, see :doc:`here <open_actions>`.
|
For details, see :doc:`here <open_actions>`.
|
||||||
@@ -214,12 +221,11 @@ Font control
|
|||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|kitty| has extremely flexible and powerful font selection features. You can
|
|kitty| has extremely flexible and powerful font selection features. You can
|
||||||
specify individual families for the regular, bold, italic and bold+italic
|
specify individual families for the regular, bold, italic and bold+italic fonts.
|
||||||
fonts. You can even specify specific font families for specific ranges of
|
You can even specify specific font families for specific ranges of Unicode
|
||||||
unicode characters. This allows precise control over text rendering. It can
|
characters. This allows precise control over text rendering. It can comein handy
|
||||||
come in handy for applications like powerline, without the need to use patched
|
for applications like powerline, without the need to use patched fonts. See the
|
||||||
fonts. See the various font related configuration directives in
|
various font related configuration directives in :ref:`conf-kitty-fonts`.
|
||||||
:ref:`conf-kitty-fonts`.
|
|
||||||
|
|
||||||
|
|
||||||
.. _scrollback:
|
.. _scrollback:
|
||||||
@@ -228,29 +234,29 @@ The scrollback buffer
|
|||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|kitty| supports scrolling back to view history, just like most terminals. You
|
|kitty| supports scrolling back to view history, just like most terminals. You
|
||||||
can use either keyboard shortcuts or the mouse scroll wheel to do so. However,
|
can use either keyboard shortcuts or the mouse scroll wheel to do so. However,
|
||||||
|kitty| has an extra, neat feature. Sometimes you need to explore the
|
|kitty| has an extra, neat feature. Sometimes you need to explore the scrollback
|
||||||
scrollback buffer in more detail, maybe search for some text or refer to it
|
buffer in more detail, maybe search for some text or refer to it side-by-side
|
||||||
side-by-side while typing in a follow-up command. |kitty| allows you to do this
|
while typing in a follow-up command. |kitty| allows you to do this by pressing
|
||||||
by pressing the :sc:`show_scrollback` key-combination, which will open the
|
the :sc:`show_scrollback` shortcut, which will open the scrollback buffer in
|
||||||
scrollback buffer in your favorite pager program (which is ``less`` by default).
|
your favorite pager program (which is :program:`less` by default). Colors and
|
||||||
Colors and text formatting are preserved. You can explore the scrollback buffer
|
text formatting are preserved. You can explore the scrollback buffer comfortably
|
||||||
comfortably within the pager.
|
within the pager.
|
||||||
|
|
||||||
Additionally, you can pipe the contents of the scrollback buffer to an
|
Additionally, you can pipe the contents of the scrollback buffer to an
|
||||||
arbitrary, command running in a new :term:`window`, :term:`tab` or :term:`overlay`,
|
arbitrary, command running in a new :term:`window`, :term:`tab` or
|
||||||
for example::
|
:term:`overlay`. For example::
|
||||||
|
|
||||||
map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting less +G -R
|
map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting less +G -R
|
||||||
|
|
||||||
Would open the scrollback buffer in a new :term:`window` when you press the :kbd:`F1`
|
Would open the scrollback buffer in a new :term:`window` when you press the
|
||||||
key. See :sc:`show_scrollback <show_scrollback>` for details.
|
:kbd:`F1` key. See :sc:`show_scrollback <show_scrollback>` for details.
|
||||||
|
|
||||||
If you want to use it with an editor such as vim to get more powerful features,
|
If you want to use it with an editor such as :program:`vim` to get more powerful
|
||||||
you can see tips for doing so, in :iss:`this thread <719>`.
|
features, you can see tips for doing so, in :iss:`this thread <719>`.
|
||||||
|
|
||||||
If you wish to store very large amounts of scrollback to view using the piping or
|
If you wish to store very large amounts of scrollback to view using the piping
|
||||||
:sc:`show_scrollback <show_scrollback>` features, you can use the
|
or :sc:`show_scrollback <show_scrollback>` features, you can use the
|
||||||
:opt:`scrollback_pager_history_size` option.
|
:opt:`scrollback_pager_history_size` option.
|
||||||
|
|
||||||
|
|
||||||
@@ -258,8 +264,8 @@ Integration with shells
|
|||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
kitty has the ability to integrate closely within common shells, such as `zsh
|
kitty has the ability to integrate closely within common shells, such as `zsh
|
||||||
<https://www.zsh.org/>`_, `fish <https://fishshell.com>`_ and `bash
|
<https://www.zsh.org/>`__, `fish <https://fishshell.com>`__ and `bash
|
||||||
<https://www.gnu.org/software/bash/>`_ to enable features such as jumping to
|
<https://www.gnu.org/software/bash/>`__ to enable features such as jumping to
|
||||||
previous prompts in the scrollback, viewing the output of the last command in
|
previous prompts in the scrollback, viewing the output of the last command in
|
||||||
:program:`less`, using the mouse to move the cursor while editing prompts, etc.
|
:program:`less`, using the mouse to move the cursor while editing prompts, etc.
|
||||||
See :doc:`shell-integration` for details.
|
See :doc:`shell-integration` for details.
|
||||||
@@ -274,17 +280,16 @@ See :doc:`shell-integration` for details.
|
|||||||
Multiple copy/paste buffers
|
Multiple copy/paste buffers
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
In addition to being able to copy/paste from the system clipboard, in |kitty| you
|
In addition to being able to copy/paste from the system clipboard, in |kitty|
|
||||||
can also setup an arbitrary number of copy paste buffers. To do so, simply add
|
you can also setup an arbitrary number of copy paste buffers. To do so, simply
|
||||||
something like the following to your :file:`kitty.conf`::
|
add something like the following to your :file:`kitty.conf`::
|
||||||
|
|
||||||
map f1 copy_to_buffer a
|
map f1 copy_to_buffer a
|
||||||
map f2 paste_from_buffer a
|
map f2 paste_from_buffer a
|
||||||
|
|
||||||
This will allow you to press :kbd:`F1` to copy the current selection to an
|
This will allow you to press :kbd:`F1` to copy the current selection to an
|
||||||
internal buffer named ``a`` and :kbd:`F2` to paste from that buffer. The buffer
|
internal buffer named ``a`` and :kbd:`F2` to paste from that buffer. The buffer
|
||||||
names are arbitrary strings, so you can define as many such buffers as you
|
names are arbitrary strings, so you can define as many such buffers as you need.
|
||||||
need.
|
|
||||||
|
|
||||||
|
|
||||||
Marks
|
Marks
|
||||||
|
|||||||
@@ -2,30 +2,31 @@ Performance
|
|||||||
===================
|
===================
|
||||||
|
|
||||||
The main goals for |kitty| performance are user perceived latency while typing
|
The main goals for |kitty| performance are user perceived latency while typing
|
||||||
and "smoothness" while scrolling as well as CPU usage. |kitty| tries hard to find
|
and "smoothness" while scrolling as well as CPU usage. |kitty| tries hard to
|
||||||
an optimum balance for these. To that end it keeps a cache of each rendered
|
find an optimum balance for these. To that end it keeps a cache of each rendered
|
||||||
glyph in video RAM so that font rendering is not a bottleneck. Interaction
|
glyph in video RAM so that font rendering is not a bottleneck. Interaction with
|
||||||
with child programs takes place in a separate thread from rendering, to improve
|
child programs takes place in a separate thread from rendering, to improve
|
||||||
smoothness.
|
smoothness.
|
||||||
|
|
||||||
There are two parameters you can tune to adjust the performance. :opt:`repaint_delay`
|
There are two config options you can tune to adjust the performance,
|
||||||
and :opt:`input_delay`. These control the artificial delays introduced into the
|
:opt:`repaint_delay` and :opt:`input_delay`. These control the artificial delays
|
||||||
render loop to reduce CPU usage. See :ref:`conf-kitty-performance` for details.
|
introduced into the render loop to reduce CPU usage. See
|
||||||
See also the :opt:`sync_to_monitor` option to further decrease latency at the cost
|
:ref:`conf-kitty-performance` for details. See also the :opt:`sync_to_monitor`
|
||||||
of some `tearing <https://en.wikipedia.org/wiki/Screen_tearing>`_ while scrolling.
|
option to further decrease latency at the cost of some `screen tearing
|
||||||
|
<https://en.wikipedia.org/wiki/Screen_tearing>`__ while scrolling.
|
||||||
|
|
||||||
You can generate detailed per-function performance data using `gperftools
|
You can generate detailed per-function performance data using
|
||||||
<https://github.com/gperftools/gperftools>`_. Build |kitty| with `make
|
`gperftools <https://github.com/gperftools/gperftools>`__. Build |kitty| with
|
||||||
profile`. Run kitty and perform the task you want to analyse, for example,
|
``make profile``. Run kitty and perform the task you want to analyse, for
|
||||||
scrolling a large file with `less`. After you quit, function call statistics
|
example, scrolling a large file with :program:`less`. After you quit, function
|
||||||
will be printed to `stdout` and you can use tools like *kcachegrind* for more
|
call statistics will be printed to STDOUT and you can use tools like
|
||||||
detailed analysis.
|
*KCachegrind* for more detailed analysis.
|
||||||
|
|
||||||
Here are some CPU usage numbers for the task of scrolling a file continuously
|
Here are some CPU usage numbers for the task of scrolling a file continuously in
|
||||||
in less. The CPU usage is for the terminal process and X together and is
|
:program:`less`. The CPU usage is for the terminal process and X together and is
|
||||||
measured using htop. The measurements are taken at the same font and window
|
measured using :program:`htop`. The measurements are taken at the same font and
|
||||||
size for all terminals on a ``Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz`` CPU
|
window size for all terminals on a ``Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz``
|
||||||
with a ``Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde XT [Radeon HD
|
CPU with a ``Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde XT [Radeon HD
|
||||||
7770/8760 / R7 250X]`` GPU.
|
7770/8760 / R7 250X]`` GPU.
|
||||||
|
|
||||||
============== =========================
|
============== =========================
|
||||||
@@ -49,11 +50,11 @@ admittedly biased, eyes).
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Some people have asked why kitty does not perform better than terminal XXX in
|
Some people have asked why kitty does not perform better than terminal XXX
|
||||||
the test of sinking large amounts of data, such as catting a large text
|
in the test of sinking large amounts of data, such as catting a large text
|
||||||
file. The answer is because this is not a goal for kitty. kitty
|
file. The answer is because this is not a goal for kitty. kitty deliberately
|
||||||
deliberately throttles input parsing and output rendering to minimize
|
throttles input parsing and output rendering to minimize resource usage
|
||||||
resource usage while still being able to sink output faster than any real
|
while still being able to sink output faster than any real world program can
|
||||||
world program can produce it. Reducing CPU usage, and hence battery drain
|
produce it. Reducing CPU usage, and hence battery drain while achieving
|
||||||
while achieving instant response times and smooth scrolling to a human eye
|
instant response times and smooth scrolling to a human eye is a far more
|
||||||
is a far more important goal.
|
important goal.
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
Terminal protocol extensions
|
Terminal protocol extensions
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
|kitty| has extensions to the legacy terminal protocol, to enable advanced features.
|
|kitty| has extensions to the legacy terminal protocol, to enable advanced
|
||||||
These are typically in the form of new or re-purposed escape codes. While these
|
features. These are typically in the form of new or re-purposed escape codes.
|
||||||
extensions are currently |kitty| specific, it would be nice to get some of them
|
While these extensions are currently |kitty| specific, it would be nice to get
|
||||||
adopted more broadly, to push the state of terminal emulators forward.
|
some of them adopted more broadly, to push the state of terminal emulators
|
||||||
|
forward.
|
||||||
|
|
||||||
The goal of these extensions is to be as small and unobtrusive as possible,
|
The goal of these extensions is to be as small and unobtrusive as possible,
|
||||||
while filling in some gaps in the existing xterm protocol. In particular, one
|
while filling in some gaps in the existing xterm protocol. In particular, one of
|
||||||
of the goals of this specification is explicitly not to "re-imagine" the tty.
|
the goals of this specification is explicitly not to "re-imagine" the TTY. The
|
||||||
The tty should remain what it is -- a device for efficiently processing text
|
TTY should remain what it is -- a device for efficiently processing text
|
||||||
received as a simple byte stream. Another objective is to only move the minimum
|
received as a simple byte stream. Another objective is to only move the minimum
|
||||||
possible amount of extra functionality into the terminal program itself. This
|
possible amount of extra functionality into the terminal program itself. This is
|
||||||
is to make it as easy to implement these protocol extensions as possible,
|
to make it as easy to implement these protocol extensions as possible, thereby
|
||||||
thereby hopefully encouraging their widespread adoption.
|
hopefully encouraging their widespread adoption.
|
||||||
|
|
||||||
If you wish to discuss these extensions, propose additions/changes to them
|
If you wish to discuss these extensions, propose additions or changes to them,
|
||||||
please do so by opening issues in the `GitHub
|
please do so by opening issues in the `GitHub bug tracker
|
||||||
<https://github.com/kovidgoyal/kitty/issues>`_ bug tracker.
|
<https://github.com/kovidgoyal/kitty/issues>`__.
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ Quickstart
|
|||||||
binary
|
binary
|
||||||
build
|
build
|
||||||
|
|
||||||
Pre-built binaries of |kitty| are available for both macOS and Linux.
|
Pre-built binaries of |kitty| are available for both macOS and Linux. See the
|
||||||
See the :doc:`binary install instructions </binary>`. You can also
|
:doc:`binary install instructions </binary>`. You can also :doc:`build from
|
||||||
:doc:`build from source </build>`.
|
source </build>`.
|
||||||
|
|
||||||
Additionally, you can use your favorite package manager to install the |kitty|
|
Additionally, you can use your favorite package manager to install the |kitty|
|
||||||
package, but note that some Linux distribution packages are woefully outdated.
|
package, but note that some Linux distribution packages are woefully outdated.
|
||||||
@@ -25,4 +25,4 @@ and Linux.
|
|||||||
See :doc:`Configuring kitty <conf>` for help on configuring |kitty| and
|
See :doc:`Configuring kitty <conf>` for help on configuring |kitty| and
|
||||||
:doc:`Invocation <invocation>` for the command line arguments |kitty| supports.
|
:doc:`Invocation <invocation>` for the command line arguments |kitty| supports.
|
||||||
|
|
||||||
For a tour of kitty's design and features, see the :doc:`/overview`.
|
For a tour of kitty's design and features, see the :doc:`overview`.
|
||||||
|
|||||||
@@ -16,25 +16,26 @@ Where ``<ESC>`` is the byte ``0x1b``. The JSON object has the form::
|
|||||||
"payload": <Optional JSON object>,
|
"payload": <Optional JSON object>,
|
||||||
}
|
}
|
||||||
|
|
||||||
The ``version`` above is an array of the form :code:`[0, 14, 2]`. If you are developing a
|
The ``version`` above is an array of the form :code:`[0, 14, 2]`. If you are
|
||||||
standalone client, use the kitty version that you are developing against. Using
|
developing a standalone client, use the kitty version that you are developing
|
||||||
a version greater than the version of the kitty instance you are talking to,
|
against. Using a version greater than the version of the kitty instance you are
|
||||||
will cause a failure.
|
talking to, will cause a failure.
|
||||||
|
|
||||||
Set ``no_response`` to ``true`` if you don't want a response from kitty.
|
Set ``no_response`` to ``true`` if you don't want a response from kitty.
|
||||||
|
|
||||||
The optional payload is a JSON object that is specific to the actual command being sent.
|
The optional payload is a JSON object that is specific to the actual command
|
||||||
The fields in the object for every command are documented below.
|
being sent. The fields in the object for every command are documented below.
|
||||||
|
|
||||||
As a quick example showing how easy to use this protocol is, we will implement
|
As a quick example showing how easy to use this protocol is, we will implement
|
||||||
the ``@ ls`` command from the shell using only shell tools. First, run kitty
|
the ``@ ls`` command from the shell using only shell tools.
|
||||||
as::
|
|
||||||
|
First, run kitty as::
|
||||||
|
|
||||||
kitty -o allow_remote_control=socket-only --listen-on unix:/tmp/test
|
kitty -o allow_remote_control=socket-only --listen-on unix:/tmp/test
|
||||||
|
|
||||||
Now, in a different terminal, you can get the pretty printed ``@ ls`` output
|
Now, in a different terminal, you can get the pretty printed ``@ ls`` output
|
||||||
with the following command line::
|
with the following command line::
|
||||||
|
|
||||||
echo -en '\eP@kitty-cmd{"cmd":"ls","version":[0,14,2]}\e\' | socat - unix:/tmp/test | awk '{ print substr($0, 13, length($0) - 14) }' | jq -c '.data | fromjson' | jq .
|
echo -en '\eP@kitty-cmd{"cmd":"ls","version":[0,14,2]}\e\\' | socat - unix:/tmp/test | awk '{ print substr($0, 13, length($0) - 14) }' | jq -c '.data | fromjson' | jq .
|
||||||
|
|
||||||
.. include:: generated/rc.rst
|
.. include:: generated/rc.rst
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ Control kitty from scripts
|
|||||||
.. highlight:: sh
|
.. highlight:: sh
|
||||||
|
|
||||||
|kitty| can be controlled from scripts or the shell prompt. You can open new
|
|kitty| can be controlled from scripts or the shell prompt. You can open new
|
||||||
windows, send arbitrary text input to any window, name windows and tabs, etc.
|
windows, send arbitrary text input to any window, change the title of windows
|
||||||
|
and tabs, etc.
|
||||||
|
|
||||||
Let's walk through a few examples of controlling |kitty|.
|
Let's walk through a few examples of controlling |kitty|.
|
||||||
|
|
||||||
|
|
||||||
Tutorial
|
Tutorial
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@@ -21,43 +24,44 @@ Now, in the new |kitty| window, enter the command::
|
|||||||
|
|
||||||
kitty @ launch --title Output --keep-focus cat
|
kitty @ launch --title Output --keep-focus cat
|
||||||
|
|
||||||
This will open a new window, running the ``cat`` program that will appear next
|
This will open a new window, running the :program:`cat` program that will appear
|
||||||
to the current window.
|
next to the current window.
|
||||||
|
|
||||||
Let's send some text to this new window::
|
Let's send some text to this new window::
|
||||||
|
|
||||||
kitty @ send-text --match cmdline:cat Hello, World
|
kitty @ send-text --match cmdline:cat Hello, World
|
||||||
|
|
||||||
This will make ``Hello, World`` show up in the window running the ``cat`` program.
|
This will make ``Hello, World`` show up in the window running the :program:`cat`
|
||||||
The :option:`kitty @ send-text --match` option is very powerful, it allows selecting windows by their
|
program. The :option:`kitty @ send-text --match` option is very powerful, it
|
||||||
titles, the command line of the program running in the window, the working
|
allows selecting windows by their titles, the command line of the program
|
||||||
directory of the program running in the window, etc. See ``kitty @ send-text
|
running in the window, the working directory of the program running in the
|
||||||
--help`` for details.
|
window, etc. See :ref:`kitty @ send-text --help <at_send-text>` for details.
|
||||||
|
|
||||||
More usefully, you can pipe the output of a command running in one window to
|
More usefully, you can pipe the output of a command running in one window to
|
||||||
another window, for example::
|
another window, for example::
|
||||||
|
|
||||||
ls | kitty @ send-text --match title:Output --stdin
|
ls | kitty @ send-text --match title:^Output --stdin
|
||||||
|
|
||||||
This will show the output of ls in the output window instead of the current
|
This will show the output of :program:`ls` in the output window instead of the
|
||||||
window. You can use this technique to, for example, show the output of running
|
current window. You can use this technique to, for example, show the output of
|
||||||
``make`` in your editor in a different window. The possibilities are endless.
|
running :program:`make` in your editor in a different window. The possibilities
|
||||||
|
are endless.
|
||||||
|
|
||||||
You can even have things you type show up in a different window. Run::
|
You can even have things you type show up in a different window. Run::
|
||||||
|
|
||||||
kitty @ send-text --match title:Output --stdin
|
kitty @ send-text --match title:^Output --stdin
|
||||||
|
|
||||||
And type some text, it will show up in the output window, instead of the current
|
And type some text, it will show up in the output window, instead of the current
|
||||||
window. Type ``Ctrl+D`` when you are ready to stop.
|
window. Type :kbd:`Ctrl+D` when you are ready to stop.
|
||||||
|
|
||||||
Now, let's open a new tab::
|
Now, let's open a new tab::
|
||||||
|
|
||||||
kitty @ launch --type=tab --tab-title "My Tab" --keep-focus bash
|
kitty @ launch --type=tab --tab-title "My Tab" --keep-focus bash
|
||||||
|
|
||||||
This will open a new tab running the bash shell with the title "My Tab".
|
This will open a new tab running the bash shell with the title "My Tab".
|
||||||
We can change the title of the tab with::
|
We can change the title of the tab to "New Title" with::
|
||||||
|
|
||||||
kitty @ set-tab-title --match title:My New Title
|
kitty @ set-tab-title --match title:^My New Title
|
||||||
|
|
||||||
Let's change the title of the current tab::
|
Let's change the title of the current tab::
|
||||||
|
|
||||||
@@ -65,79 +69,83 @@ Let's change the title of the current tab::
|
|||||||
|
|
||||||
Now lets switch to the newly opened tab::
|
Now lets switch to the newly opened tab::
|
||||||
|
|
||||||
kitty @ focus-tab --match title:New
|
kitty @ focus-tab --match title:^New
|
||||||
|
|
||||||
Similarly, to focus the previously opened output window (which will also switch
|
Similarly, to focus the previously opened output window (which will also switch
|
||||||
back to the old tab, automatically)::
|
back to the old tab, automatically)::
|
||||||
|
|
||||||
kitty @ focus-window --match title:Output
|
kitty @ focus-window --match title:^Output
|
||||||
|
|
||||||
You can get a listing of available tabs and windows, by running::
|
You can get a listing of available tabs and windows, by running::
|
||||||
|
|
||||||
kitty @ ls
|
kitty @ ls
|
||||||
|
|
||||||
This outputs a tree of data in JSON format. The top level of the tree is all
|
This outputs a tree of data in JSON format. The top level of the tree is all
|
||||||
operating system kitty windows. Each OS window has an id and a list of tabs.
|
:term:`OS windows <os_window>`. Each OS window has an id and a list of
|
||||||
Each tab has its own id, a title and a list of windows. Each window has an id,
|
:term:`tabs <tab>`. Each tab has its own id, a title and a list of :term:`kitty
|
||||||
title, current working directory, process id (PID) and command-line of the
|
windows <window>`. Each window has an id, title, current working directory,
|
||||||
process running in the window. You can use this information with :option:`kitty @ focus-window --match`
|
process id (PID) and command-line of the process running in the window. You can
|
||||||
to control individual windows.
|
use this information with :option:`kitty @ focus-window --match` to control
|
||||||
|
individual windows.
|
||||||
|
|
||||||
As you can see, it is very easy to control |kitty| using the
|
As you can see, it is very easy to control |kitty| using the ``kitty @``
|
||||||
``kitty @`` messaging system. This tutorial touches only the
|
messaging system. This tutorial touches only the surface of what is possible.
|
||||||
surface of what is possible. See ``kitty @ --help`` for more details.
|
See ``kitty @ --help`` for more details.
|
||||||
|
|
||||||
Note that in the example's above, ``kitty @`` messaging works only when run inside a |kitty| window,
|
Note that in the example's above, ``kitty @`` messaging works only when run
|
||||||
not anywhere. But, within a |kitty| window it even works over SSH. If you want to control
|
inside a |kitty| window, not anywhere. But, within a |kitty| window it even
|
||||||
|kitty| from programs/scripts not running inside a |kitty| window, you have to implement a couple of
|
works over SSH. If you want to control |kitty| from programs/scripts not running
|
||||||
extra steps. First start |kitty| as::
|
inside a |kitty| window, you have to implement a couple of extra steps. First
|
||||||
|
start |kitty| as::
|
||||||
|
|
||||||
kitty -o allow_remote_control=yes --listen-on unix:/tmp/mykitty
|
kitty -o allow_remote_control=yes --listen-on unix:/tmp/mykitty
|
||||||
|
|
||||||
The :option:`kitty --listen-on` option tells |kitty| to listen for control messages at the
|
The :option:`kitty --listen-on` option tells |kitty| to listen for control
|
||||||
specified path. See ``kitty --help`` for details. Now you can control this
|
messages at the specified UNIX-domain socket. See ``kitty --help`` for details.
|
||||||
instance of |kitty| using the :option:`kitty @ --to` command line argument to ``kitty @``. For example::
|
Now you can control this instance of |kitty| using the :option:`kitty @ --to`
|
||||||
|
command line argument to ``kitty @``. For example::
|
||||||
|
|
||||||
kitty @ --to unix:/tmp/mykitty ls
|
kitty @ --to unix:/tmp/mykitty ls
|
||||||
|
|
||||||
|
|
||||||
Note that if all you want to do is run a single |kitty| "daemon" and have subsequent
|
Note that if all you want to do is run a single |kitty| "daemon" and have
|
||||||
|kitty| invocations appear as new top-level windows, you can use the simpler :option:`kitty --single-instance`
|
subsequent |kitty| invocations appear as new top-level windows, you can use the
|
||||||
option, see ``kitty --help`` for that.
|
simpler :option:`kitty --single-instance` option, see ``kitty --help`` for that.
|
||||||
|
|
||||||
|
|
||||||
The builtin kitty shell
|
The builtin kitty shell
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
You can explore the |kitty| command language more easily using the builtin |kitty|
|
You can explore the |kitty| command language more easily using the builtin
|
||||||
shell. Run ``kitty @`` with no arguments and you will be dropped into the |kitty|
|
|kitty| shell. Run ``kitty @`` with no arguments and you will be dropped into
|
||||||
shell with completion for |kitty| command names and options.
|
the |kitty| shell with completion for |kitty| command names and options.
|
||||||
|
|
||||||
You can even open the |kitty| shell inside a running |kitty| using a simple
|
You can even open the |kitty| shell inside a running |kitty| using a simple
|
||||||
keyboard shortcut (:sc:`kitty_shell` by default).
|
keyboard shortcut (:sc:`kitty_shell` by default).
|
||||||
|
|
||||||
.. note:: This has the added advantage that you don't need to use
|
.. note:: This has the added advantage that you don't need to use
|
||||||
``allow_remote_control`` to make it work.
|
:opt:`allow_remote_control` to make it work.
|
||||||
|
|
||||||
|
|
||||||
Allowing only some windows to control kitty
|
Allowing only some windows to control kitty
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
If you do not want to allow all programs running in |kitty| to control it, you can selectively
|
If you do not want to allow all programs running in |kitty| to control it, you
|
||||||
enable remote control for only some |kitty| windows. Simply create a shortcut
|
can selectively enable remote control for only some |kitty| windows. Simply
|
||||||
such as::
|
create a shortcut such as::
|
||||||
|
|
||||||
map ctrl+k launch --allow-remote-control some_program
|
map ctrl+k launch --allow-remote-control some_program
|
||||||
|
|
||||||
Then programs running in windows created with that shortcut can use ``kitty @``
|
Then programs running in windows created with that shortcut can use ``kitty @``
|
||||||
to control kitty. Note that any program with the right level of permissions can
|
to control kitty. Note that any program with the right level of permissions can
|
||||||
still write to the pipes of any other program on the same computer and
|
still write to the pipes of any other program on the same computer and therefore
|
||||||
therefore can control |kitty|. It can, however, be useful to block programs
|
can control |kitty|. It can, however, be useful to block programs running on
|
||||||
running on other computers (for example, over ssh) or as other users.
|
other computers (for example, over SSH) or as other users.
|
||||||
|
|
||||||
.. note:: You dont need ``allow_remote_control`` to make this work as it is
|
.. note:: You don't need :opt:`allow_remote_control` to make this work as it is
|
||||||
limited to only programs running in that specific window. Be careful with
|
limited to only programs running in that specific window. Be careful with
|
||||||
what programs you run in such windows, since they can effectively control
|
what programs you run in such windows, since they can effectively control
|
||||||
kitty, as if you were running with ``allow_remote_control`` turned on.
|
kitty, as if you were running with :opt:`allow_remote_control` turned on.
|
||||||
|
|
||||||
|
|
||||||
.. _rc_mapping:
|
.. _rc_mapping:
|
||||||
@@ -148,15 +156,15 @@ Mapping key presses to remote control commands
|
|||||||
If you wish to trigger a remote control command easily with just a keypress,
|
If you wish to trigger a remote control command easily with just a keypress,
|
||||||
you can map it in :file:`kitty.conf`. For example::
|
you can map it in :file:`kitty.conf`. For example::
|
||||||
|
|
||||||
map F1 remote_control set-spacing margin=30
|
map f1 remote_control set-spacing margin=30
|
||||||
|
|
||||||
Then pressing the :kbd:`F1` key will set the active window margins to 30.
|
Then pressing the :kbd:`F1` key will set the active window margins to
|
||||||
The syntax for what follows :code:`remote_control` is exactly the same
|
:code:`30`. The syntax for what follows :ac:`remote_control` is exactly the same
|
||||||
as the syntax for what follows :code:`kitty @` above.
|
as the syntax for what follows :code:`kitty @` above.
|
||||||
|
|
||||||
.. note:: You do not need ``allow_remote_control`` to use these mappings,
|
.. note:: You do not need :opt:`allow_remote_control` to use these mappings,
|
||||||
as they are not actual remote programs, but are simply a way to resuse
|
as they are not actual remote programs, but are simply a way to resuse the
|
||||||
the remote control infrastructure via keybings.
|
remote control infrastructure via keybings.
|
||||||
|
|
||||||
|
|
||||||
Broadcasting what you type to all kitty windows
|
Broadcasting what you type to all kitty windows
|
||||||
@@ -166,17 +174,36 @@ As a simple illustration of the power of remote control, lets
|
|||||||
have what we type sent to all open kitty windows. To do that define the
|
have what we type sent to all open kitty windows. To do that define the
|
||||||
following mapping in :file:`kitty.conf`::
|
following mapping in :file:`kitty.conf`::
|
||||||
|
|
||||||
map F1 launch --allow-remote-control kitty +kitten broadcast
|
map f1 launch --allow-remote-control kitty +kitten broadcast
|
||||||
|
|
||||||
Now press, F1 and start typing, what you type will be sent to all windows,
|
Now press :kbd:`F1` and start typing, what you type will be sent to all windows,
|
||||||
live, as you type it.
|
live, as you type it.
|
||||||
|
|
||||||
|
|
||||||
The remote control protocol
|
The remote control protocol
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
If you wish to develop your own client to talk to |kitty|, you
|
If you wish to develop your own client to talk to |kitty|, you can use the
|
||||||
can use the :doc:`protocol specification <rc_protocol>`.
|
:doc:`remote control protocol specification <rc_protocol>`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _search_syntax:
|
||||||
|
|
||||||
|
Matching windows and tabs
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Many remote control operations operate on windows or tabs. To select these, the
|
||||||
|
:code:`--match` option is often used. This allows matching using various
|
||||||
|
sophisticated criteria such as title, ids, cmdlines, etc. These criteria are
|
||||||
|
expressions of the form :code:`field:query`. Where :italic:`field` is the field
|
||||||
|
against which to match and :italic:`query` is the expression to match. They can
|
||||||
|
be further combined using Boolean operators, best illustrated with some
|
||||||
|
examples::
|
||||||
|
|
||||||
|
title:"My special window" or id:43
|
||||||
|
title:bash and env:USER=kovid
|
||||||
|
not id:1
|
||||||
|
(id:2 or id:3) and title:something
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:hidden:
|
:hidden:
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ Shell integration
|
|||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
kitty has the ability to integrate closely within common shells, such as `zsh
|
kitty has the ability to integrate closely within common shells, such as `zsh
|
||||||
<https://www.zsh.org/>`_, `fish <https://fishshell.com>`_ and `bash
|
<https://www.zsh.org/>`__, `fish <https://fishshell.com>`__ and `bash
|
||||||
<https://www.gnu.org/software/bash/>`_ to enable features such as jumping to
|
<https://www.gnu.org/software/bash/>`__ to enable features such as jumping to
|
||||||
previous prompts in the scrollback, viewing the output of the last command in
|
previous prompts in the scrollback, viewing the output of the last command in
|
||||||
:program:`less`, using the mouse to move the cursor while editing prompts, etc.
|
:program:`less`, using the mouse to move the cursor while editing prompts, etc.
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ Features
|
|||||||
|
|
||||||
* Click with the mouse anywhere in the current command to move the cursor there
|
* Click with the mouse anywhere in the current command to move the cursor there
|
||||||
|
|
||||||
* Hold :kbd:`ctrl+shift` and right-click on any command output in the scrollback
|
* Hold :kbd:`Ctrl+Shift` and right-click on any command output in the scrollback
|
||||||
to view it in a pager
|
to view it in a pager
|
||||||
|
|
||||||
* The current working directory or the command being executed are automatically
|
* The current working directory or the command being executed are automatically
|
||||||
@@ -30,6 +30,11 @@ Features
|
|||||||
|
|
||||||
* The text cursor is changed to a bar when editing commands at the shell prompt
|
* The text cursor is changed to a bar when editing commands at the shell prompt
|
||||||
|
|
||||||
|
* :ref:`clone_shell` with all environment variables and the working directory
|
||||||
|
copied
|
||||||
|
|
||||||
|
* :ref:`Edit files in new kitty windows <edit_file>` even over SSH
|
||||||
|
|
||||||
* Glitch free window resizing even with complex prompts. Achieved by erasing
|
* Glitch free window resizing even with complex prompts. Achieved by erasing
|
||||||
the prompt on resize and allowing the shell to redraw it cleanly.
|
the prompt on resize and allowing the shell to redraw it cleanly.
|
||||||
|
|
||||||
@@ -43,7 +48,7 @@ Configuration
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
Shell integration is controlled by the :opt:`shell_integration` option. By
|
Shell integration is controlled by the :opt:`shell_integration` option. By
|
||||||
default, all shell integration is enabled. Individual features can be turned
|
default, all integration features are enabled. Individual features can be turned
|
||||||
off or it can be disabled entirely as well. The :opt:`shell_integration` option
|
off or it can be disabled entirely as well. The :opt:`shell_integration` option
|
||||||
takes a space separated list of keywords:
|
takes a space separated list of keywords:
|
||||||
|
|
||||||
@@ -53,26 +58,28 @@ disabled
|
|||||||
:ref:`manual integration <manual_shell_integration>`.
|
:ref:`manual integration <manual_shell_integration>`.
|
||||||
|
|
||||||
no-rc
|
no-rc
|
||||||
Do not modify the shell's launch environment to enable integration. Useful if you prefer
|
Do not modify the shell's launch environment to enable integration. Useful
|
||||||
to load the kitty shell integration code yourself, either as part of
|
if you prefer to load the kitty shell integration code yourself, either as
|
||||||
:ref:`manually integration <manual_shell_integration>` or because you have
|
part of :ref:`manually integration <manual_shell_integration>` or because
|
||||||
some other software that sets up shell integration.
|
you have some other software that sets up shell integration.
|
||||||
This will still set the :envvar:`KITTY_SHELL_INTEGRATION` environment variable when kitty runs the shell.
|
This will still set the :envvar:`KITTY_SHELL_INTEGRATION` environment
|
||||||
|
variable when kitty runs the shell.
|
||||||
|
|
||||||
no-cursor
|
no-cursor
|
||||||
Turn off changing of the text cursor to a bar when editing text
|
Turn off changing of the text cursor to a bar when editing shell command
|
||||||
|
line.
|
||||||
|
|
||||||
no-title
|
no-title
|
||||||
Turn off setting the kitty window/tab title based on shell state.
|
Turn off setting the kitty window/tab title based on shell state.
|
||||||
Note that for the ``fish`` shell kitty relies on fish's native title
|
Note that for the fish shell kitty relies on fish's native title setting
|
||||||
setting functionality instead.
|
functionality instead.
|
||||||
|
|
||||||
no-cwd
|
no-cwd
|
||||||
Turn off reporting the current working directory. This is used to allow
|
Turn off reporting the current working directory. This is used to allow
|
||||||
:ac:`new_window_with_cwd` and similar to open windows logged
|
:ac:`new_window_with_cwd` and similar to open windows logged into remote
|
||||||
into remote machines using the :doc:`ssh kitten <kittens/ssh>`
|
machines using the :doc:`ssh kitten <kittens/ssh>` automatically with the
|
||||||
automatically with the same working directory as the current window.
|
same working directory as the current window.
|
||||||
Note that for the ``fish`` shell this will not disable its built-in current
|
Note that for the fish shell this will not disable its built-in current
|
||||||
working directory reporting.
|
working directory reporting.
|
||||||
|
|
||||||
no-prompt-mark
|
no-prompt-mark
|
||||||
@@ -81,16 +88,16 @@ no-prompt-mark
|
|||||||
|
|
||||||
no-complete
|
no-complete
|
||||||
Turn off completion for the kitty command.
|
Turn off completion for the kitty command.
|
||||||
Note that for the ``fish`` shell this does not take effect, since fish
|
Note that for the fish shell this does not take effect, since fish already
|
||||||
already comes with a kitty completion script.
|
comes with a kitty completion script.
|
||||||
|
|
||||||
|
|
||||||
More ways to browse command output
|
More ways to browse command output
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
You can add further key and mouse bindings to browse the output of commands
|
You can add further key and mouse bindings to browse the output of commands
|
||||||
easily. For example to select the output of a command by right clicking the mouse
|
easily. For example to select the output of a command by right clicking the
|
||||||
on the output, define the following in :file:`kitty.conf`:
|
mouse on the output, define the following in :file:`kitty.conf`:
|
||||||
|
|
||||||
.. code:: conf
|
.. code:: conf
|
||||||
|
|
||||||
@@ -101,8 +108,8 @@ to be copied.
|
|||||||
|
|
||||||
The feature to jump to previous prompts (
|
The feature to jump to previous prompts (
|
||||||
:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) and mouse
|
:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) and mouse
|
||||||
actions (:ac:`mouse_select_command_output` and :ac:`mouse_show_command_output`) can
|
actions (:ac:`mouse_select_command_output` and :ac:`mouse_show_command_output`)
|
||||||
be integrated with browsing command output as well. For example, define the
|
can be integrated with browsing command output as well. For example, define the
|
||||||
following mapping in :file:`kitty.conf`:
|
following mapping in :file:`kitty.conf`:
|
||||||
|
|
||||||
.. code:: conf
|
.. code:: conf
|
||||||
@@ -134,34 +141,34 @@ How it works
|
|||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
At startup, kitty detects if the shell you have configured (either system wide
|
At startup, kitty detects if the shell you have configured (either system wide
|
||||||
or in kitty.conf) is a supported shell. If so, kitty injects some shell specific
|
or the :opt:`shell` option in :file:`kitty.conf`) is a supported shell. If so,
|
||||||
code into the shell, to enable shell integration. How it does so varies for
|
kitty injects some shell specific code into the shell, to enable shell
|
||||||
different shells.
|
integration. How it does so varies for different shells.
|
||||||
|
|
||||||
|
|
||||||
.. tab:: zsh
|
.. tab:: zsh
|
||||||
|
|
||||||
For zsh, kitty sets the ``ZDOTDIR`` environment variable to make zsh load
|
For zsh, kitty sets the :envvar:`ZDOTDIR` environment variable to make zsh
|
||||||
kitty's :file:`.zshenv` which restores the original value of ``ZDOTDIR``
|
load kitty's :file:`.zshenv` which restores the original value of
|
||||||
and sources the original :file:`.zshenv`. It then loads the shell integration code.
|
:envvar:`ZDOTDIR` and sources the original :file:`.zshenv`. It then loads
|
||||||
The remainder of zsh's startup process proceeds as normal.
|
the shell integration code. The remainder of zsh's startup process proceeds
|
||||||
|
as normal.
|
||||||
|
|
||||||
.. tab:: fish
|
.. tab:: fish
|
||||||
|
|
||||||
For fish, to make it automatically load the integration code provided by
|
For fish, to make it automatically load the integration code provided by
|
||||||
kitty, the integration script directory path is prepended to the
|
kitty, the integration script directory path is prepended to the
|
||||||
``XDG_DATA_DIRS`` environment variable. This is only applied to the fish
|
:envvar:`XDG_DATA_DIRS` environment variable. This is only applied to the
|
||||||
process and will be cleaned up by the integration script after startup. No files
|
fish process and will be cleaned up by the integration script after startup.
|
||||||
are added or modified.
|
No files are added or modified.
|
||||||
|
|
||||||
.. tab:: bash
|
.. tab:: bash
|
||||||
|
|
||||||
For bash, kitty starts bash in POSIX mode, using the environment variable
|
For bash, kitty starts bash in POSIX mode, using the environment variable
|
||||||
``ENV`` to load the shell integration script. This prevents bash from
|
:envvar:`ENV` to load the shell integration script. This prevents bash from
|
||||||
loading any startup files itself. The loading of the startup files is done
|
loading any startup files itself. The loading of the startup files is done
|
||||||
by the integration script, after disabling POSIX mode. From the
|
by the integration script, after disabling POSIX mode. From the perspective
|
||||||
perspective of those scripts there should be no difference to running
|
of those scripts there should be no difference to running vanilla bash.
|
||||||
vanilla bash.
|
|
||||||
|
|
||||||
|
|
||||||
Then, when launching the shell, kitty sets the environment variable
|
Then, when launching the shell, kitty sets the environment variable
|
||||||
@@ -215,6 +222,76 @@ shell integration. Alternately, you can :ref:`setup shell integration manually
|
|||||||
the remote server and editing the shell rc files there, as described below.
|
the remote server and editing the shell rc files there, as described below.
|
||||||
|
|
||||||
|
|
||||||
|
.. _clone_shell:
|
||||||
|
|
||||||
|
Clone the current shell into a new window
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
You can clone the current shell into a new kitty window by simply running the
|
||||||
|
:command:`clone-in-kitty` command, for example:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
clone-in-kitty
|
||||||
|
clone-in-kitty --type=tab
|
||||||
|
clone-in-kitty --title "I am a clone"
|
||||||
|
|
||||||
|
This will open a new window running a new shell instance but with all
|
||||||
|
environment variables and the current working directory copied. This even works
|
||||||
|
over SSH when using :doc:`kittens/ssh`.
|
||||||
|
|
||||||
|
The :command:`clone-in-kitty` command takes almost all the same arguments as the
|
||||||
|
:doc:`launch <launch>` command, so you can open a new tab instead or a new OS
|
||||||
|
window, etc. Arguments of launch that can cause code execution or that don't
|
||||||
|
make sense when cloning are ignored. Most prominently, the following options are
|
||||||
|
ignored: :option:`--allow-remote-control <launch --allow-remote-control>`,
|
||||||
|
:option:`--copy-cmdline <launch --copy-cmdline>`, :option:`--copy-env <launch
|
||||||
|
--copy-env>`, :option:`--stdin-source <launch --stdin-source>`,
|
||||||
|
:option:`--marker <launch --marker>` and :option:`--watcher <launch --watcher>`.
|
||||||
|
|
||||||
|
:command:`clone-in-kitty` can be configured to source arbitrary code in the
|
||||||
|
cloned window using environment variables. It will automatically clone virtual
|
||||||
|
environments created by the :link:`Python venv module
|
||||||
|
<https://docs.python.org/3/library/venv.html>` or :link:`Conda
|
||||||
|
<https://conda.io/>`. In addition, setting the
|
||||||
|
env var :envvar:`KITTY_CLONE_SOURCE_CODE` to some shell code will cause that
|
||||||
|
code to be run in the cloned window with :code:`eval`. Similarly, setting
|
||||||
|
:envvar:`KITTY_CLONE_SOURCE_PATH` to the path of a file will cause that file to
|
||||||
|
be sourced in the cloned window. This can be controlled by
|
||||||
|
:opt:`clone_source_strategies`.
|
||||||
|
|
||||||
|
:command:`clone-in-kitty` works by asking the shell to serialize its internal
|
||||||
|
state (mainly CWD and env vars) and this state is transmitted to kitty and
|
||||||
|
restored by the shell integration scripts in the cloned window.
|
||||||
|
|
||||||
|
|
||||||
|
.. _edit_file:
|
||||||
|
|
||||||
|
Edit files in new kitty windows even over SSH
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
edit-in-kitty myfile.txt
|
||||||
|
edit-in-kitty --type tab --title "Editing My File" myfile.txt
|
||||||
|
# open file.txt at line 75 (works with vim, emacs, nano, micro)
|
||||||
|
edit-in-kitty +75 file.txt
|
||||||
|
|
||||||
|
The :command:`edit-in-kitty` command allows you to seamlessly edit files
|
||||||
|
in your default :opt:`editor` in new kitty windows. This works even over
|
||||||
|
SSH (if you use the :doc:`ssh kitten <kittens/ssh>`), allowing you
|
||||||
|
to easily edit remote files in your local editor with all its bells and
|
||||||
|
whistles.
|
||||||
|
|
||||||
|
The :command:`edit-in-kitty` command takes almost all the same arguments as the
|
||||||
|
:doc:`launch <launch>` command, so you can open a new tab instead or a new OS
|
||||||
|
window, etc. Not all arguments are supported, see the discussion in the
|
||||||
|
:ref:`clone_shell` section above.
|
||||||
|
|
||||||
|
In order to avoid remote code execution, kitty will only execute the configured
|
||||||
|
editor and pass the file path to edit to it.
|
||||||
|
|
||||||
|
|
||||||
.. _manual_shell_integration:
|
.. _manual_shell_integration:
|
||||||
|
|
||||||
Manual shell integration
|
Manual shell integration
|
||||||
@@ -276,9 +353,10 @@ to point to the location of the scripts.
|
|||||||
Integration with other shells
|
Integration with other shells
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
There exist third-party integrations to use these features for various other shells:
|
There exist third-party integrations to use these features for various other
|
||||||
|
shells:
|
||||||
|
|
||||||
* Jupyter console via a patch (:iss:`4475`)
|
* Jupyter console and IPython via a patch (:iss:`4475`)
|
||||||
* `xonsh <https://github.com/xonsh/xonsh/issues/4623>`__
|
* `xonsh <https://github.com/xonsh/xonsh/issues/4623>`__
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
Colored and styled underlines
|
Colored and styled underlines
|
||||||
================================
|
================================
|
||||||
|
|
||||||
|kitty| supports colored and styled (wavy) underlines. This is of particular
|
|kitty| supports colored and styled (wavy) underlines. This is of particular use
|
||||||
use in terminal editors such as vim and emacs to display red, wavy underlines
|
in terminal based text editors such as :program:`vim` and :program:`emacs` to
|
||||||
under mis-spelled words and/or syntax errors. This is done by re-purposing some
|
display red, wavy underlines under mis-spelled words and/or syntax errors. This
|
||||||
SGR escape codes that are not used in modern terminals (`CSI codes
|
is done by re-purposing some SGR escape codes that are not used in modern
|
||||||
<https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences>`_)
|
terminals (`CSI codes <https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences>`__)
|
||||||
|
|
||||||
To set the underline style::
|
To set the underline style::
|
||||||
|
|
||||||
<ESC>[4:0m # this is no underline
|
<ESC>[4:0m # no underline
|
||||||
<ESC>[4:1m # this is a straight underline
|
<ESC>[4:1m # straight underline
|
||||||
<ESC>[4:2m # this is a double underline
|
<ESC>[4:2m # double underline
|
||||||
<ESC>[4:3m # this is a curly underline
|
<ESC>[4:3m # curly underline
|
||||||
<ESC>[4:4m # this is a dotted underline
|
<ESC>[4:4m # dotted underline
|
||||||
<ESC>[4:5m # this is a dashed underline
|
<ESC>[4:5m # dashed underline
|
||||||
<ESC>[4m # this is a straight underline (for backwards compat)
|
<ESC>[4m # straight underline (for backwards compat)
|
||||||
<ESC>[24m # this is no underline (for backwards compat)
|
<ESC>[24m # no underline (for backwards compat)
|
||||||
|
|
||||||
To set the underline color (this is reserved and as far as I can tell not actually used for anything)::
|
To set the underline color (this is reserved and as far as I can tell not
|
||||||
|
actually used for anything)::
|
||||||
|
|
||||||
<ESC>[58...m
|
<ESC>[58...m
|
||||||
|
|
||||||
@@ -29,8 +30,8 @@ To reset the underline color (also previously reserved and unused)::
|
|||||||
|
|
||||||
<ESC>[59m
|
<ESC>[59m
|
||||||
|
|
||||||
The underline color must remain the same under reverse video, if it has a
|
The underline color must remain the same under reverse video, if it has a color,
|
||||||
color, if not, it should follow the foreground color.
|
if not, it should follow the foreground color.
|
||||||
|
|
||||||
To detect support for this feature in a terminal emulator, query the terminfo database
|
To detect support for this feature in a terminal emulator, query the terminfo
|
||||||
for the ``Su`` boolean capability.
|
database for the ``Su`` boolean capability.
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ Unscrolling the screen
|
|||||||
========================
|
========================
|
||||||
|
|
||||||
This is a small extension to the `SD (Pan up) escape code
|
This is a small extension to the `SD (Pan up) escape code
|
||||||
<https://vt100.net/docs/vt510-rm/SD.html>`_ from the VT-420 terminal. The
|
<https://vt100.net/docs/vt510-rm/SD.html>`_ from the VT-420 terminal. The ``SD``
|
||||||
``SD`` escape code normally causes the text on screen to scroll down by the
|
escape code normally causes the text on screen to scroll down by the specified
|
||||||
specified number of lines, with empty lines appearing at the top of the screen.
|
number of lines, with empty lines appearing at the top of the screen. This
|
||||||
This extension allows the new lines to be filled in from the scrollback buffer
|
extension allows the new lines to be filled in from the scrollback buffer
|
||||||
instead of being blank.
|
instead of being blank.
|
||||||
|
|
||||||
The motivation for this is that many modern shells will show completions in a
|
The motivation for this is that many modern shells will show completions in a
|
||||||
@@ -22,17 +22,17 @@ scrolled down is implementation defined, but must be at least one screen worth.
|
|||||||
|
|
||||||
The syntax of the escape code is identical to that of ``SD`` except that it has
|
The syntax of the escape code is identical to that of ``SD`` except that it has
|
||||||
a trailing ``+`` modifier. This is legal under the `ECMA 48 standard
|
a trailing ``+`` modifier. This is legal under the `ECMA 48 standard
|
||||||
<https://www.ecma-international.org/publications-and-standards/standards/ecma-48/>`_
|
<https://www.ecma-international.org/publications-and-standards/standards/ecma-48/>`__
|
||||||
and unused for any other purpose as far as I can tell. So for example, to
|
and unused for any other purpose as far as I can tell. So for example, to
|
||||||
unscroll three lines, the escape code would be::
|
unscroll three lines, the escape code would be::
|
||||||
|
|
||||||
CSI 3 + T
|
CSI 3 + T
|
||||||
|
|
||||||
See `discussion here
|
See `discussion here
|
||||||
<https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/30>`_.
|
<https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/30>`__.
|
||||||
|
|
||||||
.. versionadded:: 0.20.2
|
.. versionadded:: 0.20.2
|
||||||
|
|
||||||
Also supported by the terminals:
|
Also supported by the terminals:
|
||||||
|
|
||||||
* `mintty <https://github.com/mintty/mintty/releases/tag/3.5.2>`_
|
* `mintty <https://github.com/mintty/mintty/releases/tag/3.5.2>`__
|
||||||
|
|||||||
@@ -1731,7 +1731,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
|||||||
|
|
||||||
if (!createNativeWindow(window, wndconfig, fbconfig))
|
if (!createNativeWindow(window, wndconfig, fbconfig))
|
||||||
return false;
|
return false;
|
||||||
[window->ns.object setColorSpace:[NSColorSpace sRGBColorSpace]];
|
switch((GlfwCocoaColorSpaces)wndconfig->ns.color_space) {
|
||||||
|
case SRGB_COLORSPACE: [window->ns.object setColorSpace:[NSColorSpace sRGBColorSpace]]; break;
|
||||||
|
case DISPLAY_P3_COLORSPACE: [window->ns.object setColorSpace:[NSColorSpace displayP3ColorSpace]]; break;
|
||||||
|
case DEFAULT_COLORSPACE: break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctxconfig->client != GLFW_NO_API)
|
if (ctxconfig->client != GLFW_NO_API)
|
||||||
{
|
{
|
||||||
@@ -2478,25 +2482,35 @@ void _glfwPlatformSetClipboardString(const char* string)
|
|||||||
const char* _glfwPlatformGetClipboardString(void)
|
const char* _glfwPlatformGetClipboardString(void)
|
||||||
{
|
{
|
||||||
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||||
|
free(_glfw.ns.clipboardString); _glfw.ns.clipboardString = NULL;
|
||||||
|
|
||||||
if (![[pasteboard types] containsObject:NSPasteboardTypeString])
|
NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
|
||||||
{
|
NSArray* objs = [pasteboard readObjectsForClasses:@[[NSURL class], [NSString class]] options:options];
|
||||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
if (objs) {
|
||||||
"Cocoa: Failed to retrieve string from pasteboard");
|
const NSUInteger count = [objs count];
|
||||||
return NULL;
|
if (count) {
|
||||||
|
NSMutableString *path_list = [NSMutableString stringWithCapacity:4096]; // auto-released
|
||||||
|
NSMutableString *text_list = [NSMutableString stringWithCapacity:4096]; // auto-released
|
||||||
|
for (NSUInteger i = 0; i < count; i++) {
|
||||||
|
id obj = objs[i];
|
||||||
|
if ([obj isKindOfClass:[NSURL class]]) {
|
||||||
|
NSURL *url = (NSURL*)obj;
|
||||||
|
if (url.fileURL && url.fileSystemRepresentation) {
|
||||||
|
if ([path_list length] > 0) [path_list appendString:@("\n")];
|
||||||
|
[path_list appendString:@(url.fileSystemRepresentation)];
|
||||||
|
}
|
||||||
|
} else if ([obj isKindOfClass:[NSString class]]) {
|
||||||
|
if ([text_list length] > 0) [text_list appendString:@("\n")];
|
||||||
|
[text_list appendString:obj];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *text = NULL;
|
||||||
|
if (path_list.length > 0) text = [path_list UTF8String];
|
||||||
|
else if (text_list.length > 0) text = [text_list UTF8String];
|
||||||
|
if (text) _glfw.ns.clipboardString = _glfw_strdup(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!_glfw.ns.clipboardString) _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to retrieve object from pasteboard");
|
||||||
NSString* object = [pasteboard stringForType:NSPasteboardTypeString];
|
|
||||||
if (!object)
|
|
||||||
{
|
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
||||||
"Cocoa: Failed to retrieve object from pasteboard");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(_glfw.ns.clipboardString);
|
|
||||||
_glfw.ns.clipboardString = _glfw_strdup([object UTF8String]);
|
|
||||||
|
|
||||||
return _glfw.ns.clipboardString;
|
return _glfw.ns.clipboardString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
glfw/glfw3.h
vendored
13
glfw/glfw3.h
vendored
@@ -1024,6 +1024,16 @@ typedef enum GLFWMouseButton {
|
|||||||
* [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint).
|
* [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint).
|
||||||
*/
|
*/
|
||||||
#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003
|
#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003
|
||||||
|
/*! @brief macOS specific
|
||||||
|
* [window hint](@ref GLFW_COCOA_COLOR_SPACE_hint).
|
||||||
|
*/
|
||||||
|
#define GLFW_COCOA_COLOR_SPACE 0x00023004
|
||||||
|
typedef enum {
|
||||||
|
DEFAULT_COLORSPACE = 0,
|
||||||
|
SRGB_COLORSPACE = 1,
|
||||||
|
DISPLAY_P3_COLORSPACE = 2,
|
||||||
|
} GlfwCocoaColorSpaces;
|
||||||
|
|
||||||
/*! @brief X11 specific
|
/*! @brief X11 specific
|
||||||
* [window hint](@ref GLFW_X11_CLASS_NAME_hint).
|
* [window hint](@ref GLFW_X11_CLASS_NAME_hint).
|
||||||
*/
|
*/
|
||||||
@@ -1206,7 +1216,8 @@ typedef enum {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
GLFW_IME_NONE,
|
GLFW_IME_NONE,
|
||||||
GLFW_IME_PREEDIT_CHANGED,
|
GLFW_IME_PREEDIT_CHANGED,
|
||||||
GLFW_IME_COMMIT_TEXT
|
GLFW_IME_COMMIT_TEXT,
|
||||||
|
GLFW_IME_WAYLAND_DONE_EVENT,
|
||||||
} GLFWIMEState;
|
} GLFWIMEState;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
1
glfw/internal.h
vendored
1
glfw/internal.h
vendored
@@ -307,6 +307,7 @@ struct _GLFWwndconfig
|
|||||||
bool scaleToMonitor;
|
bool scaleToMonitor;
|
||||||
struct {
|
struct {
|
||||||
bool retina;
|
bool retina;
|
||||||
|
int color_space;
|
||||||
char frameName[256];
|
char frameName[256];
|
||||||
} ns;
|
} ns;
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
5
glfw/window.c
vendored
5
glfw/window.c
vendored
@@ -332,6 +332,8 @@ void glfwDefaultWindowHints(void)
|
|||||||
|
|
||||||
// The default is to use full Retina resolution framebuffers
|
// The default is to use full Retina resolution framebuffers
|
||||||
_glfw.hints.window.ns.retina = true;
|
_glfw.hints.window.ns.retina = true;
|
||||||
|
// use the default colorspace assigned by the system
|
||||||
|
_glfw.hints.window.ns.color_space = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWAPI void glfwWindowHint(int hint, int value)
|
GLFWAPI void glfwWindowHint(int hint, int value)
|
||||||
@@ -412,6 +414,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
|
|||||||
case GLFW_COCOA_RETINA_FRAMEBUFFER:
|
case GLFW_COCOA_RETINA_FRAMEBUFFER:
|
||||||
_glfw.hints.window.ns.retina = value ? true : false;
|
_glfw.hints.window.ns.retina = value ? true : false;
|
||||||
return;
|
return;
|
||||||
|
case GLFW_COCOA_COLOR_SPACE:
|
||||||
|
_glfw.hints.window.ns.color_space = value;
|
||||||
|
return;
|
||||||
case GLFW_COCOA_GRAPHICS_SWITCHING:
|
case GLFW_COCOA_GRAPHICS_SWITCHING:
|
||||||
_glfw.hints.context.nsgl.offline = value ? true : false;
|
_glfw.hints.context.nsgl.offline = value ? true : false;
|
||||||
return;
|
return;
|
||||||
|
|||||||
32
glfw/wl_client_side_decorations.c
vendored
32
glfw/wl_client_side_decorations.c
vendored
@@ -102,13 +102,31 @@ init_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, unsi
|
|||||||
return 2 * pair->size_in_bytes;
|
return 2 * pair->size_in_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
window_has_buffer(_GLFWwindow *window, struct wl_buffer *q) {
|
||||||
|
#define Q(which) decs.which.buffer.a == q || decs.which.buffer.b == q
|
||||||
|
return Q(left) || Q(top) || Q(right) || Q(bottom);
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
alloc_buffer_pair(_GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) {
|
buffer_release_event(void *data, struct wl_buffer *buffer) {
|
||||||
|
wl_buffer_destroy(buffer);
|
||||||
|
_GLFWwindow *window = _glfwWindowForId((uintptr_t)data);
|
||||||
|
if (window && window_has_buffer(window, buffer)) decs.buffer_destroyed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wl_buffer_listener handle_buffer_events = {.release = buffer_release_event};
|
||||||
|
|
||||||
|
static void
|
||||||
|
alloc_buffer_pair(uintptr_t window_id, _GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) {
|
||||||
pair->data.a = data + *offset;
|
pair->data.a = data + *offset;
|
||||||
pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
|
pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
|
||||||
|
wl_buffer_add_listener(pair->a, &handle_buffer_events, (void*)window_id);
|
||||||
*offset += pair->size_in_bytes;
|
*offset += pair->size_in_bytes;
|
||||||
pair->data.b = data + *offset;
|
pair->data.b = data + *offset;
|
||||||
pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
|
pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
|
||||||
|
wl_buffer_add_listener(pair->b, &handle_buffer_events, (void*)window_id);
|
||||||
*offset += pair->size_in_bytes;
|
*offset += pair->size_in_bytes;
|
||||||
pair->front = pair->a; pair->back = pair->b;
|
pair->front = pair->a; pair->back = pair->b;
|
||||||
pair->data.front = pair->data.a; pair->data.back = pair->data.b;
|
pair->data.front = pair->data.a; pair->data.back = pair->data.b;
|
||||||
@@ -287,7 +305,7 @@ create_shm_buffers(_GLFWwindow* window) {
|
|||||||
struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size);
|
struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size);
|
||||||
close(fd);
|
close(fd);
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
#define a(which) alloc_buffer_pair(&decs.which.buffer, pool, decs.mapping.data, &offset)
|
#define a(which) alloc_buffer_pair(window->id, &decs.which.buffer, pool, decs.mapping.data, &offset)
|
||||||
a(top); a(left); a(bottom); a(right);
|
a(top); a(left); a(bottom); a(right);
|
||||||
#undef a
|
#undef a
|
||||||
wl_shm_pool_destroy(pool);
|
wl_shm_pool_destroy(pool);
|
||||||
@@ -313,8 +331,6 @@ free_csd_surfaces(_GLFWwindow *window) {
|
|||||||
static void
|
static void
|
||||||
free_csd_buffers(_GLFWwindow *window) {
|
free_csd_buffers(_GLFWwindow *window) {
|
||||||
#define d(which) { \
|
#define d(which) { \
|
||||||
if (decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \
|
|
||||||
if (decs.which.buffer.b) wl_buffer_destroy(decs.which.buffer.b); \
|
|
||||||
memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \
|
memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \
|
||||||
}
|
}
|
||||||
d(left); d(top); d(right); d(bottom);
|
d(left); d(top); d(right); d(bottom);
|
||||||
@@ -352,12 +368,14 @@ ensure_csd_resources(_GLFWwindow *window) {
|
|||||||
decs.for_window_state.scale != window->wl.scale ||
|
decs.for_window_state.scale != window->wl.scale ||
|
||||||
!decs.mapping.data
|
!decs.mapping.data
|
||||||
);
|
);
|
||||||
const bool needs_update = focus_changed || size_changed || !decs.left.surface;
|
const bool needs_update = focus_changed || size_changed || !decs.left.surface || decs.buffer_destroyed;
|
||||||
debug("CSD: old.size: %dx%d new.size: %dx%d needs_update: %d size_changed: %d\n", decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update, size_changed);
|
debug("CSD: old.size: %dx%d new.size: %dx%d needs_update: %d size_changed: %d buffer_destroyed: %d\n",
|
||||||
|
decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update, size_changed, decs.buffer_destroyed);
|
||||||
if (!needs_update) return false;
|
if (!needs_update) return false;
|
||||||
if (size_changed) {
|
if (size_changed || decs.buffer_destroyed) {
|
||||||
free_csd_buffers(window);
|
free_csd_buffers(window);
|
||||||
if (!create_shm_buffers(window)) return false;
|
if (!create_shm_buffers(window)) return false;
|
||||||
|
decs.buffer_destroyed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t x, y, scale = window->wl.scale < 1 ? 1 : window->wl.scale;
|
int32_t x, y, scale = window->wl.scale < 1 ? 1 : window->wl.scale;
|
||||||
|
|||||||
14
glfw/wl_init.c
vendored
14
glfw/wl_init.c
vendored
@@ -143,10 +143,15 @@ static void setCursor(GLFWCursorShape shape, _GLFWwindow* window)
|
|||||||
|
|
||||||
if (!image)
|
if (!image)
|
||||||
return;
|
return;
|
||||||
|
if (image->width % scale || image->height % scale) {
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "WARNING: Cursor image size: %dx%d is not a multiple of window scale: %d. This will"
|
||||||
|
" cause some compositors such as GNOME to crash. See https://github.com/kovidgoyal/kitty/issues/4878", image->width, image->height, scale);
|
||||||
|
}
|
||||||
|
|
||||||
buffer = wl_cursor_image_get_buffer(image);
|
buffer = wl_cursor_image_get_buffer(image);
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return;
|
return;
|
||||||
|
debug("Calling wl_pointer_set_cursor in setCursor with surface: %p\n", (void*)surface);
|
||||||
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
|
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
|
||||||
surface,
|
surface,
|
||||||
image->hotspot_x / scale,
|
image->hotspot_x / scale,
|
||||||
@@ -568,6 +573,10 @@ static void seatHandleCapabilities(void* data UNUSED,
|
|||||||
{
|
{
|
||||||
wl_pointer_destroy(_glfw.wl.pointer);
|
wl_pointer_destroy(_glfw.wl.pointer);
|
||||||
_glfw.wl.pointer = NULL;
|
_glfw.wl.pointer = NULL;
|
||||||
|
if (_glfw.wl.cursorAnimationTimer) {
|
||||||
|
removeTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer);
|
||||||
|
_glfw.wl.cursorAnimationTimer = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
|
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
|
||||||
@@ -579,6 +588,11 @@ static void seatHandleCapabilities(void* data UNUSED,
|
|||||||
{
|
{
|
||||||
wl_keyboard_destroy(_glfw.wl.keyboard);
|
wl_keyboard_destroy(_glfw.wl.keyboard);
|
||||||
_glfw.wl.keyboard = NULL;
|
_glfw.wl.keyboard = NULL;
|
||||||
|
_glfw.wl.keyboardFocusId = 0;
|
||||||
|
if (_glfw.wl.keyRepeatInfo.keyRepeatTimer) {
|
||||||
|
removeTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer);
|
||||||
|
_glfw.wl.keyRepeatInfo.keyRepeatTimer = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
glfw/wl_platform.h
vendored
2
glfw/wl_platform.h
vendored
@@ -172,7 +172,7 @@ typedef struct _GLFWwindowWayland
|
|||||||
struct zwp_idle_inhibitor_v1* idleInhibitor;
|
struct zwp_idle_inhibitor_v1* idleInhibitor;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool serverSide;
|
bool serverSide, buffer_destroyed;
|
||||||
_GLFWdecorationSideWayland focus;
|
_GLFWdecorationSideWayland focus;
|
||||||
_GLFWWaylandCSDEdge top, left, right, bottom;
|
_GLFWWaylandCSDEdge top, left, right, bottom;
|
||||||
|
|
||||||
|
|||||||
4
glfw/wl_text_input.c
vendored
4
glfw/wl_text_input.c
vendored
@@ -84,7 +84,7 @@ text_input_delete_surrounding_text(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
text_input_done(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, uint32_t serial UNUSED) {
|
text_input_done(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, uint32_t serial) {
|
||||||
debug("text-input: done event: serial: %u current_commit_serial: %u\n", serial, commit_serial);
|
debug("text-input: done event: serial: %u current_commit_serial: %u\n", serial, commit_serial);
|
||||||
if (serial != commit_serial) {
|
if (serial != commit_serial) {
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: text_input_done serial mismatch, expected=%u got=%u\n", commit_serial, serial);
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: text_input_done serial mismatch, expected=%u got=%u\n", commit_serial, serial);
|
||||||
@@ -95,7 +95,7 @@ text_input_done(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, u
|
|||||||
free(pending_pre_edit); pending_pre_edit = NULL;
|
free(pending_pre_edit); pending_pre_edit = NULL;
|
||||||
} else {
|
} else {
|
||||||
// Clear pre-edit text
|
// Clear pre-edit text
|
||||||
send_text(NULL, GLFW_IME_PREEDIT_CHANGED);
|
send_text(NULL, GLFW_IME_WAYLAND_DONE_EVENT);
|
||||||
}
|
}
|
||||||
if (pending_commit) {
|
if (pending_commit) {
|
||||||
send_text(pending_commit, GLFW_IME_COMMIT_TEXT);
|
send_text(pending_commit, GLFW_IME_COMMIT_TEXT);
|
||||||
|
|||||||
3
glfw/wl_window.c
vendored
3
glfw/wl_window.c
vendored
@@ -147,6 +147,7 @@ setCursorImage(_GLFWwindow* window, bool on_theme_change) {
|
|||||||
cursorWayland->yhot = image->hotspot_y;
|
cursorWayland->yhot = image->hotspot_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug("Calling wl_pointer_set_cursor in setCursorImage with surface: %p\n", (void*)surface);
|
||||||
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
|
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
|
||||||
surface,
|
surface,
|
||||||
cursorWayland->xhot / scale,
|
cursorWayland->xhot / scale,
|
||||||
@@ -1397,6 +1398,7 @@ static void lockPointer(_GLFWwindow* window)
|
|||||||
window->wl.pointerLock.relativePointer = relativePointer;
|
window->wl.pointerLock.relativePointer = relativePointer;
|
||||||
window->wl.pointerLock.lockedPointer = lockedPointer;
|
window->wl.pointerLock.lockedPointer = lockedPointer;
|
||||||
|
|
||||||
|
debug("Calling wl_pointer_set_cursor in lockPointer with surface: %p\n", NULL);
|
||||||
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
|
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
|
||||||
NULL, 0, 0);
|
NULL, 0, 0);
|
||||||
}
|
}
|
||||||
@@ -1433,6 +1435,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
|||||||
}
|
}
|
||||||
else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
|
else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
|
||||||
{
|
{
|
||||||
|
debug("Calling wl_pointer_set_cursor in _glfwPlatformSetCursor with surface: %p\n", NULL);
|
||||||
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0);
|
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,17 +92,17 @@ be used for completions and via the browse history readline bindings.
|
|||||||
--choice -c
|
--choice -c
|
||||||
type=list
|
type=list
|
||||||
dest=choices
|
dest=choices
|
||||||
A choice for the choices type. Every choice has the syntax: letter:text Where
|
A choice for the choices type. Can be specified multiple times. Every choice has
|
||||||
letter is the accelerator key and text is the corresponding text. There can be
|
the syntax: ``letter[;color]:text``. Where :italic:`letter` is the accelerator key
|
||||||
an optional color specification after the letter to indicate what color it should
|
and :italic:`text` is the corresponding text. There can be an optional color
|
||||||
be.
|
specification after the letter to indicate what color it should be.
|
||||||
For example: y:Yes and n;red:No
|
For example: :code:`y:Yes` and :code:`n;red:No`
|
||||||
|
|
||||||
|
|
||||||
--default -d
|
--default -d
|
||||||
A default choice or text. If unspecified, it is "y" for :code:`yesno`, the first choice
|
A default choice or text. If unspecified, it is :code:`y` for the type
|
||||||
for :code:`choices` and empty for others. The default choice is selected when the user
|
:code:`yesno`, the first choice for :code:`choices` and empty for others types.
|
||||||
presses the Enter key.
|
The default choice is selected when the user presses the :kbd:`Enter` key.
|
||||||
|
|
||||||
|
|
||||||
--prompt -p
|
--prompt -p
|
||||||
@@ -406,7 +406,7 @@ def main(args: List[str]) -> Response:
|
|||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
if e.code != 0:
|
if e.code != 0:
|
||||||
print(e.args[0])
|
print(e.args[0])
|
||||||
input('Press enter to quit...')
|
input('Press Enter to quit')
|
||||||
raise SystemExit(e.code)
|
raise SystemExit(e.code)
|
||||||
|
|
||||||
if cli_opts.type in ('yesno', 'choices'):
|
if cli_opts.type in ('yesno', 'choices'):
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class Broadcast(Handler):
|
|||||||
|
|
||||||
|
|
||||||
OPTIONS = (MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')).format
|
OPTIONS = (MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')).format
|
||||||
help_text = 'Broadcast typed text to all kitty windows. By default text is sent to all windows, unless one of the matching options is specified'
|
help_text = 'Broadcast typed text to kitty windows. By default text is sent to all windows, unless one of the matching options is specified'
|
||||||
usage = '[initial text to send ...]'
|
usage = '[initial text to send ...]'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ OPTIONS = r'''
|
|||||||
default=False
|
default=False
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Output the current contents of the clipboard to STDOUT. Note that by default
|
Output the current contents of the clipboard to STDOUT. Note that by default
|
||||||
kitty will prompt you asking to allow access to the clipboard. Can be controlled
|
kitty will prompt for permission to access the clipboard. Can be controlled
|
||||||
by :opt:`clipboard_control`.
|
by :opt:`clipboard_control`.
|
||||||
|
|
||||||
|
|
||||||
@@ -93,7 +93,10 @@ def main(args: List[str]) -> NoReturn:
|
|||||||
data: Optional[bytes] = None
|
data: Optional[bytes] = None
|
||||||
if not sys.stdin.isatty():
|
if not sys.stdin.isatty():
|
||||||
data = sys.stdin.buffer.read()
|
data = sys.stdin.buffer.read()
|
||||||
sys.stdin = open(os.ctermid())
|
try:
|
||||||
|
sys.stdin = open(os.ctermid())
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise SystemExit('Not connected to a controlling terminal device, no /dev/tty')
|
||||||
loop = Loop()
|
loop = Loop()
|
||||||
handler = Clipboard(data, cli_opts)
|
handler = Clipboard(data, cli_opts)
|
||||||
loop.loop(handler)
|
loop.loop(handler)
|
||||||
|
|||||||
38
kittens/diff/collect.py
Normal file → Executable file
38
kittens/diff/collect.py
Normal file → Executable file
@@ -4,10 +4,14 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
from fnmatch import fnmatch
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Union
|
||||||
|
)
|
||||||
|
|
||||||
from kitty.guess_mime_type import guess_type
|
from kitty.guess_mime_type import guess_type
|
||||||
from typing import TYPE_CHECKING, Dict, List, Set, Optional, Iterator, Tuple, Union
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .highlight import DiffHighlight # noqa
|
from .highlight import DiffHighlight # noqa
|
||||||
@@ -37,6 +41,8 @@ class Segment:
|
|||||||
|
|
||||||
class Collection:
|
class Collection:
|
||||||
|
|
||||||
|
ignore_names: Tuple[str, ...] = ()
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.changes: Dict[str, str] = {}
|
self.changes: Dict[str, str] = {}
|
||||||
self.renames: Dict[str, str] = {}
|
self.renames: Dict[str, str] = {}
|
||||||
@@ -105,22 +111,34 @@ def resolve_remote_name(path: str, default: str) -> str:
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def allowed_items(items: Sequence[str], ignore_patterns: Sequence[str]) -> Iterator[str]:
|
||||||
|
for name in items:
|
||||||
|
for pat in ignore_patterns:
|
||||||
|
if fnmatch(name, pat):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
yield name
|
||||||
|
|
||||||
|
|
||||||
|
def walk(base: str, names: Set[str], pmap: Dict[str, str], ignore_names: Tuple[str, ...]) -> None:
|
||||||
|
for dirpath, dirnames, filenames in os.walk(base):
|
||||||
|
dirnames[:] = allowed_items(dirnames, ignore_names)
|
||||||
|
for filename in allowed_items(filenames, ignore_names):
|
||||||
|
path = os.path.abspath(os.path.join(dirpath, filename))
|
||||||
|
path_name_map[path] = name = os.path.relpath(path, base)
|
||||||
|
names.add(name)
|
||||||
|
pmap[name] = path
|
||||||
|
|
||||||
|
|
||||||
def collect_files(collection: Collection, left: str, right: str) -> None:
|
def collect_files(collection: Collection, left: str, right: str) -> None:
|
||||||
left_names: Set[str] = set()
|
left_names: Set[str] = set()
|
||||||
right_names: Set[str] = set()
|
right_names: Set[str] = set()
|
||||||
left_path_map: Dict[str, str] = {}
|
left_path_map: Dict[str, str] = {}
|
||||||
right_path_map: Dict[str, str] = {}
|
right_path_map: Dict[str, str] = {}
|
||||||
|
|
||||||
def walk(base: str, names: Set[str], pmap: Dict[str, str]) -> None:
|
walk(left, left_names, left_path_map, collection.ignore_names)
|
||||||
for dirpath, dirnames, filenames in os.walk(base):
|
walk(right, right_names, right_path_map, collection.ignore_names)
|
||||||
for filename in filenames:
|
|
||||||
path = os.path.abspath(os.path.join(dirpath, filename))
|
|
||||||
path_name_map[path] = name = os.path.relpath(path, base)
|
|
||||||
names.add(name)
|
|
||||||
pmap[name] = path
|
|
||||||
|
|
||||||
walk(left, left_names, left_path_map)
|
|
||||||
walk(right, right_names, right_path_map)
|
|
||||||
common_names = left_names & right_names
|
common_names = left_names & right_names
|
||||||
changed_names = {n for n in common_names if data_for_path(left_path_map[n]) != data_for_path(right_path_map[n])}
|
changed_names = {n for n in common_names if data_for_path(left_path_map[n]) != data_for_path(right_path_map[n])}
|
||||||
for n in changed_names:
|
for n in changed_names:
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ def highlight_collection(collection: Collection, aliases: Optional[Dict[str, str
|
|||||||
if p:
|
if p:
|
||||||
is_binary = isinstance(data_for_path(p), bytes)
|
is_binary = isinstance(data_for_path(p), bytes)
|
||||||
if not is_binary:
|
if not is_binary:
|
||||||
jobs[executor.submit(highlight_for_diff, p, aliases)] = p
|
jobs[executor.submit(highlight_for_diff, p, aliases or {})] = p
|
||||||
for future in concurrent.futures.as_completed(jobs):
|
for future in concurrent.futures.as_completed(jobs):
|
||||||
path = jobs[future]
|
path = jobs[future]
|
||||||
try:
|
try:
|
||||||
|
|||||||
7
kittens/diff/main.py
Normal file → Executable file
7
kittens/diff/main.py
Normal file → Executable file
@@ -571,8 +571,8 @@ OPTIONS = partial('''\
|
|||||||
--context
|
--context
|
||||||
type=int
|
type=int
|
||||||
default=-1
|
default=-1
|
||||||
Number of lines of context to show between changes. Negative values
|
Number of lines of context to show between changes. Negative values use the
|
||||||
use the number set in diff.conf
|
number set in :file:`diff.conf`.
|
||||||
|
|
||||||
|
|
||||||
--config
|
--config
|
||||||
@@ -599,7 +599,7 @@ class ShowWarning:
|
|||||||
|
|
||||||
|
|
||||||
showwarning = ShowWarning()
|
showwarning = ShowWarning()
|
||||||
help_text = 'Show a side-by-side diff of the specified files/directories. You can also use ssh:hostname:remote-file-path to diff remote files.'
|
help_text = 'Show a side-by-side diff of the specified files/directories. You can also use :italic:`ssh:hostname:remote-file-path` to diff remote files.'
|
||||||
usage = 'file_or_directory_left file_or_directory_right'
|
usage = 'file_or_directory_left file_or_directory_right'
|
||||||
|
|
||||||
|
|
||||||
@@ -655,6 +655,7 @@ def main(args: List[str]) -> None:
|
|||||||
opts = init_config(cli_opts)
|
opts = init_config(cli_opts)
|
||||||
set_diff_command(opts.diff_cmd)
|
set_diff_command(opts.diff_cmd)
|
||||||
lines_for_path.replace_tab_by = opts.replace_tab_by
|
lines_for_path.replace_tab_by = opts.replace_tab_by
|
||||||
|
Collection.ignore_names = tuple(opts.ignore_name)
|
||||||
left, right = map(get_remote_file, (left, right))
|
left, right = map(get_remote_file, (left, right))
|
||||||
if os.path.isdir(left) != os.path.isdir(right):
|
if os.path.isdir(left) != os.path.isdir(right):
|
||||||
raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.')
|
raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.')
|
||||||
|
|||||||
24
kittens/diff/options/definition.py
Normal file → Executable file
24
kittens/diff/options/definition.py
Normal file → Executable file
@@ -24,8 +24,8 @@ agr('diff', 'Diffing')
|
|||||||
opt('syntax_aliases', 'pyj:py pyi:py recipe:py',
|
opt('syntax_aliases', 'pyj:py pyi:py recipe:py',
|
||||||
option_type='syntax_aliases',
|
option_type='syntax_aliases',
|
||||||
long_text='''
|
long_text='''
|
||||||
File extension aliases for syntax highlight For example, to syntax highlight
|
File extension aliases for syntax highlight. For example, to syntax highlight
|
||||||
:file:`file.xyz` as :file:`file.abc` use a setting of :code:`xyz:abc`
|
:file:`file.xyz` as :file:`file.abc` use a setting of :code:`xyz:abc`.
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,8 +37,9 @@ opt('num_context_lines', '3',
|
|||||||
opt('diff_cmd', 'auto',
|
opt('diff_cmd', 'auto',
|
||||||
long_text='''
|
long_text='''
|
||||||
The diff command to use. Must contain the placeholder :code:`_CONTEXT_` which
|
The diff command to use. Must contain the placeholder :code:`_CONTEXT_` which
|
||||||
will be replaced by the number of lines of context. The default is to search the
|
will be replaced by the number of lines of context. The default special value
|
||||||
system for either git or diff and use that, if found.
|
:code:`auto` is to search the system for either :program:`git` or
|
||||||
|
:program:`diff` and use that, if found.
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,6 +47,21 @@ opt('replace_tab_by', '\\x20\\x20\\x20\\x20',
|
|||||||
option_type='python_string',
|
option_type='python_string',
|
||||||
long_text='The string to replace tabs with. Default is to use four spaces.'
|
long_text='The string to replace tabs with. Default is to use four spaces.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
opt('+ignore_name', '',
|
||||||
|
option_type='store_multiple',
|
||||||
|
add_to_default=False,
|
||||||
|
long_text='''
|
||||||
|
A glob pattern that is matched against only the filename of files and directories. Matching
|
||||||
|
files and directories are ignored when scanning the filesystem to look for files to diff.
|
||||||
|
Can be specified multiple times to use multiple patterns. For example::
|
||||||
|
|
||||||
|
ignore_name .git
|
||||||
|
ignore_name *~
|
||||||
|
ignore_name *.pyc
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
|
||||||
egr() # }}}
|
egr() # }}}
|
||||||
|
|
||||||
# colors {{{
|
# colors {{{
|
||||||
|
|||||||
7
kittens/diff/options/parse.py
generated
7
kittens/diff/options/parse.py
generated
@@ -1,7 +1,7 @@
|
|||||||
# generated by gen-config.py DO NOT edit
|
# generated by gen-config.py DO NOT edit
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
from kittens.diff.options.utils import parse_map, syntax_aliases
|
from kittens.diff.options.utils import parse_map, store_multiple, syntax_aliases
|
||||||
from kitty.conf.utils import merge_dicts, positive_int, python_string, to_color, to_color_or_none
|
from kitty.conf.utils import merge_dicts, positive_int, python_string, to_color, to_color_or_none
|
||||||
|
|
||||||
|
|
||||||
@@ -37,6 +37,10 @@ class Parser:
|
|||||||
def hunk_margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
def hunk_margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
ans['hunk_margin_bg'] = to_color(val)
|
ans['hunk_margin_bg'] = to_color(val)
|
||||||
|
|
||||||
|
def ignore_name(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
|
for k, v in store_multiple(val, ans["ignore_name"]):
|
||||||
|
ans["ignore_name"][k] = v
|
||||||
|
|
||||||
def margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
def margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
ans['margin_bg'] = to_color(val)
|
ans['margin_bg'] = to_color(val)
|
||||||
|
|
||||||
@@ -89,6 +93,7 @@ class Parser:
|
|||||||
|
|
||||||
def create_result_dict() -> typing.Dict[str, typing.Any]:
|
def create_result_dict() -> typing.Dict[str, typing.Any]:
|
||||||
return {
|
return {
|
||||||
|
'ignore_name': {},
|
||||||
'map': [],
|
'map': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
kittens/diff/options/types.py
generated
3
kittens/diff/options/types.py
generated
@@ -20,6 +20,7 @@ option_names = ( # {{{
|
|||||||
'highlight_removed_bg',
|
'highlight_removed_bg',
|
||||||
'hunk_bg',
|
'hunk_bg',
|
||||||
'hunk_margin_bg',
|
'hunk_margin_bg',
|
||||||
|
'ignore_name',
|
||||||
'map',
|
'map',
|
||||||
'margin_bg',
|
'margin_bg',
|
||||||
'margin_fg',
|
'margin_fg',
|
||||||
@@ -64,6 +65,7 @@ class Options:
|
|||||||
syntax_aliases: typing.Dict[str, str] = {'pyj': 'py', 'pyi': 'py', 'recipe': 'py'}
|
syntax_aliases: typing.Dict[str, str] = {'pyj': 'py', 'pyi': 'py', 'recipe': 'py'}
|
||||||
title_bg: Color = Color(255, 255, 255)
|
title_bg: Color = Color(255, 255, 255)
|
||||||
title_fg: Color = Color(0, 0, 0)
|
title_fg: Color = Color(0, 0, 0)
|
||||||
|
ignore_name: typing.Dict[str, str] = {}
|
||||||
map: typing.List[typing.Tuple[kitty.types.ParsedShortcut, kitty.conf.utils.KeyAction]] = []
|
map: typing.List[typing.Tuple[kitty.types.ParsedShortcut, kitty.conf.utils.KeyAction]] = []
|
||||||
key_definitions: KittensKeyMap = {}
|
key_definitions: KittensKeyMap = {}
|
||||||
config_paths: typing.Tuple[str, ...] = ()
|
config_paths: typing.Tuple[str, ...] = ()
|
||||||
@@ -116,6 +118,7 @@ class Options:
|
|||||||
|
|
||||||
|
|
||||||
defaults = Options()
|
defaults = Options()
|
||||||
|
defaults.ignore_name = {}
|
||||||
defaults.map = [
|
defaults.map = [
|
||||||
# quit
|
# quit
|
||||||
(ParsedShortcut(mods=0, key_name='q'), KeyAction('quit')), # noqa
|
(ParsedShortcut(mods=0, key_name='q'), KeyAction('quit')), # noqa
|
||||||
|
|||||||
8
kittens/diff/options/utils.py
Normal file → Executable file
8
kittens/diff/options/utils.py
Normal file → Executable file
@@ -3,7 +3,7 @@
|
|||||||
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
from typing import Any, Dict, Iterable, Tuple, Union
|
from typing import Any, Container, Dict, Iterable, Tuple, Union
|
||||||
|
|
||||||
from kitty.conf.utils import (
|
from kitty.conf.utils import (
|
||||||
KeyFuncWrapper, KittensKeyDefinition, parse_kittens_key
|
KeyFuncWrapper, KittensKeyDefinition, parse_kittens_key
|
||||||
@@ -58,6 +58,12 @@ def syntax_aliases(raw: str) -> Dict[str, str]:
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def store_multiple(val: str, current_val: Container[str]) -> Iterable[Tuple[str, str]]:
|
||||||
|
val = val.strip()
|
||||||
|
if val not in current_val:
|
||||||
|
yield val, val
|
||||||
|
|
||||||
|
|
||||||
def parse_map(val: str) -> Iterable[KittensKeyDefinition]:
|
def parse_map(val: str) -> Iterable[KittensKeyDefinition]:
|
||||||
x = parse_kittens_key(val, func_with_args.args_funcs)
|
x = parse_kittens_key(val, func_with_args.args_funcs)
|
||||||
if x is not None:
|
if x is not None:
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ class Hunk:
|
|||||||
if c.left_start + c.left_count != self.left_start + self.left_count:
|
if c.left_start + c.left_count != self.left_start + self.left_count:
|
||||||
raise ValueError(f'Left side line mismatch {c.left_start + c.left_count} != {self.left_start + self.left_count}')
|
raise ValueError(f'Left side line mismatch {c.left_start + c.left_count} != {self.left_start + self.left_count}')
|
||||||
if c.right_start + c.right_count != self.right_start + self.right_count:
|
if c.right_start + c.right_count != self.right_start + self.right_count:
|
||||||
raise ValueError(f'Left side line mismatch {c.right_start + c.right_count} != {self.right_start + self.right_count}')
|
raise ValueError(f'Right side line mismatch {c.right_start + c.right_count} != {self.right_start + self.right_count}')
|
||||||
for c in self.chunks:
|
for c in self.chunks:
|
||||||
c.finalize()
|
c.finalize()
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ def parse_patch(raw: str) -> Patch:
|
|||||||
else:
|
else:
|
||||||
if current_hunk is None:
|
if current_hunk is None:
|
||||||
continue
|
continue
|
||||||
q = line[0]
|
q = line[0] if line else ''
|
||||||
if q == '+':
|
if q == '+':
|
||||||
current_hunk.add_line()
|
current_hunk.add_line()
|
||||||
elif q == '-':
|
elif q == '-':
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ def images_supported() -> bool:
|
|||||||
|
|
||||||
class Ref:
|
class Ref:
|
||||||
|
|
||||||
|
__slots__: Tuple[str, ...] = ()
|
||||||
|
|
||||||
def __setattr__(self, name: str, value: object) -> None:
|
def __setattr__(self, name: str, value: object) -> None:
|
||||||
raise AttributeError("can't set attribute")
|
raise AttributeError("can't set attribute")
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ from functools import lru_cache
|
|||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, Generator, Iterable, List, Optional, Pattern,
|
Any, Callable, Dict, Generator, Iterable, Iterator, List, Optional,
|
||||||
Sequence, Set, Tuple, Type, cast
|
Pattern, Sequence, Set, Tuple, Type, cast
|
||||||
)
|
)
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
@@ -44,6 +44,7 @@ DEFAULT_HINT_ALPHABET = string.digits + string.ascii_lowercase
|
|||||||
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
|
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
|
||||||
FILE_EXTENSION = r'\.(?:[a-zA-Z0-9]{2,7}|[ahcmo])(?!\.)'
|
FILE_EXTENSION = r'\.(?:[a-zA-Z0-9]{2,7}|[ahcmo])(?!\.)'
|
||||||
PATH_REGEX = fr'(?:\S*?/[\r\S]+)|(?:\S[\r\S]*{FILE_EXTENSION})\b'
|
PATH_REGEX = fr'(?:\S*?/[\r\S]+)|(?:\S[\r\S]*{FILE_EXTENSION})\b'
|
||||||
|
DEFAULT_LINENUM_REGEX = fr'(?P<path>{PATH_REGEX}):(?P<line>\d+)'
|
||||||
|
|
||||||
|
|
||||||
class Mark:
|
class Mark:
|
||||||
@@ -64,6 +65,10 @@ class Mark:
|
|||||||
self.is_hyperlink = is_hyperlink
|
self.is_hyperlink = is_hyperlink
|
||||||
self.group_id = group_id
|
self.group_id = group_id
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (f'Mark(index={self.index!r}, start={self.start!r}, end={self.end!r},'
|
||||||
|
f' text={self.text!r}, groupdict={self.groupdict!r}, is_hyperlink={self.is_hyperlink!r}, group_id={self.group_id!r})')
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=2048)
|
@lru_cache(maxsize=2048)
|
||||||
def encode_hint(num: int, alphabet: str) -> str:
|
def encode_hint(num: int, alphabet: str) -> str:
|
||||||
@@ -225,14 +230,14 @@ class Hints(Handler):
|
|||||||
self.write(self.current_text)
|
self.write(self.current_text)
|
||||||
|
|
||||||
|
|
||||||
def regex_finditer(pat: 'Pattern[str]', minimum_match_length: int, text: str) -> Generator[Tuple[int, int, Dict[str, str]], None, None]:
|
def regex_finditer(pat: 'Pattern[str]', minimum_match_length: int, text: str) -> Iterator[Tuple[int, int, 're.Match[str]']]:
|
||||||
has_named_groups = bool(pat.groupindex)
|
has_named_groups = bool(pat.groupindex)
|
||||||
for m in pat.finditer(text):
|
for m in pat.finditer(text):
|
||||||
s, e = m.span(0 if has_named_groups else pat.groups)
|
s, e = m.span(0 if has_named_groups else pat.groups)
|
||||||
while e > s + 1 and text[e-1] == '\0':
|
while e > s + 1 and text[e-1] == '\0':
|
||||||
e -= 1
|
e -= 1
|
||||||
if e - s >= minimum_match_length:
|
if e - s >= minimum_match_length:
|
||||||
yield s, e, m.groupdict()
|
yield s, e, m
|
||||||
|
|
||||||
|
|
||||||
closing_bracket_map = {'(': ')', '[': ']', '{': '}', '<': '>', '*': '*', '"': '"', "'": "'", "“": "”", "‘": "’"}
|
closing_bracket_map = {'(': ')', '[': ']', '{': '}', '<': '>', '*': '*', '"': '"', "'": "'", "“": "”", "‘": "’"}
|
||||||
@@ -318,16 +323,22 @@ def ip(text: str, s: int, e: int) -> Tuple[int, int]:
|
|||||||
return s, e
|
return s, e
|
||||||
|
|
||||||
|
|
||||||
def mark(pattern: str, post_processors: Iterable[PostprocessorFunc], text: str, args: HintsCLIOptions) -> Generator[Mark, None, None]:
|
def mark(pattern: str, post_processors: Iterable[PostprocessorFunc], text: str, args: HintsCLIOptions) -> Iterator[Mark]:
|
||||||
pat = re.compile(pattern)
|
pat = re.compile(pattern)
|
||||||
for idx, (s, e, groupdict) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
|
sanitize_pat = re.compile('[\r\n\0]')
|
||||||
|
for idx, (s, e, match_object) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
|
||||||
try:
|
try:
|
||||||
for func in post_processors:
|
for func in post_processors:
|
||||||
s, e = func(text, s, e)
|
s, e = func(text, s, e)
|
||||||
except InvalidMatch:
|
except InvalidMatch:
|
||||||
continue
|
continue
|
||||||
|
groupdict = match_object.groupdict()
|
||||||
mark_text = re.sub('[\r\n\0]', '', text[s:e])
|
for group_name in groupdict:
|
||||||
|
group_idx = pat.groupindex[group_name]
|
||||||
|
gs, ge = match_object.span(group_idx)
|
||||||
|
gs, ge = max(gs, s), min(ge, e)
|
||||||
|
groupdict[group_name] = sanitize_pat.sub('', text[gs:ge])
|
||||||
|
mark_text = sanitize_pat.sub('', text[s:e])
|
||||||
yield Mark(idx, s, e, mark_text, groupdict)
|
yield Mark(idx, s, e, mark_text, groupdict)
|
||||||
|
|
||||||
|
|
||||||
@@ -420,7 +431,7 @@ def parse_input(text: str) -> str:
|
|||||||
def linenum_marks(text: str, args: HintsCLIOptions, Mark: Type[Mark], extra_cli_args: Sequence[str], *a: Any) -> Generator[Mark, None, None]:
|
def linenum_marks(text: str, args: HintsCLIOptions, Mark: Type[Mark], extra_cli_args: Sequence[str], *a: Any) -> Generator[Mark, None, None]:
|
||||||
regex = args.regex
|
regex = args.regex
|
||||||
if regex == DEFAULT_REGEX:
|
if regex == DEFAULT_REGEX:
|
||||||
regex = fr'(?P<path>{PATH_REGEX}):(?P<line>\d+)'
|
regex = DEFAULT_LINENUM_REGEX
|
||||||
yield from mark(regex, [brackets, quotes], text, args)
|
yield from mark(regex, [brackets, quotes], text, args)
|
||||||
|
|
||||||
|
|
||||||
@@ -525,10 +536,10 @@ OPTIONS = r'''
|
|||||||
--program
|
--program
|
||||||
type=list
|
type=list
|
||||||
What program to use to open matched text. Defaults to the default open program
|
What program to use to open matched text. Defaults to the default open program
|
||||||
for the operating system. Use a value of :file:`-` to paste the match into the
|
for the operating system. Use a value of :code:`-` to paste the match into the
|
||||||
terminal window instead. A value of :file:`@` will copy the match to the
|
terminal window instead. A value of :code:`@` will copy the match to the
|
||||||
clipboard. A value of :file:`*` will copy the match to the primary selection
|
clipboard. A value of :code:`*` will copy the match to the primary selection
|
||||||
(on systems that support primary selections). A value of :file:`default` will
|
(on systems that support primary selections). A value of :code:`default` will
|
||||||
run the default open program. Can be specified multiple times to run multiple
|
run the default open program. Can be specified multiple times to run multiple
|
||||||
programs.
|
programs.
|
||||||
|
|
||||||
@@ -538,22 +549,22 @@ default=url
|
|||||||
choices=url,regex,path,line,hash,word,linenum,hyperlink,ip
|
choices=url,regex,path,line,hash,word,linenum,hyperlink,ip
|
||||||
The type of text to search for. A value of :code:`linenum` is special, it looks
|
The type of text to search for. A value of :code:`linenum` is special, it looks
|
||||||
for error messages using the pattern specified with :option:`--regex`, which
|
for error messages using the pattern specified with :option:`--regex`, which
|
||||||
must have the named groups, :code:`path` and :code:`line`. If not specified,
|
must have the named groups: :code:`path` and :code:`line`. If not specified,
|
||||||
will look for :code:`path:line`. The :option:`--linenum-action` option
|
will look for :code:`path:line`. The :option:`--linenum-action` option
|
||||||
controls where to display the selected error message, other options are ignored.
|
controls where to display the selected error message, other options are ignored.
|
||||||
|
|
||||||
|
|
||||||
--regex
|
--regex
|
||||||
default={default_regex}
|
default={default_regex}
|
||||||
The regular expression to use when :option:`kitty +kitten hints --type`=regex.
|
The regular expression to use when option :option:`--type` is set to
|
||||||
The regular expression is in python syntax. If you specify a numbered group in
|
:code:`regex`, in python syntax. If you specify a numbered group in the regular
|
||||||
the regular expression only the group will be matched. This allow you to match
|
expression, only the group will be matched. This allow you to match text
|
||||||
text ignoring a prefix/suffix, as needed. The default expression matches lines.
|
ignoring a prefix/suffix, as needed. The default expression matches lines. To
|
||||||
To match text over multiple lines you should prefix the regular expression with
|
match text over multiple lines, you should prefix the regular expression with
|
||||||
:code:`(?ms)`, which turns on MULTILINE and DOTALL modes for the regex engine.
|
:code:`(?ms)`, which turns on MULTILINE and DOTALL modes for the regex engine.
|
||||||
If you specify named groups and a :option:`kitty +kitten hints --program` then
|
If you specify named groups and a :option:`--program`, then the program will be
|
||||||
the program will be passed arguments corresponding to each named group of
|
passed arguments corresponding to each named group of the form
|
||||||
the form key=value.
|
:code:`key=value`.
|
||||||
|
|
||||||
|
|
||||||
--linenum-action
|
--linenum-action
|
||||||
@@ -565,22 +576,22 @@ window, :code:`window` a new kitty window, :code:`tab` a new tab,
|
|||||||
:code:`os_window` a new OS window and :code:`background` run in the background.
|
:code:`os_window` a new OS window and :code:`background` run in the background.
|
||||||
The actual action is whatever arguments are provided to the kitten, for
|
The actual action is whatever arguments are provided to the kitten, for
|
||||||
example:
|
example:
|
||||||
:code:`kitty + kitten hints --type=linenum --linenum-action=tab vim +{line} {path}`
|
:code:`kitty +kitten hints --type=linenum --linenum-action=tab vim +{line} {path}`
|
||||||
will open the matched path at the matched line number in vim in
|
will open the matched path at the matched line number in vim in
|
||||||
a new kitty tab. Note that only when using :code:`self` are the special values for
|
a new kitty tab. Note that in order to use :option:`--program` to copy or paste
|
||||||
:option:`kitty +kitten hints --program` to copy/paste the text respected.
|
text, you need to use the special value :code:`self`.
|
||||||
|
|
||||||
|
|
||||||
--url-prefixes
|
--url-prefixes
|
||||||
default=default
|
default=default
|
||||||
Comma separated list of recognized URL prefixes. Defaults, to
|
Comma separated list of recognized URL prefixes. Defaults to the list of
|
||||||
the list of prefixes defined in kitty.conf.
|
prefixes defined by the :opt:`url_prefixes` option in :file:`kitty.conf`.
|
||||||
|
|
||||||
|
|
||||||
--word-characters
|
--word-characters
|
||||||
Characters to consider as part of a word. In addition, all characters marked as
|
Characters to consider as part of a word. In addition, all characters marked as
|
||||||
alphanumeric in the unicode database will be considered as word characters.
|
alphanumeric in the Unicode database will be considered as word characters.
|
||||||
Defaults to the select_by_word_characters setting from kitty.conf.
|
Defaults to the :opt:`select_by_word_characters` option from :file:`kitty.conf`.
|
||||||
|
|
||||||
|
|
||||||
--minimum-match-length
|
--minimum-match-length
|
||||||
@@ -591,26 +602,26 @@ The minimum number of characters to consider a match.
|
|||||||
|
|
||||||
--multiple
|
--multiple
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Select multiple matches and perform the action on all of them together at the end.
|
Select multiple matches and perform the action on all of them together at the
|
||||||
In this mode, press :kbd:`Esc` to finish selecting.
|
end. In this mode, press :kbd:`Esc` to finish selecting.
|
||||||
|
|
||||||
|
|
||||||
--multiple-joiner
|
--multiple-joiner
|
||||||
default=auto
|
default=auto
|
||||||
String to use to join multiple selections when copying to the clipboard or
|
String for joining multiple selections when copying to the clipboard or
|
||||||
inserting into the terminal. The special strings: "space", "newline", "empty",
|
inserting into the terminal. The special values are: :code:`space` - a space
|
||||||
"json" and "auto" are interpreted as a space character, a newline an empty
|
character, :code:`newline` - a newline, :code:`empty` - an empty joiner,
|
||||||
joiner, a JSON serialized list and an automatic choice, based on the type of
|
:code:`json` - a JSON serialized list, :code:`auto` - an automatic choice, based
|
||||||
text being selected. In addition, integers are interpreted as zero-based
|
on the type of text being selected. In addition, integers are interpreted as
|
||||||
indices into the list of selections. You can use 0 for the first selection and
|
zero-based indices into the list of selections. You can use :code:`0` for the
|
||||||
-1 for the last.
|
first selection and :code:`-1` for the last.
|
||||||
|
|
||||||
|
|
||||||
--add-trailing-space
|
--add-trailing-space
|
||||||
default=auto
|
default=auto
|
||||||
choices=auto,always,never
|
choices=auto,always,never
|
||||||
Add trailing space after matched text. Defaults to auto, which adds the space
|
Add trailing space after matched text. Defaults to :code:`auto`, which adds the
|
||||||
when used together with :option:`--multiple`.
|
space when used together with :option:`--multiple`.
|
||||||
|
|
||||||
|
|
||||||
--hints-offset
|
--hints-offset
|
||||||
@@ -621,45 +632,47 @@ greater than or equal to zero are respected.
|
|||||||
|
|
||||||
|
|
||||||
--alphabet
|
--alphabet
|
||||||
The list of characters to use for hints. The default is to use numbers and lowercase
|
The list of characters to use for hints. The default is to use numbers and
|
||||||
English alphabets. Specify your preference as a string of characters. Note that
|
lowercase English alphabets. Specify your preference as a string of characters.
|
||||||
unless you specify the hints offset as zero the first match will be highlighted with
|
Note that you need to specify the :option:`--hints-offset` as zero to use the
|
||||||
the second character you specify.
|
first character to highlight the first match, otherwise it will start with the
|
||||||
|
second character by default.
|
||||||
|
|
||||||
|
|
||||||
--ascending
|
--ascending
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Have the hints increase from top to bottom instead of decreasing from top to bottom.
|
Make the hints increase from top to bottom, instead of decreasing from top to
|
||||||
|
bottom.
|
||||||
|
|
||||||
|
|
||||||
--hints-foreground-color
|
--hints-foreground-color
|
||||||
default=black
|
default=black
|
||||||
type=str
|
type=str
|
||||||
The foreground color for hints
|
The foreground color for hints.
|
||||||
|
|
||||||
|
|
||||||
--hints-background-color
|
--hints-background-color
|
||||||
default=green
|
default=green
|
||||||
type=str
|
type=str
|
||||||
The background color for hints
|
The background color for hints.
|
||||||
|
|
||||||
|
|
||||||
--hints-text-color
|
--hints-text-color
|
||||||
default=gray
|
default=gray
|
||||||
type=str
|
type=str
|
||||||
The foreground color for text pointed to by the hints
|
The foreground color for text pointed to by the hints.
|
||||||
|
|
||||||
|
|
||||||
--customize-processing
|
--customize-processing
|
||||||
Name of a python file in the kitty config directory which will be imported to provide
|
Name of a python file in the kitty config directory which will be imported to
|
||||||
custom implementations for pattern finding and performing actions
|
provide custom implementations for pattern finding and performing actions
|
||||||
on selected matches. See {hints_url}
|
on selected matches. You can also specify absolute paths to load the script from
|
||||||
for details. You can also specify absolute paths to load the script from elsewhere.
|
elsewhere. See {hints_url} for details.
|
||||||
|
|
||||||
|
|
||||||
--window-title
|
--window-title
|
||||||
The window title for the hints window, default title is selected based on
|
The title for the hints window, default title is based on the type of text being
|
||||||
the type of text being hinted.
|
hinted.
|
||||||
'''.format(
|
'''.format(
|
||||||
default_regex=DEFAULT_REGEX,
|
default_regex=DEFAULT_REGEX,
|
||||||
line='{{line}}', path='{{path}}',
|
line='{{line}}', path='{{path}}',
|
||||||
@@ -697,18 +710,14 @@ def main(args: List[str]) -> Optional[Dict[str, Any]]:
|
|||||||
|
|
||||||
|
|
||||||
def linenum_process_result(data: Dict[str, Any]) -> Tuple[str, int]:
|
def linenum_process_result(data: Dict[str, Any]) -> Tuple[str, int]:
|
||||||
pat = re.compile(r':(\d+)$')
|
lnum_pat = re.compile(r'(:\d+)$')
|
||||||
for m, g in zip(data['match'], data['groupdicts']):
|
for match, g in zip(data['match'], data['groupdicts']):
|
||||||
if m:
|
path, line = g['path'], g['line']
|
||||||
path, line = g['path'], g['line']
|
if path and line:
|
||||||
# look for trailers on path of the for :number
|
m = lnum_pat.search(path)
|
||||||
while True:
|
if m is not None:
|
||||||
m = pat.search(path)
|
line = m.group(1)[1:]
|
||||||
if m is None:
|
path = path.rpartition(':')[0]
|
||||||
break
|
|
||||||
line = m.group(1)
|
|
||||||
path = path[:-len(m.group())]
|
|
||||||
|
|
||||||
return os.path.expanduser(path), int(line)
|
return os.path.expanduser(path), int(line)
|
||||||
return '', -1
|
return '', -1
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ def write_hyperlink(write: Callable[[bytes], None], url: bytes, line: bytes, fra
|
|||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
if not sys.stdout.isatty() and '--pretty' not in sys.argv:
|
if not sys.stdout.isatty() and '--pretty' not in sys.argv and '-p' not in sys.argv:
|
||||||
os.execlp('rg', 'rg', *sys.argv[1:])
|
os.execlp('rg', 'rg', *sys.argv[1:])
|
||||||
cmdline = ['rg', '--pretty', '--with-filename'] + sys.argv[1:]
|
cmdline = ['rg', '--pretty', '--with-filename'] + sys.argv[1:]
|
||||||
try:
|
try:
|
||||||
@@ -59,7 +59,7 @@ def main() -> None:
|
|||||||
write(line)
|
write(line)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
p.send_signal(signal.SIGINT)
|
p.send_signal(signal.SIGINT)
|
||||||
except EOFError:
|
except (EOFError, BrokenPipeError):
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
p.stdout.close()
|
p.stdout.close()
|
||||||
|
|||||||
@@ -42,24 +42,24 @@ Horizontal alignment for the displayed image.
|
|||||||
|
|
||||||
|
|
||||||
--place
|
--place
|
||||||
Choose where on the screen to display the image. The image will
|
Choose where on the screen to display the image. The image will be scaled to fit
|
||||||
be scaled to fit into the specified rectangle. The syntax for
|
into the specified rectangle. The syntax for specifying rectangles is
|
||||||
specifying rectangles is <:italic:`width`>x<:italic:`height`>@<:italic:`left`>x<:italic:`top`>.
|
<:italic:`width`>x<:italic:`height`>@<:italic:`left`>x<:italic:`top`>.
|
||||||
All measurements are in cells (i.e. cursor positions) with the
|
All measurements are in cells (i.e. cursor positions) with the origin
|
||||||
origin :italic:`(0, 0)` at the top-left corner of the screen.
|
:italic:`(0, 0)` at the top-left corner of the screen.
|
||||||
|
|
||||||
|
|
||||||
--scale-up
|
--scale-up
|
||||||
type=bool-set
|
type=bool-set
|
||||||
When used in combination with :option:`--place` it will cause images that
|
When used in combination with :option:`--place` it will cause images that are
|
||||||
are smaller than the specified area to be scaled up to use as much
|
smaller than the specified area to be scaled up to use as much of the specified
|
||||||
of the specified area as possible.
|
area as possible.
|
||||||
|
|
||||||
|
|
||||||
--background
|
--background
|
||||||
default=none
|
default=none
|
||||||
Specify a background color, this will cause transparent images to be composited on
|
Specify a background color, this will cause transparent images to be composited
|
||||||
top of the specified color.
|
on top of the specified color.
|
||||||
|
|
||||||
|
|
||||||
--mirror
|
--mirror
|
||||||
@@ -79,17 +79,18 @@ type=choices
|
|||||||
choices=detect,file,stream
|
choices=detect,file,stream
|
||||||
default=detect
|
default=detect
|
||||||
Which mechanism to use to transfer images to the terminal. The default is to
|
Which mechanism to use to transfer images to the terminal. The default is to
|
||||||
auto-detect. :italic:`file` means to use a temporary file and :italic:`stream` means to
|
auto-detect. :italic:`file` means to use a temporary file and :italic:`stream`
|
||||||
send the data via terminal escape codes. Note that if you use the :italic:`file`
|
means to send the data via terminal escape codes. Note that if you use the
|
||||||
transfer mode and you are connecting over a remote session then image display
|
:italic:`file` transfer mode and you are connecting over a remote session then
|
||||||
will not work.
|
image display will not work.
|
||||||
|
|
||||||
|
|
||||||
--detect-support
|
--detect-support
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Detect support for image display in the terminal. If not supported, will exit
|
Detect support for image display in the terminal. If not supported, will exit
|
||||||
with exit code 1, otherwise will exit with code 0 and print the supported
|
with exit code 1, otherwise will exit with code 0 and print the supported
|
||||||
transfer mode to stderr, which can be used with the :option:`--transfer-mode` option.
|
transfer mode to stderr, which can be used with the :option:`--transfer-mode`
|
||||||
|
option.
|
||||||
|
|
||||||
|
|
||||||
--detection-timeout
|
--detection-timeout
|
||||||
@@ -101,17 +102,17 @@ detecting image display support.
|
|||||||
|
|
||||||
--print-window-size
|
--print-window-size
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Print out the window size as :italic:`widthxheight` (in pixels) and quit. This is a
|
Print out the window size as <:italic:`width`>x<:italic:`height`> (in pixels) and quit. This is a
|
||||||
convenience method to query the window size if using :code:`kitty +kitten icat` from a
|
convenience method to query the window size if using :code:`kitty +kitten icat`
|
||||||
scripting language that cannot make termios calls.
|
from a scripting language that cannot make termios calls.
|
||||||
|
|
||||||
|
|
||||||
--stdin
|
--stdin
|
||||||
type=choices
|
type=choices
|
||||||
choices=detect,yes,no
|
choices=detect,yes,no
|
||||||
default=detect
|
default=detect
|
||||||
Read image data from stdin. The default is to do it automatically, when STDIN is not a terminal,
|
Read image data from STDIN. The default is to do it automatically, when STDIN is
|
||||||
but you can turn it off or on explicitly, if needed.
|
not a terminal, but you can turn it off or on explicitly, if needed.
|
||||||
|
|
||||||
|
|
||||||
--silent
|
--silent
|
||||||
@@ -121,9 +122,9 @@ Do not print out anything to STDOUT during operation.
|
|||||||
|
|
||||||
--z-index -z
|
--z-index -z
|
||||||
default=0
|
default=0
|
||||||
Z-index of the image. When negative, text will be displayed on top of the image. Use
|
Z-index of the image. When negative, text will be displayed on top of the image.
|
||||||
a double minus for values under the threshold for drawing images under cell background
|
Use a double minus for values under the threshold for drawing images under cell
|
||||||
colors. For example, :code:`--1` evaluates as -1,073,741,825.
|
background colors. For example, :code:`--1` evaluates as -1,073,741,825.
|
||||||
|
|
||||||
|
|
||||||
--loop -l
|
--loop -l
|
||||||
|
|||||||
0
kittens/prewarm/__init__.py
Normal file
0
kittens/prewarm/__init__.py
Normal file
453
kittens/prewarm/main.py
Normal file
453
kittens/prewarm/main.py
Normal file
@@ -0,0 +1,453 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import select
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from contextlib import suppress
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from importlib import import_module
|
||||||
|
from typing import (
|
||||||
|
IO, TYPE_CHECKING, Any, Dict, List, NoReturn, Optional, Union, cast
|
||||||
|
)
|
||||||
|
|
||||||
|
from kitty.child import remove_cloexec
|
||||||
|
from kitty.constants import kitty_exe
|
||||||
|
from kitty.entry_points import main as main_entry_point
|
||||||
|
from kitty.fast_data_types import establish_controlling_tty, safe_pipe
|
||||||
|
from kitty.shm import SharedMemory
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from _typeshed import ReadableBuffer, WriteableBuffer
|
||||||
|
|
||||||
|
|
||||||
|
hangup_events = select.POLLHUP
|
||||||
|
error_events = select.POLLERR | select.POLLNVAL
|
||||||
|
basic_events = hangup_events | error_events
|
||||||
|
|
||||||
|
|
||||||
|
class PrewarmProcessFailed(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Child:
|
||||||
|
child_id: int
|
||||||
|
child_process_pid: int
|
||||||
|
|
||||||
|
|
||||||
|
class PrewarmProcess:
|
||||||
|
|
||||||
|
def __init__(self, create_file_to_read_from_worker: bool = False) -> None:
|
||||||
|
self.from_worker_fd, self.in_worker_fd = safe_pipe()
|
||||||
|
self.children: Dict[int, Child] = {}
|
||||||
|
if create_file_to_read_from_worker:
|
||||||
|
os.set_blocking(self.from_worker_fd, True)
|
||||||
|
self.from_worker = open(self.from_worker_fd, mode='r', closefd=True)
|
||||||
|
self.from_worker_fd = -1
|
||||||
|
|
||||||
|
def take_from_worker_fd(self) -> int:
|
||||||
|
ans, self.from_worker_fd = self.from_worker_fd, -1
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
if self.from_worker_fd > -1:
|
||||||
|
os.close(self.from_worker_fd)
|
||||||
|
self.from_worker_fd = -1
|
||||||
|
if hasattr(self, 'from_worker'):
|
||||||
|
self.from_worker.close()
|
||||||
|
del self.from_worker
|
||||||
|
if self.worker_started:
|
||||||
|
import subprocess
|
||||||
|
self.process.stdin and self.process.stdin.close()
|
||||||
|
self.process.stdout and self.process.stdout.close()
|
||||||
|
try:
|
||||||
|
self.process.wait(timeout=1.0)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
self.process.kill()
|
||||||
|
del self.process
|
||||||
|
|
||||||
|
@property
|
||||||
|
def worker_started(self) -> bool:
|
||||||
|
return self.in_worker_fd == -1
|
||||||
|
|
||||||
|
def ensure_worker(self) -> None:
|
||||||
|
if not self.worker_started:
|
||||||
|
import subprocess
|
||||||
|
self.process = subprocess.Popen(
|
||||||
|
[kitty_exe(), '+kitten', 'prewarm', str(self.in_worker_fd)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, pass_fds=(self.in_worker_fd,))
|
||||||
|
os.close(self.in_worker_fd)
|
||||||
|
self.in_worker_fd = -1
|
||||||
|
assert self.process.stdin is not None and self.process.stdout is not None
|
||||||
|
self.write_to_process_fd = self.process.stdin.fileno()
|
||||||
|
self.read_from_process_fd = self.process.stdout.fileno()
|
||||||
|
os.set_blocking(self.write_to_process_fd, False)
|
||||||
|
os.set_blocking(self.read_from_process_fd, False)
|
||||||
|
self.poll = select.poll()
|
||||||
|
self.poll.register(self.process.stdout.fileno(), select.POLLIN | basic_events)
|
||||||
|
|
||||||
|
def poll_to_send(self, yes: bool = True) -> None:
|
||||||
|
if yes:
|
||||||
|
self.poll.register(self.write_to_process_fd, select.POLLOUT | basic_events)
|
||||||
|
else:
|
||||||
|
self.poll.unregister(self.write_to_process_fd)
|
||||||
|
|
||||||
|
def reload_kitty_config(self) -> None:
|
||||||
|
if self.worker_started:
|
||||||
|
self.send_to_prewarm_process('reload_kitty_config:\n')
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
tty_fd: int,
|
||||||
|
argv: List[str],
|
||||||
|
cwd: str = '',
|
||||||
|
env: Optional[Dict[str, str]] = None,
|
||||||
|
stdin_data: Optional[Union[str, bytes]] = None
|
||||||
|
) -> Child:
|
||||||
|
self.ensure_worker()
|
||||||
|
tty_name = os.ttyname(tty_fd)
|
||||||
|
if isinstance(stdin_data, str):
|
||||||
|
stdin_data = stdin_data.encode()
|
||||||
|
if env is None:
|
||||||
|
env = dict(os.environ)
|
||||||
|
cmd: Dict[str, Union[int, List[str], str, Dict[str, str]]] = {
|
||||||
|
'tty_name': tty_name, 'cwd': cwd or os.getcwd(), 'argv': argv, 'env': env,
|
||||||
|
}
|
||||||
|
total_size = 0
|
||||||
|
if stdin_data is not None:
|
||||||
|
cmd['stdin_size'] = len(stdin_data)
|
||||||
|
total_size += len(stdin_data)
|
||||||
|
data = json.dumps(cmd).encode()
|
||||||
|
total_size += len(data) + SharedMemory.num_bytes_for_size
|
||||||
|
with SharedMemory(size=total_size, unlink_on_exit=True) as shm:
|
||||||
|
shm.write_data_with_size(data)
|
||||||
|
if stdin_data:
|
||||||
|
shm.write(stdin_data)
|
||||||
|
shm.flush()
|
||||||
|
self.send_to_prewarm_process(f'fork:{shm.name}\n')
|
||||||
|
input_buf = b''
|
||||||
|
st = time.monotonic()
|
||||||
|
while time.monotonic() - st < 2:
|
||||||
|
for (fd, event) in self.poll.poll(0.2):
|
||||||
|
if event & basic_events:
|
||||||
|
raise PrewarmProcessFailed('Failed doing I/O with prewarm process')
|
||||||
|
if fd == self.read_from_process_fd and event & select.POLLIN:
|
||||||
|
d = os.read(self.read_from_process_fd, io.DEFAULT_BUFFER_SIZE)
|
||||||
|
input_buf += d
|
||||||
|
while (idx := input_buf.find(b'\n')) > -1:
|
||||||
|
line = input_buf[:idx].decode()
|
||||||
|
input_buf = input_buf[idx+1:]
|
||||||
|
if line.startswith('CHILD:'):
|
||||||
|
_, cid, pid = line.split(':')
|
||||||
|
child = self.add_child(int(cid), int(pid))
|
||||||
|
shm.unlink_on_exit = False
|
||||||
|
return child
|
||||||
|
if line.startswith('ERR:'):
|
||||||
|
raise PrewarmProcessFailed(line.split(':', 1)[-1])
|
||||||
|
raise PrewarmProcessFailed('Timed out waiting for I/O with prewarm process')
|
||||||
|
|
||||||
|
def add_child(self, child_id: int, pid: int) -> Child:
|
||||||
|
self.children[child_id] = c = Child(child_id, pid)
|
||||||
|
return c
|
||||||
|
|
||||||
|
def send_to_prewarm_process(self, output_buf: Union[str, bytes] = b'', timeout: float = 2) -> None:
|
||||||
|
if isinstance(output_buf, str):
|
||||||
|
output_buf = output_buf.encode()
|
||||||
|
st = time.monotonic()
|
||||||
|
while time.monotonic() - st < timeout and output_buf:
|
||||||
|
self.poll_to_send(bool(output_buf))
|
||||||
|
for (fd, event) in self.poll.poll(0.2):
|
||||||
|
if event & basic_events:
|
||||||
|
raise PrewarmProcessFailed('Failed doing I/O with prewarm process')
|
||||||
|
if fd == self.write_to_process_fd and event & select.POLLOUT:
|
||||||
|
n = os.write(self.write_to_process_fd, output_buf)
|
||||||
|
output_buf = output_buf[n:]
|
||||||
|
self.poll_to_send(False)
|
||||||
|
if output_buf:
|
||||||
|
raise PrewarmProcessFailed('Timed out waiting to write to prewarm process')
|
||||||
|
|
||||||
|
def mark_child_as_ready(self, child_id: int) -> bool:
|
||||||
|
c = self.children.pop(child_id, None)
|
||||||
|
if c is None:
|
||||||
|
return False
|
||||||
|
self.send_to_prewarm_process(f'ready:{child_id}\n')
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def reload_kitty_config() -> None:
|
||||||
|
from kittens.tui.utils import kitty_opts
|
||||||
|
kitty_opts.clear_cached()
|
||||||
|
kitty_opts()
|
||||||
|
|
||||||
|
|
||||||
|
def prewarm() -> None:
|
||||||
|
reload_kitty_config()
|
||||||
|
for kitten in ('hints', 'ssh', 'unicode_input', 'ask', 'show_error'):
|
||||||
|
import_module(f'kittens.{kitten}.main')
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryViewReadWrapperBytes(io.BufferedIOBase):
|
||||||
|
|
||||||
|
def __init__(self, mw: memoryview):
|
||||||
|
self.mw = mw
|
||||||
|
self.pos = 0
|
||||||
|
|
||||||
|
def detach(self) -> io.RawIOBase:
|
||||||
|
raise io.UnsupportedOperation('detach() not supported')
|
||||||
|
|
||||||
|
def read(self, size: Optional[int] = -1) -> bytes:
|
||||||
|
if size is None or size < 0:
|
||||||
|
size = max(0, len(self.mw) - self.pos)
|
||||||
|
oldpos = self.pos
|
||||||
|
self.pos = min(len(self.mw), self.pos + size)
|
||||||
|
if self.pos <= oldpos:
|
||||||
|
return b''
|
||||||
|
return bytes(self.mw[oldpos:self.pos])
|
||||||
|
|
||||||
|
def readinto(self, b: 'WriteableBuffer') -> int:
|
||||||
|
if not isinstance(b, memoryview):
|
||||||
|
b = memoryview(b)
|
||||||
|
b = b.cast('B')
|
||||||
|
data = self.read(len(b))
|
||||||
|
n = len(data)
|
||||||
|
b[:n] = data
|
||||||
|
return n
|
||||||
|
readinto1 = readinto
|
||||||
|
|
||||||
|
def readall(self) -> bytes:
|
||||||
|
return self.read()
|
||||||
|
|
||||||
|
def write(self, b: 'ReadableBuffer') -> int:
|
||||||
|
raise io.UnsupportedOperation('readonly stream')
|
||||||
|
|
||||||
|
def readable(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryViewReadWrapper(io.TextIOWrapper):
|
||||||
|
|
||||||
|
def __init__(self, mw: memoryview):
|
||||||
|
super().__init__(cast(IO[bytes], MemoryViewReadWrapperBytes(mw)), encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
|
||||||
|
def child_main(cmd: Dict[str, Any], ready_fd: int) -> NoReturn:
|
||||||
|
cwd = cmd.get('cwd')
|
||||||
|
if cwd:
|
||||||
|
try:
|
||||||
|
os.chdir(cwd)
|
||||||
|
except OSError:
|
||||||
|
with suppress(OSError):
|
||||||
|
os.chdir('/')
|
||||||
|
os.setsid()
|
||||||
|
env = cmd.get('env')
|
||||||
|
if env is not None:
|
||||||
|
os.environ.clear()
|
||||||
|
os.environ.update(env)
|
||||||
|
argv = cmd.get('argv')
|
||||||
|
if argv:
|
||||||
|
sys.argv = list(argv)
|
||||||
|
poll = select.poll()
|
||||||
|
poll.register(ready_fd, select.POLLIN | select.POLLERR | select.POLLHUP)
|
||||||
|
poll.poll()
|
||||||
|
os.close(ready_fd)
|
||||||
|
tty_name = cmd.get('tty_name')
|
||||||
|
if tty_name:
|
||||||
|
sys.__stdout__.flush()
|
||||||
|
sys.__stderr__.flush()
|
||||||
|
establish_controlling_tty(tty_name, sys.__stdin__.fileno(), sys.__stdout__.fileno(), sys.__stderr__.fileno())
|
||||||
|
main_entry_point()
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def fork(shm_address: str, ready_fd: int) -> int:
|
||||||
|
sz = pos = 0
|
||||||
|
with SharedMemory(name=shm_address, unlink_on_exit=True) as shm:
|
||||||
|
data = shm.read_data_with_size()
|
||||||
|
cmd = json.loads(data)
|
||||||
|
sz = cmd.get('stdin_size', 0)
|
||||||
|
if sz:
|
||||||
|
pos = shm.tell()
|
||||||
|
shm.unlink_on_exit = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
child_pid = os.fork()
|
||||||
|
except OSError:
|
||||||
|
if sz:
|
||||||
|
with SharedMemory(shm_address, unlink_on_exit=True):
|
||||||
|
pass
|
||||||
|
if child_pid:
|
||||||
|
# master process
|
||||||
|
return child_pid
|
||||||
|
# child process
|
||||||
|
if shm.unlink_on_exit:
|
||||||
|
child_main(cmd, ready_fd)
|
||||||
|
else:
|
||||||
|
with SharedMemory(shm_address, unlink_on_exit=True) as shm:
|
||||||
|
stdin_data = memoryview(shm.mmap)[pos:pos + sz]
|
||||||
|
if stdin_data:
|
||||||
|
sys.stdin = MemoryViewReadWrapper(stdin_data)
|
||||||
|
try:
|
||||||
|
child_main(cmd, ready_fd)
|
||||||
|
finally:
|
||||||
|
stdin_data.release()
|
||||||
|
sys.stdin = sys.__stdin__
|
||||||
|
|
||||||
|
|
||||||
|
def main(args: List[str] = sys.argv) -> None:
|
||||||
|
read_signal_fd, write_signal_fd = safe_pipe()
|
||||||
|
notify_child_death_fd = int(sys.argv[-1])
|
||||||
|
os.set_blocking(notify_child_death_fd, False)
|
||||||
|
signal.set_wakeup_fd(write_signal_fd)
|
||||||
|
signal.signal(signal.SIGCHLD, lambda *a: None)
|
||||||
|
signal.siginterrupt(signal.SIGCHLD, False)
|
||||||
|
prewarm()
|
||||||
|
stdin_fd = sys.__stdin__.fileno()
|
||||||
|
os.set_blocking(stdin_fd, False)
|
||||||
|
stdout_fd = sys.__stdout__.fileno()
|
||||||
|
os.set_blocking(stdout_fd, False)
|
||||||
|
poll = select.poll()
|
||||||
|
poll.register(stdin_fd, select.POLLIN | basic_events)
|
||||||
|
poll.register(read_signal_fd, select.POLLIN | basic_events)
|
||||||
|
input_buf = output_buf = child_death_buf = b''
|
||||||
|
child_ready_fds: Dict[int, int] = {}
|
||||||
|
child_id_map: Dict[int, int] = {}
|
||||||
|
self_pid = os.getpid()
|
||||||
|
|
||||||
|
def check_event(event: int, err_msg: str) -> None:
|
||||||
|
if event & hangup_events:
|
||||||
|
raise SystemExit(0)
|
||||||
|
if event & error_events:
|
||||||
|
raise SystemExit(err_msg)
|
||||||
|
|
||||||
|
def handle_input(event: int) -> None:
|
||||||
|
nonlocal input_buf, output_buf
|
||||||
|
check_event(event, 'Polling of STDIN failed')
|
||||||
|
if not (event & select.POLLIN):
|
||||||
|
return
|
||||||
|
d = os.read(stdin_fd, io.DEFAULT_BUFFER_SIZE)
|
||||||
|
if not d:
|
||||||
|
raise SystemExit(0)
|
||||||
|
input_buf += d
|
||||||
|
while (idx := input_buf.find(b'\n')) > -1:
|
||||||
|
line = input_buf[:idx].decode()
|
||||||
|
input_buf = input_buf[idx+1:]
|
||||||
|
cmd, _, payload = line.partition(':')
|
||||||
|
if cmd == 'reload_kitty_config':
|
||||||
|
reload_kitty_config()
|
||||||
|
elif cmd == 'ready':
|
||||||
|
child_id = int(payload)
|
||||||
|
cfd = child_ready_fds.pop(child_id)
|
||||||
|
if cfd is not None:
|
||||||
|
os.write(cfd, b'1')
|
||||||
|
os.close(cfd)
|
||||||
|
elif cmd == 'fork':
|
||||||
|
read_fd, write_fd = safe_pipe(False)
|
||||||
|
remove_cloexec(read_fd)
|
||||||
|
try:
|
||||||
|
child_pid = fork(payload, read_fd)
|
||||||
|
except Exception as e:
|
||||||
|
es = str(e).replace('\n', ' ')
|
||||||
|
output_buf += f'ERR:{es}\n'.encode()
|
||||||
|
else:
|
||||||
|
if os.getpid() == self_pid:
|
||||||
|
child_id = len(child_id_map) + 1
|
||||||
|
child_id_map[child_id] = child_pid
|
||||||
|
child_ready_fds[child_id] = write_fd
|
||||||
|
output_buf += f'CHILD:{child_id}:{child_pid}\n'.encode()
|
||||||
|
finally:
|
||||||
|
if os.getpid() == self_pid:
|
||||||
|
os.close(read_fd)
|
||||||
|
elif cmd == 'echo':
|
||||||
|
output_buf += f'{payload}\n'.encode()
|
||||||
|
|
||||||
|
def handle_output(event: int) -> None:
|
||||||
|
nonlocal output_buf
|
||||||
|
check_event(event, 'Polling of STDOUT failed')
|
||||||
|
if not (event & select.POLLOUT):
|
||||||
|
return
|
||||||
|
if output_buf:
|
||||||
|
n = os.write(stdout_fd, output_buf)
|
||||||
|
if not n:
|
||||||
|
raise SystemExit(0)
|
||||||
|
output_buf = output_buf[n:]
|
||||||
|
if not output_buf:
|
||||||
|
poll.unregister(stdout_fd)
|
||||||
|
|
||||||
|
def handle_notify_child_death(event: int) -> None:
|
||||||
|
nonlocal child_death_buf
|
||||||
|
check_event(event, 'Polling of notify child death fd failed')
|
||||||
|
if not (event & select.POLLOUT):
|
||||||
|
return
|
||||||
|
if child_death_buf:
|
||||||
|
n = os.write(notify_child_death_fd, child_death_buf)
|
||||||
|
if not n:
|
||||||
|
raise SystemExit(0)
|
||||||
|
child_death_buf = child_death_buf[n:]
|
||||||
|
if not child_death_buf:
|
||||||
|
poll.unregister(notify_child_death_fd)
|
||||||
|
|
||||||
|
def handle_signal(event: int) -> None:
|
||||||
|
nonlocal child_death_buf
|
||||||
|
check_event(event, 'Polling of signal fd failed')
|
||||||
|
if not (event & select.POLLIN):
|
||||||
|
return
|
||||||
|
d = os.read(read_signal_fd, io.DEFAULT_BUFFER_SIZE)
|
||||||
|
if not d:
|
||||||
|
raise SystemExit(0)
|
||||||
|
signals = set(bytearray(d))
|
||||||
|
if signal.SIGCHLD in signals:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
pid, exit_status = os.waitpid(-1, os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
# a zero return means there is at least one child process
|
||||||
|
# existing and no exit status is available
|
||||||
|
if pid == 0:
|
||||||
|
break
|
||||||
|
matched_child_id = -1
|
||||||
|
for child_id, child_pid in child_id_map.items():
|
||||||
|
if child_pid == pid:
|
||||||
|
matched_child_id = child_id
|
||||||
|
break
|
||||||
|
if matched_child_id > -1:
|
||||||
|
del child_id_map[matched_child_id]
|
||||||
|
child_ready_fds.pop(matched_child_id, None)
|
||||||
|
child_death_buf += f'{pid}\n'.encode()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
if output_buf:
|
||||||
|
poll.register(stdout_fd, select.POLLOUT | basic_events)
|
||||||
|
if child_death_buf:
|
||||||
|
poll.register(notify_child_death_fd, select.POLLOUT | basic_events)
|
||||||
|
for (q, event) in poll.poll():
|
||||||
|
if q == stdin_fd:
|
||||||
|
handle_input(event)
|
||||||
|
elif q == stdout_fd:
|
||||||
|
handle_output(event)
|
||||||
|
elif q == read_signal_fd:
|
||||||
|
handle_signal(event)
|
||||||
|
elif q == notify_child_death_fd:
|
||||||
|
handle_notify_child_death(event)
|
||||||
|
except (KeyboardInterrupt, EOFError, BrokenPipeError):
|
||||||
|
if os.getpid() == self_pid:
|
||||||
|
raise SystemExit(1)
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
if os.getpid() == self_pid:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
if os.getpid() == self_pid:
|
||||||
|
for fmd in child_ready_fds.values():
|
||||||
|
with suppress(OSError):
|
||||||
|
os.close(fmd)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -66,7 +66,7 @@ def query(cls: Type[Query]) -> Type[Query]:
|
|||||||
class TerminalName(Query):
|
class TerminalName(Query):
|
||||||
name: str = 'name'
|
name: str = 'name'
|
||||||
override_query_name: str = 'name'
|
override_query_name: str = 'name'
|
||||||
help_text: str = f'Terminal name ({names[0]})'
|
help_text: str = f'Terminal name (e.g. :code:`{names[0]}`)'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_result(opts: Options) -> str:
|
def get_result(opts: Options) -> str:
|
||||||
@@ -76,7 +76,7 @@ class TerminalName(Query):
|
|||||||
@query
|
@query
|
||||||
class TerminalVersion(Query):
|
class TerminalVersion(Query):
|
||||||
name: str = 'version'
|
name: str = 'version'
|
||||||
help_text: str = 'Terminal version, for e.g.: 0.19.2'
|
help_text: str = f'Terminal version (e.g. :code:`{str_version}`)'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_result(opts: Options) -> str:
|
def get_result(opts: Options) -> str:
|
||||||
@@ -86,7 +86,7 @@ class TerminalVersion(Query):
|
|||||||
@query
|
@query
|
||||||
class AllowHyperlinks(Query):
|
class AllowHyperlinks(Query):
|
||||||
name: str = 'allow_hyperlinks'
|
name: str = 'allow_hyperlinks'
|
||||||
help_text: str = 'The :opt:`setting <allow_hyperlinks>` for allowing hyperlinks can be yes, no or ask'
|
help_text: str = 'The config option :opt:`allow_hyperlinks` in :file:`kitty.conf` for allowing hyperlinks can be :code:`yes`, :code:`no` or :code:`ask`'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_result(opts: Options) -> str:
|
def get_result(opts: Options) -> str:
|
||||||
@@ -154,7 +154,7 @@ class FontSize(Query):
|
|||||||
@query
|
@query
|
||||||
class ClipboardControl(Query):
|
class ClipboardControl(Query):
|
||||||
name: str = 'clipboard_control'
|
name: str = 'clipboard_control'
|
||||||
help_text: str = 'The :opt:`setting <clipboard_control>` for allowing reads/writes to/from the clipboard'
|
help_text: str = 'The config option :opt:`clipboard_control` in :file:`kitty.conf` for allowing reads/writes to/from the clipboard'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_result(opts: Options) -> str:
|
def get_result(opts: Options) -> str:
|
||||||
@@ -173,17 +173,22 @@ def do_queries(queries: Iterable[str], cli_opts: QueryTerminalCLIOptions) -> Dic
|
|||||||
actions = tuple(all_queries[x]() for x in queries)
|
actions = tuple(all_queries[x]() for x in queries)
|
||||||
qstring = ''.join(a.query_code() for a in actions)
|
qstring = ''.join(a.query_code() for a in actions)
|
||||||
received = b''
|
received = b''
|
||||||
|
pat = re.compile(rb'\x1b\[\?.+?c')
|
||||||
|
|
||||||
def more_needed(data: bytes) -> bool:
|
def more_needed(data: bytes) -> bool:
|
||||||
nonlocal received
|
nonlocal received
|
||||||
received += data
|
received += data
|
||||||
|
has_da1_response = pat.search(received) is not None
|
||||||
|
if has_da1_response:
|
||||||
|
return False
|
||||||
for a in actions:
|
for a in actions:
|
||||||
if a.more_needed(received):
|
if a.more_needed(received):
|
||||||
return True
|
return True
|
||||||
return False
|
return has_da1_response
|
||||||
|
|
||||||
with TTYIO() as ttyio:
|
with TTYIO() as ttyio:
|
||||||
ttyio.send(qstring)
|
ttyio.send(qstring)
|
||||||
|
ttyio.send('\x1b[c') # DA1 query https://vt100.net/docs/vt510-rm/DA1.html
|
||||||
ttyio.recv(more_needed, timeout=cli_opts.wait_for)
|
ttyio.recv(more_needed, timeout=cli_opts.wait_for)
|
||||||
|
|
||||||
return {a.name: a.output_line() for a in actions}
|
return {a.name: a.output_line() for a in actions}
|
||||||
@@ -200,30 +205,28 @@ querying it.
|
|||||||
|
|
||||||
|
|
||||||
help_text = '''\
|
help_text = '''\
|
||||||
Query the terminal this kitten is run in for various
|
Query the terminal this kitten is run in for various capabilities. This sends
|
||||||
capabilities. This sends escape codes to the terminal
|
escape codes to the terminal and based on its response prints out data about
|
||||||
and based on its response prints out data about supported
|
supported capabilities. Note that this is a blocking operation, since it has to
|
||||||
capabilities. Note that this is a blocking operation, since
|
wait for a response from the terminal. You can control the maximum wait time via
|
||||||
it has to wait for a response from the terminal. You can control
|
the :code:`--wait-for` option.
|
||||||
the maximum wait time via the ``--wait-for`` option.
|
|
||||||
|
|
||||||
The output is lines of the form::
|
The output is lines of the form::
|
||||||
|
|
||||||
query: data
|
query: data
|
||||||
|
|
||||||
If a particular query is unsupported by the running kitty version,
|
If a particular :italic:`query` is unsupported by the running kitty version, the
|
||||||
the data will be blank.
|
:italic:`data` will be blank.
|
||||||
|
|
||||||
Note that when calling this from another program, be very
|
Note that when calling this from another program, be very careful not to perform
|
||||||
careful not to perform any I/O on the terminal device
|
any I/O on the terminal device until this kitten exits.
|
||||||
until the kitten exits.
|
|
||||||
|
|
||||||
Available queries are:
|
Available queries are:
|
||||||
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
'''.format('\n'.join(
|
'''.format('\n'.join(
|
||||||
f'``{name}``\n {c.help_text}\n' for name, c in all_queries.items()))
|
f':code:`{name}`:\n {c.help_text}\n' for name, c in all_queries.items()))
|
||||||
usage = '[query1 query2 ...]'
|
usage = '[query1 query2 ...]'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ from ..tui.operations import (
|
|||||||
from ..tui.utils import get_key_press
|
from ..tui.utils import get_key_press
|
||||||
|
|
||||||
|
|
||||||
|
is_ssh_kitten_sentinel = '!#*&$#($ssh-kitten)(##$'
|
||||||
|
|
||||||
|
|
||||||
def key(x: str) -> str:
|
def key(x: str) -> str:
|
||||||
return styled(x, bold=True, fg='green')
|
return styled(x, bold=True, fg='green')
|
||||||
|
|
||||||
@@ -53,10 +56,9 @@ The data used to connect over ssh.
|
|||||||
|
|
||||||
|
|
||||||
def show_error(msg: str) -> None:
|
def show_error(msg: str) -> None:
|
||||||
print(styled(msg, fg='red'))
|
print(styled(msg, fg='red'), file=sys.stderr)
|
||||||
print()
|
print()
|
||||||
print('Press any key to exit...')
|
print('Press any key to quit', flush=True)
|
||||||
sys.stdout.flush()
|
|
||||||
with raw_mode():
|
with raw_mode():
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@@ -112,15 +114,25 @@ class ControlMaster:
|
|||||||
self.remote_path = remote_path
|
self.remote_path = remote_path
|
||||||
self.dest = dest
|
self.dest = dest
|
||||||
self.tdir = ''
|
self.tdir = ''
|
||||||
|
self.last_error_log = ''
|
||||||
self.cmd_prefix = cmd = [
|
self.cmd_prefix = cmd = [
|
||||||
conn_data.binary, '-o', f'ControlPath=~/.ssh/kitty-master-{os.getpid()}-%r@%h:%p',
|
conn_data.binary, '-o', f'ControlPath=~/.ssh/kitty-master-{os.getpid()}-%r@%h:%p',
|
||||||
'-o', 'TCPKeepAlive=yes', '-o', 'ControlPersist=yes'
|
'-o', 'TCPKeepAlive=yes', '-o', 'ControlPersist=yes'
|
||||||
]
|
]
|
||||||
if conn_data.port:
|
self.is_ssh_kitten = conn_data.binary is is_ssh_kitten_sentinel
|
||||||
cmd.extend(['-p', str(conn_data.port)])
|
if self.is_ssh_kitten:
|
||||||
if conn_data.identity_file:
|
del cmd[:]
|
||||||
cmd.extend(['-i', conn_data.identity_file])
|
self.batch_cmd_prefix = cmd
|
||||||
self.batch_cmd_prefix = cmd + ['-o', 'BatchMode=yes']
|
sk_cmdline = json.loads(conn_data.identity_file)
|
||||||
|
while '-t' in sk_cmdline:
|
||||||
|
sk_cmdline.remove('-t')
|
||||||
|
cmd.extend(sk_cmdline[:-2])
|
||||||
|
else:
|
||||||
|
if conn_data.port:
|
||||||
|
cmd.extend(['-p', str(conn_data.port)])
|
||||||
|
if conn_data.identity_file:
|
||||||
|
cmd.extend(['-i', conn_data.identity_file])
|
||||||
|
self.batch_cmd_prefix = cmd + ['-o', 'BatchMode=yes']
|
||||||
|
|
||||||
def check_call(self, cmd: List[str]) -> None:
|
def check_call(self, cmd: List[str]) -> None:
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL)
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL)
|
||||||
@@ -130,31 +142,37 @@ class ControlMaster:
|
|||||||
raise Exception(f'The ssh command: {shlex.join(cmd)} failed with exit code {p.returncode} and output: {out}')
|
raise Exception(f'The ssh command: {shlex.join(cmd)} failed with exit code {p.returncode} and output: {out}')
|
||||||
|
|
||||||
def __enter__(self) -> 'ControlMaster':
|
def __enter__(self) -> 'ControlMaster':
|
||||||
self.check_call(
|
if not self.is_ssh_kitten:
|
||||||
self.cmd_prefix + ['-o', 'ControlMaster=auto', '-fN', self.conn_data.hostname])
|
self.check_call(
|
||||||
self.check_call(
|
self.cmd_prefix + ['-o', 'ControlMaster=auto', '-fN', self.conn_data.hostname])
|
||||||
self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname])
|
self.check_call(
|
||||||
|
self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname])
|
||||||
if not self.dest:
|
if not self.dest:
|
||||||
self.tdir = tempfile.mkdtemp()
|
self.tdir = tempfile.mkdtemp()
|
||||||
self.dest = os.path.join(self.tdir, os.path.basename(self.remote_path))
|
self.dest = os.path.join(self.tdir, os.path.basename(self.remote_path))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, *a: Any) -> None:
|
def __exit__(self, *a: Any) -> None:
|
||||||
subprocess.Popen(
|
if not self.is_ssh_kitten:
|
||||||
self.batch_cmd_prefix + ['-O', 'exit', self.conn_data.hostname],
|
subprocess.Popen(
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL
|
self.batch_cmd_prefix + ['-O', 'exit', self.conn_data.hostname],
|
||||||
).wait()
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL
|
||||||
|
).wait()
|
||||||
if self.tdir:
|
if self.tdir:
|
||||||
shutil.rmtree(self.tdir)
|
shutil.rmtree(self.tdir)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_alive(self) -> bool:
|
def is_alive(self) -> bool:
|
||||||
|
if self.is_ssh_kitten:
|
||||||
|
return True
|
||||||
return subprocess.Popen(
|
return subprocess.Popen(
|
||||||
self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname],
|
self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname],
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL
|
||||||
).wait() == 0
|
).wait() == 0
|
||||||
|
|
||||||
def check_hostname_matches(self) -> bool:
|
def check_hostname_matches(self) -> bool:
|
||||||
|
if self.is_ssh_kitten:
|
||||||
|
return True
|
||||||
cp = subprocess.run(self.batch_cmd_prefix + [self.conn_data.hostname, 'hostname', '-f'], stdout=subprocess.PIPE,
|
cp = subprocess.run(self.batch_cmd_prefix + [self.conn_data.hostname, 'hostname', '-f'], stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL)
|
stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL)
|
||||||
if cp.returncode == 0:
|
if cp.returncode == 0:
|
||||||
@@ -181,21 +199,35 @@ class ControlMaster:
|
|||||||
return response == 'y'
|
return response == 'y'
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def show_error(self, msg: str) -> None:
|
||||||
|
if self.last_error_log:
|
||||||
|
print(self.last_error_log, file=sys.stderr)
|
||||||
|
self.last_error_log = ''
|
||||||
|
show_error(msg)
|
||||||
|
|
||||||
def download(self) -> bool:
|
def download(self) -> bool:
|
||||||
|
cmdline = self.batch_cmd_prefix + [self.conn_data.hostname, 'cat', self.remote_path]
|
||||||
with open(self.dest, 'wb') as f:
|
with open(self.dest, 'wb') as f:
|
||||||
return subprocess.run(
|
cp = subprocess.run(cmdline, stdout=f, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL)
|
||||||
self.batch_cmd_prefix + [self.conn_data.hostname, 'cat', self.remote_path],
|
if cp.returncode != 0:
|
||||||
stdout=f, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL
|
self.last_error_log = f'The command: {shlex.join(cmdline)} failed\n' + cp.stderr.decode()
|
||||||
).returncode == 0
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def upload(self, suppress_output: bool = True) -> bool:
|
def upload(self, suppress_output: bool = True) -> bool:
|
||||||
cmd_prefix = self.cmd_prefix if suppress_output else self.batch_cmd_prefix
|
cmd_prefix = self.cmd_prefix if suppress_output else self.batch_cmd_prefix
|
||||||
cmd = cmd_prefix + [self.conn_data.hostname, 'cat', '>', self.remote_path]
|
cmd = cmd_prefix + [self.conn_data.hostname, 'cat', '>', self.remote_path]
|
||||||
if not suppress_output:
|
if not suppress_output:
|
||||||
print(' '.join(map(shlex.quote, cmd)))
|
print(shlex.join(cmd))
|
||||||
redirect = subprocess.DEVNULL if suppress_output else None
|
|
||||||
with open(self.dest, 'rb') as f:
|
with open(self.dest, 'rb') as f:
|
||||||
return subprocess.run(cmd, stdout=redirect, stderr=redirect, stdin=f).returncode == 0
|
if suppress_output:
|
||||||
|
cp = subprocess.run(cmd, stdin=f, capture_output=True)
|
||||||
|
if cp.returncode == 0:
|
||||||
|
return True
|
||||||
|
self.last_error_log = f'The command: {shlex.join(cmd)} failed\n' + cp.stdout.decode()
|
||||||
|
else:
|
||||||
|
return subprocess.run(cmd, stdin=f).returncode == 0
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
Result = Optional[str]
|
Result = Optional[str]
|
||||||
@@ -208,7 +240,7 @@ def main(args: List[str]) -> Result:
|
|||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
if e.code != 0:
|
if e.code != 0:
|
||||||
print(e.args[0])
|
print(e.args[0])
|
||||||
input('Press enter to quit...')
|
input('Press Enter to quit')
|
||||||
raise SystemExit(e.code)
|
raise SystemExit(e.code)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -236,7 +268,7 @@ def save_as(conn_data: SSHConnectionData, remote_path: str, cli_opts: RemoteFile
|
|||||||
last_used_path = tempfile.gettempdir()
|
last_used_path = tempfile.gettempdir()
|
||||||
last_used_file = os.path.join(last_used_path, os.path.basename(remote_path))
|
last_used_file = os.path.join(last_used_path, os.path.basename(remote_path))
|
||||||
print(
|
print(
|
||||||
'Where do you wish to save the file? Leaving it blank will save it as:',
|
'Where do you want to save the file? Leaving it blank will save it as:',
|
||||||
styled(last_used_file, fg='yellow')
|
styled(last_used_file, fg='yellow')
|
||||||
)
|
)
|
||||||
print('Relative paths will be resolved from:', styled(os.getcwd(), fg_intense=True, bold=True))
|
print('Relative paths will be resolved from:', styled(os.getcwd(), fg_intense=True, bold=True))
|
||||||
@@ -278,11 +310,15 @@ def save_as(conn_data: SSHConnectionData, remote_path: str, cli_opts: RemoteFile
|
|||||||
with ControlMaster(conn_data, remote_path, cli_opts, dest=dest) as master:
|
with ControlMaster(conn_data, remote_path, cli_opts, dest=dest) as master:
|
||||||
if master.check_hostname_matches():
|
if master.check_hostname_matches():
|
||||||
if not master.download():
|
if not master.download():
|
||||||
show_error('Failed to copy file from remote machine')
|
master.show_error('Failed to copy file from remote machine')
|
||||||
|
|
||||||
|
|
||||||
def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
|
def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
|
||||||
conn_data = SSHConnectionData(*json.loads(cli_opts.ssh_connection_data or ''))
|
cli_data = json.loads(cli_opts.ssh_connection_data or '')
|
||||||
|
if cli_data and cli_data[0] == is_ssh_kitten_sentinel:
|
||||||
|
conn_data = SSHConnectionData(is_ssh_kitten_sentinel, cli_data[-1], -1, identity_file=json.dumps(cli_data[1:]))
|
||||||
|
else:
|
||||||
|
conn_data = SSHConnectionData(*cli_data)
|
||||||
remote_path = cli_opts.path or ''
|
remote_path = cli_opts.path or ''
|
||||||
if action == 'open':
|
if action == 'open':
|
||||||
print('Opening', cli_opts.path, 'from', cli_opts.hostname)
|
print('Opening', cli_opts.path, 'from', cli_opts.hostname)
|
||||||
@@ -291,7 +327,7 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
|
|||||||
if master.check_hostname_matches():
|
if master.check_hostname_matches():
|
||||||
if master.download():
|
if master.download():
|
||||||
return dest
|
return dest
|
||||||
show_error('Failed to copy file from remote machine')
|
master.show_error('Failed to copy file from remote machine')
|
||||||
elif action == 'edit':
|
elif action == 'edit':
|
||||||
print('Editing', cli_opts.path, 'from', cli_opts.hostname)
|
print('Editing', cli_opts.path, 'from', cli_opts.hostname)
|
||||||
editor = get_editor()
|
editor = get_editor()
|
||||||
@@ -299,7 +335,7 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
|
|||||||
if not master.check_hostname_matches():
|
if not master.check_hostname_matches():
|
||||||
return None
|
return None
|
||||||
if not master.download():
|
if not master.download():
|
||||||
show_error(f'Failed to download {remote_path}')
|
master.show_error(f'Failed to download {remote_path}')
|
||||||
return None
|
return None
|
||||||
mtime = os.path.getmtime(master.dest)
|
mtime = os.path.getmtime(master.dest)
|
||||||
print(reset_terminal(), end='', flush=True)
|
print(reset_terminal(), end='', flush=True)
|
||||||
@@ -314,9 +350,9 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
|
|||||||
print(reset_terminal(), end='', flush=True)
|
print(reset_terminal(), end='', flush=True)
|
||||||
if master.is_alive:
|
if master.is_alive:
|
||||||
if not master.upload(suppress_output=False):
|
if not master.upload(suppress_output=False):
|
||||||
show_error(f'Failed to upload {remote_path}')
|
master.show_error(f'Failed to upload {remote_path}')
|
||||||
else:
|
else:
|
||||||
show_error(f'Failed to upload {remote_path}, SSH master process died')
|
master.show_error(f'Failed to upload {remote_path}, SSH master process died')
|
||||||
elif action == 'save':
|
elif action == 'save':
|
||||||
print('Saving', cli_opts.path, 'from', cli_opts.hostname)
|
print('Saving', cli_opts.path, 'from', cli_opts.hostname)
|
||||||
save_as(conn_data, remote_path, cli_opts)
|
save_as(conn_data, remote_path, cli_opts)
|
||||||
|
|||||||
@@ -175,4 +175,4 @@ def main() -> None:
|
|||||||
print('Unhandled exception running kitten:')
|
print('Unhandled exception running kitten:')
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
input('Press Enter to quit...')
|
input('Press Enter to quit')
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import termios
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
from kitty.cli_stub import ErrorCLIOptions
|
from kitty.cli_stub import ErrorCLIOptions
|
||||||
|
from kitty.fast_data_types import open_tty
|
||||||
|
from kitty.utils import hold_till_enter, no_echo, write_all
|
||||||
|
|
||||||
from ..tui.operations import styled
|
from ..tui.operations import styled
|
||||||
|
|
||||||
@@ -21,14 +25,35 @@ The title for the error message.
|
|||||||
def real_main(args: List[str]) -> None:
|
def real_main(args: List[str]) -> None:
|
||||||
msg = 'Show an error message'
|
msg = 'Show an error message'
|
||||||
cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'hints', result_class=ErrorCLIOptions)
|
cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'hints', result_class=ErrorCLIOptions)
|
||||||
error_message = sys.stdin.buffer.read().decode('utf-8')
|
data = json.loads(sys.stdin.buffer.read())
|
||||||
sys.stdin = open(os.ctermid())
|
error_message = data['msg']
|
||||||
if cli_opts.title:
|
if cli_opts.title:
|
||||||
print(styled(cli_opts.title, fg_intense=True, fg='red', bold=True))
|
print(styled(cli_opts.title, fg_intense=True, fg='red', bold=True))
|
||||||
print()
|
print()
|
||||||
print(error_message)
|
print(error_message, flush=True)
|
||||||
print()
|
if data.get('tb'):
|
||||||
input('\x1b[1;32mPress Enter to close\x1b[m')
|
import select
|
||||||
|
from kittens.tui.operations import init_state, set_cursor_visible
|
||||||
|
fd, original_termios = open_tty()
|
||||||
|
msg = '\n\r\x1b[1;32mPress e to see detailed traceback or any other key to exit\x1b[m'
|
||||||
|
write_all(fd, msg)
|
||||||
|
write_all(fd, init_state(alternate_screen=False, kitty_keyboard_mode=False) + set_cursor_visible(False))
|
||||||
|
with no_echo(fd):
|
||||||
|
termios.tcdrain(fd)
|
||||||
|
while True:
|
||||||
|
rd = select.select([fd], [], [])[0]
|
||||||
|
if not rd:
|
||||||
|
break
|
||||||
|
q = os.read(fd, 1)
|
||||||
|
if q in b'eE':
|
||||||
|
break
|
||||||
|
return
|
||||||
|
if data.get('tb'):
|
||||||
|
tb = data['tb']
|
||||||
|
for ln in tb.splitlines():
|
||||||
|
print('\r\n', ln, sep='', end='')
|
||||||
|
print(flush=True)
|
||||||
|
hold_till_enter()
|
||||||
|
|
||||||
|
|
||||||
def main(args: List[str]) -> None:
|
def main(args: List[str]) -> None:
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def print_key(raw: bytearray) -> None:
|
|||||||
unix += chr(ch)
|
unix += chr(ch)
|
||||||
print(unix + '\t\t', end='')
|
print(unix + '\t\t', end='')
|
||||||
for ch in raw:
|
for ch in raw:
|
||||||
x = chr(ch).encode('ascii')
|
x = chr(ch).encode('utf-8')
|
||||||
print(styled(repr(x)[2:-1], fg='yellow'), end='')
|
print(styled(repr(x)[2:-1], fg='yellow'), end='')
|
||||||
print(end='\r\n', flush=True)
|
print(end='\r\n', flush=True)
|
||||||
|
|
||||||
@@ -57,13 +57,15 @@ OPTIONS = r'''
|
|||||||
default=normal
|
default=normal
|
||||||
type=choices
|
type=choices
|
||||||
choices=normal,application,kitty,unchanged
|
choices=normal,application,kitty,unchanged
|
||||||
The keyboard mode to use when showing keys. "normal" mode is with DECCKM reset and "application" mode is with
|
The keyboard mode to use when showing keys. :code:`normal` mode is with DECCKM
|
||||||
DECCKM set. "kitty" is the full kitty extended keyboard protocol.
|
reset and :code:`application` mode is with DECCKM set. :code:`kitty` is the full
|
||||||
|
kitty extended keyboard protocol.
|
||||||
'''.format
|
'''.format
|
||||||
|
help_text = 'Show the codes generated by the terminal for key presses in various keyboard modes'
|
||||||
|
|
||||||
|
|
||||||
def main(args: List[str]) -> None:
|
def main(args: List[str]) -> None:
|
||||||
cli_opts, items = parse_args(args[1:], OPTIONS, '', '', 'kitty +kitten show_key', result_class=ShowKeyCLIOptions)
|
cli_opts, items = parse_args(args[1:], OPTIONS, '', help_text, 'kitty +kitten show_key', result_class=ShowKeyCLIOptions)
|
||||||
if cli_opts.key_mode == 'kitty':
|
if cli_opts.key_mode == 'kitty':
|
||||||
from .kitty_mode import main as kitty_main
|
from .kitty_mode import main as kitty_main
|
||||||
return kitty_main()
|
return kitty_main()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from typing import Callable, Dict, Iterable, Iterator, Sequence, Tuple
|
|||||||
|
|
||||||
from kitty.complete import Completions, complete_files_and_dirs, debug
|
from kitty.complete import Completions, complete_files_and_dirs, debug
|
||||||
from kitty.types import run_once
|
from kitty.types import run_once
|
||||||
|
from .utils import ssh_options
|
||||||
|
|
||||||
debug
|
debug
|
||||||
|
|
||||||
@@ -71,47 +72,6 @@ def known_hosts() -> Tuple[str, ...]:
|
|||||||
return tuple(sorted(filter(lambda x: '*' not in x and '[' not in x, set(iter_known_hosts()))))
|
return tuple(sorted(filter(lambda x: '*' not in x and '[' not in x, set(iter_known_hosts()))))
|
||||||
|
|
||||||
|
|
||||||
@run_once
|
|
||||||
def ssh_options() -> Dict[str, str]:
|
|
||||||
try:
|
|
||||||
p = subprocess.run(['ssh'], stderr=subprocess.PIPE, encoding='utf-8')
|
|
||||||
raw = p.stderr or ''
|
|
||||||
except FileNotFoundError:
|
|
||||||
return {
|
|
||||||
'4': '', '6': '', 'A': '', 'a': '', 'C': '', 'f': '', 'G': '', 'g': '', 'K': '', 'k': '',
|
|
||||||
'M': '', 'N': '', 'n': '', 'q': '', 's': '', 'T': '', 't': '', 'V': '', 'v': '', 'X': '',
|
|
||||||
'x': '', 'Y': '', 'y': '', 'B': 'bind_interface', 'b': 'bind_address', 'c': 'cipher_spec',
|
|
||||||
'D': '[bind_address:]port', 'E': 'log_file', 'e': 'escape_char', 'F': 'configfile', 'I': 'pkcs11',
|
|
||||||
'i': 'identity_file', 'J': '[user@]host[:port]', 'L': 'address', 'l': 'login_name', 'm': 'mac_spec',
|
|
||||||
'O': 'ctl_cmd', 'o': 'option', 'p': 'port', 'Q': 'query_option', 'R': 'address',
|
|
||||||
'S': 'ctl_path', 'W': 'host:port', 'w': 'local_tun[:remote_tun]'
|
|
||||||
}
|
|
||||||
|
|
||||||
ans: Dict[str, str] = {}
|
|
||||||
pos = 0
|
|
||||||
while True:
|
|
||||||
pos = raw.find('[', pos)
|
|
||||||
if pos < 0:
|
|
||||||
break
|
|
||||||
num = 1
|
|
||||||
epos = pos
|
|
||||||
while num > 0:
|
|
||||||
epos += 1
|
|
||||||
if raw[epos] not in '[]':
|
|
||||||
continue
|
|
||||||
num += 1 if raw[epos] == '[' else -1
|
|
||||||
q = raw[pos+1:epos]
|
|
||||||
pos = epos
|
|
||||||
if len(q) < 2 or q[0] != '-':
|
|
||||||
continue
|
|
||||||
if ' ' in q:
|
|
||||||
opt, desc = q.split(' ', 1)
|
|
||||||
ans[opt[1:]] = desc
|
|
||||||
else:
|
|
||||||
ans.update(dict.fromkeys(q[1:], ''))
|
|
||||||
return ans
|
|
||||||
|
|
||||||
|
|
||||||
# option help {{{
|
# option help {{{
|
||||||
@run_once
|
@run_once
|
||||||
def option_help_map() -> Dict[str, str]:
|
def option_help_map() -> Dict[str, str]:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import atexit
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import glob
|
import glob
|
||||||
import io
|
import io
|
||||||
@@ -24,8 +23,8 @@ from contextlib import contextmanager, suppress
|
|||||||
from getpass import getuser
|
from getpass import getuser
|
||||||
from select import select
|
from select import select
|
||||||
from typing import (
|
from typing import (
|
||||||
Callable, Dict, Iterator, List, NoReturn, Optional, Sequence, Set, Tuple,
|
Any, Callable, Dict, Iterator, List, NoReturn, Optional, Sequence, Set,
|
||||||
Union
|
Tuple, Union, cast
|
||||||
)
|
)
|
||||||
|
|
||||||
from kittens.tui.operations import restore_colors, save_colors
|
from kittens.tui.operations import restore_colors, save_colors
|
||||||
@@ -33,16 +32,20 @@ from kitty.constants import (
|
|||||||
cache_dir, runtime_dir, shell_integration_dir, ssh_control_master_template,
|
cache_dir, runtime_dir, shell_integration_dir, ssh_control_master_template,
|
||||||
str_version, terminfo_dir
|
str_version, terminfo_dir
|
||||||
)
|
)
|
||||||
from kitty.options.types import Options
|
from kitty.shell_integration import as_str_literal
|
||||||
from kitty.shm import SharedMemory
|
from kitty.shm import SharedMemory
|
||||||
from kitty.types import run_once
|
from kitty.types import run_once
|
||||||
from kitty.utils import SSHConnectionData, set_echo as turn_off_echo, expandvars, resolve_abs_or_config_path
|
from kitty.utils import (
|
||||||
|
SSHConnectionData, expandvars, resolve_abs_or_config_path,
|
||||||
|
set_echo as turn_off_echo
|
||||||
|
)
|
||||||
|
|
||||||
from .completion import complete, ssh_options
|
from ..tui.utils import kitty_opts
|
||||||
from .config import init_config
|
from .config import init_config
|
||||||
from .copy import CopyInstruction
|
from .copy import CopyInstruction
|
||||||
from .options.types import Options as SSHOptions
|
from .options.types import Options as SSHOptions
|
||||||
from .options.utils import DELETE_ENV_VAR
|
from .options.utils import DELETE_ENV_VAR
|
||||||
|
from .utils import create_shared_memory, ssh_options
|
||||||
|
|
||||||
|
|
||||||
@run_once
|
@run_once
|
||||||
@@ -50,46 +53,54 @@ def ssh_exe() -> str:
|
|||||||
return shutil.which('ssh') or 'ssh'
|
return shutil.which('ssh') or 'ssh'
|
||||||
|
|
||||||
|
|
||||||
def is_kitten_cmdline(q: List[str]) -> bool:
|
def read_data_from_shared_memory(shm_name: str) -> Any:
|
||||||
if len(q) < 4:
|
with SharedMemory(shm_name, readonly=True) as shm:
|
||||||
return False
|
shm.unlink()
|
||||||
if os.path.basename(q[0]).lower() != 'kitty':
|
if shm.stats.st_uid != os.geteuid() or shm.stats.st_gid != os.getegid():
|
||||||
return False
|
raise ValueError('Incorrect owner on pwfile')
|
||||||
return q[1:3] == ['+kitten', 'ssh'] or q[1:4] == ['+', 'kitten', 'ssh']
|
mode = stat.S_IMODE(shm.stats.st_mode)
|
||||||
|
if mode != stat.S_IREAD:
|
||||||
|
raise ValueError('Incorrect permissions on pwfile')
|
||||||
def set_cwd_in_cmdline(cwd: str, argv: List[str]) -> None:
|
return json.loads(shm.read_data_with_size())
|
||||||
for i, arg in enumerate(tuple(argv)):
|
|
||||||
if arg.startswith('--kitten=cwd'):
|
|
||||||
argv[i] = f'--kitten=cwd={cwd}'
|
|
||||||
return
|
|
||||||
elif i > 0 and argv[i-1] == '--kitten' and (arg.startswith('cwd=') or arg.startswith('cwd ')):
|
|
||||||
argv[i] = cwd
|
|
||||||
return
|
|
||||||
idx = argv.index('ssh')
|
|
||||||
argv.insert(idx + 1, f'--kitten=cwd={cwd}')
|
|
||||||
|
|
||||||
|
|
||||||
# See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
|
# See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
|
||||||
quote_pat = re.compile('([\\`"\n])')
|
quote_pat = re.compile('([\\`"])')
|
||||||
|
|
||||||
|
|
||||||
def quote_env_val(x: str) -> str:
|
def quote_env_val(x: str, literal_quote: bool = False) -> str:
|
||||||
|
if literal_quote:
|
||||||
|
return as_str_literal(x)
|
||||||
x = quote_pat.sub(r'\\\1', x)
|
x = quote_pat.sub(r'\\\1', x)
|
||||||
x = x.replace('$(', r'\$(') # prevent execution with $()
|
x = x.replace('$(', r'\$(') # prevent execution with $()
|
||||||
return f'"{x}"'
|
return f'"{x}"'
|
||||||
|
|
||||||
|
|
||||||
def serialize_env(env: Dict[str, str], base_env: Dict[str, str]) -> bytes:
|
def serialize_env(literal_env: Dict[str, str], env: Dict[str, str], base_env: Dict[str, str], for_python: bool = False) -> bytes:
|
||||||
lines = []
|
lines = []
|
||||||
|
literal_quote = True
|
||||||
|
|
||||||
def a(k: str, val: str) -> None:
|
if for_python:
|
||||||
lines.append(f'export {shlex.quote(k)}={quote_env_val(val)}')
|
def a(k: str, val: str = '', prefix: str = 'export') -> None:
|
||||||
|
if val:
|
||||||
|
lines.append(f'{prefix} {json.dumps((k, val, literal_quote))}')
|
||||||
|
else:
|
||||||
|
lines.append(f'{prefix} {json.dumps((k,))}')
|
||||||
|
else:
|
||||||
|
def a(k: str, val: str = '', prefix: str = 'export') -> None:
|
||||||
|
if val:
|
||||||
|
lines.append(f'{prefix} {shlex.quote(k)}={quote_env_val(val, literal_quote)}')
|
||||||
|
else:
|
||||||
|
lines.append(f'{prefix} {shlex.quote(k)}')
|
||||||
|
|
||||||
|
for k, v in literal_env.items():
|
||||||
|
a(k, v)
|
||||||
|
|
||||||
|
literal_quote = False
|
||||||
for k in sorted(env):
|
for k in sorted(env):
|
||||||
v = env[k]
|
v = env[k]
|
||||||
if v == DELETE_ENV_VAR:
|
if v == DELETE_ENV_VAR:
|
||||||
lines.append(f'unset {shlex.quote(k)}')
|
a(k, prefix='unset')
|
||||||
elif v == '_kitty_copy_env_var_':
|
elif v == '_kitty_copy_env_var_':
|
||||||
q = base_env.get(k)
|
q = base_env.get(k)
|
||||||
if q is not None:
|
if q is not None:
|
||||||
@@ -99,17 +110,14 @@ def serialize_env(env: Dict[str, str], base_env: Dict[str, str]) -> bytes:
|
|||||||
return '\n'.join(lines).encode('utf-8')
|
return '\n'.join(lines).encode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
@run_once
|
def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str], compression: str = 'gz', literal_env: Dict[str, str] = {}) -> bytes:
|
||||||
def kitty_opts() -> Options:
|
|
||||||
from kitty.cli import create_default_opts
|
|
||||||
return create_default_opts()
|
|
||||||
|
|
||||||
|
|
||||||
def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str], compression: str = 'gz') -> bytes:
|
|
||||||
|
|
||||||
def normalize_tarinfo(tarinfo: tarfile.TarInfo) -> tarfile.TarInfo:
|
def normalize_tarinfo(tarinfo: tarfile.TarInfo) -> tarfile.TarInfo:
|
||||||
tarinfo.uname = tarinfo.gname = ''
|
tarinfo.uname = tarinfo.gname = ''
|
||||||
tarinfo.uid = tarinfo.gid = 0
|
tarinfo.uid = tarinfo.gid = 0
|
||||||
|
# some distro's like nix mess with installed file permissions so ensure
|
||||||
|
# files are at least readable and writable by owning user
|
||||||
|
tarinfo.mode |= stat.S_IWUSR | stat.S_IRUSR
|
||||||
return tarinfo
|
return tarinfo
|
||||||
|
|
||||||
def add_data_as_file(tf: tarfile.TarFile, arcname: str, data: Union[str, bytes]) -> tarfile.TarInfo:
|
def add_data_as_file(tf: tarfile.TarFile, arcname: str, data: Union[str, bytes]) -> tarfile.TarInfo:
|
||||||
@@ -139,6 +147,7 @@ def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str], compression: st
|
|||||||
if ssh_opts.shell_integration == 'inherited':
|
if ssh_opts.shell_integration == 'inherited':
|
||||||
ksi = get_effective_ksi_env_var(kitty_opts())
|
ksi = get_effective_ksi_env_var(kitty_opts())
|
||||||
else:
|
else:
|
||||||
|
from kitty.options.types import Options
|
||||||
from kitty.options.utils import shell_integration
|
from kitty.options.utils import shell_integration
|
||||||
ksi = get_effective_ksi_env_var(Options({'shell_integration': shell_integration(ssh_opts.shell_integration)}))
|
ksi = get_effective_ksi_env_var(Options({'shell_integration': shell_integration(ssh_opts.shell_integration)}))
|
||||||
|
|
||||||
@@ -146,11 +155,11 @@ def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str], compression: st
|
|||||||
'TERM': os.environ.get('TERM') or kitty_opts().term,
|
'TERM': os.environ.get('TERM') or kitty_opts().term,
|
||||||
'COLORTERM': 'truecolor',
|
'COLORTERM': 'truecolor',
|
||||||
}
|
}
|
||||||
|
env.update(ssh_opts.env)
|
||||||
for q in ('KITTY_WINDOW_ID', 'WINDOWID'):
|
for q in ('KITTY_WINDOW_ID', 'WINDOWID'):
|
||||||
val = os.environ.get(q)
|
val = os.environ.get(q)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
env[q] = val
|
env[q] = val
|
||||||
env.update(ssh_opts.env)
|
|
||||||
env['KITTY_SHELL_INTEGRATION'] = ksi or DELETE_ENV_VAR
|
env['KITTY_SHELL_INTEGRATION'] = ksi or DELETE_ENV_VAR
|
||||||
env['KITTY_SSH_KITTEN_DATA_DIR'] = ssh_opts.remote_dir
|
env['KITTY_SSH_KITTEN_DATA_DIR'] = ssh_opts.remote_dir
|
||||||
if ssh_opts.login_shell:
|
if ssh_opts.login_shell:
|
||||||
@@ -159,7 +168,7 @@ def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str], compression: st
|
|||||||
env['KITTY_LOGIN_CWD'] = ssh_opts.cwd
|
env['KITTY_LOGIN_CWD'] = ssh_opts.cwd
|
||||||
if ssh_opts.remote_kitty != 'no':
|
if ssh_opts.remote_kitty != 'no':
|
||||||
env['KITTY_REMOTE'] = ssh_opts.remote_kitty
|
env['KITTY_REMOTE'] = ssh_opts.remote_kitty
|
||||||
env_script = serialize_env(env, base_env)
|
env_script = serialize_env(literal_env, env, base_env, for_python=compression != 'gz')
|
||||||
buf = io.BytesIO()
|
buf = io.BytesIO()
|
||||||
with tarfile.open(mode=f'w:{compression}', fileobj=buf, encoding='utf-8') as tf:
|
with tarfile.open(mode=f'w:{compression}', fileobj=buf, encoding='utf-8') as tf:
|
||||||
rd = ssh_opts.remote_dir.rstrip('/')
|
rd = ssh_opts.remote_dir.rstrip('/')
|
||||||
@@ -196,18 +205,11 @@ def get_ssh_data(msg: str, request_id: str) -> Iterator[bytes]:
|
|||||||
yield b'invalid ssh data request message\n'
|
yield b'invalid ssh data request message\n'
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
with SharedMemory(pwfilename, readonly=True) as shm:
|
env_data = read_data_from_shared_memory(pwfilename)
|
||||||
shm.unlink()
|
if pw != env_data['pw']:
|
||||||
if shm.stats.st_uid != os.geteuid() or shm.stats.st_gid != os.getegid():
|
raise ValueError('Incorrect password')
|
||||||
raise ValueError('Incorrect owner on pwfile')
|
if rq_id != request_id:
|
||||||
mode = stat.S_IMODE(shm.stats.st_mode)
|
raise ValueError('Incorrect request id')
|
||||||
if mode != stat.S_IREAD:
|
|
||||||
raise ValueError('Incorrect permissions on pwfile')
|
|
||||||
env_data = json.loads(shm.read_data_with_size())
|
|
||||||
if pw != env_data['pw']:
|
|
||||||
raise ValueError('Incorrect password')
|
|
||||||
if rq_id != request_id:
|
|
||||||
raise ValueError('Incorrect request id')
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
yield f'{e}\n'.encode('utf-8')
|
yield f'{e}\n'.encode('utf-8')
|
||||||
@@ -267,8 +269,8 @@ def prepare_export_home_cmd(ssh_opts: SSHOptions, is_python: bool) -> str:
|
|||||||
def bootstrap_script(
|
def bootstrap_script(
|
||||||
ssh_opts: SSHOptions, script_type: str = 'sh', remote_args: Sequence[str] = (),
|
ssh_opts: SSHOptions, script_type: str = 'sh', remote_args: Sequence[str] = (),
|
||||||
test_script: str = '', request_id: Optional[str] = None, cli_hostname: str = '', cli_uname: str = '',
|
test_script: str = '', request_id: Optional[str] = None, cli_hostname: str = '', cli_uname: str = '',
|
||||||
request_data: bool = False, echo_on: bool = True
|
request_data: bool = False, echo_on: bool = True, literal_env: Dict[str, str] = {}
|
||||||
) -> Tuple[str, Dict[str, str], SharedMemory]:
|
) -> Tuple[str, Dict[str, str], str]:
|
||||||
if request_id is None:
|
if request_id is None:
|
||||||
request_id = os.environ['KITTY_PID'] + '-' + os.environ['KITTY_WINDOW_ID']
|
request_id = os.environ['KITTY_PID'] + '-' + os.environ['KITTY_WINDOW_ID']
|
||||||
is_python = script_type == 'py'
|
is_python = script_type == 'py'
|
||||||
@@ -277,14 +279,10 @@ def bootstrap_script(
|
|||||||
with open(os.path.join(shell_integration_dir, 'ssh', f'bootstrap.{script_type}')) as f:
|
with open(os.path.join(shell_integration_dir, 'ssh', f'bootstrap.{script_type}')) as f:
|
||||||
ans = f.read()
|
ans = f.read()
|
||||||
pw = secrets.token_hex()
|
pw = secrets.token_hex()
|
||||||
tfd = standard_b64encode(make_tarfile(ssh_opts, dict(os.environ), 'gz' if script_type == 'sh' else 'bz2')).decode('ascii')
|
tfd = standard_b64encode(make_tarfile(ssh_opts, dict(os.environ), 'gz' if script_type == 'sh' else 'bz2', literal_env=literal_env)).decode('ascii')
|
||||||
data = {'pw': pw, 'opts': ssh_opts._asdict(), 'hostname': cli_hostname, 'uname': cli_uname, 'tarfile': tfd}
|
data = {'pw': pw, 'opts': ssh_opts._asdict(), 'hostname': cli_hostname, 'uname': cli_uname, 'tarfile': tfd}
|
||||||
db = json.dumps(data)
|
shm_name = create_shared_memory(data, prefix=f'kssh-{os.getpid()}-')
|
||||||
with SharedMemory(size=len(db) + SharedMemory.num_bytes_for_size, mode=stat.S_IREAD, prefix=f'kssh-{os.getpid()}-') as shm:
|
sensitive_data = {'REQUEST_ID': request_id, 'DATA_PASSWORD': pw, 'PASSWORD_FILENAME': shm_name}
|
||||||
shm.write_data_with_size(db)
|
|
||||||
shm.flush()
|
|
||||||
atexit.register(shm.unlink)
|
|
||||||
sensitive_data = {'REQUEST_ID': request_id, 'DATA_PASSWORD': pw, 'PASSWORD_FILENAME': shm.name}
|
|
||||||
replacements = {
|
replacements = {
|
||||||
'EXPORT_HOME_CMD': export_home_cmd,
|
'EXPORT_HOME_CMD': export_home_cmd,
|
||||||
'EXEC_CMD': exec_cmd, 'TEST_SCRIPT': test_script,
|
'EXEC_CMD': exec_cmd, 'TEST_SCRIPT': test_script,
|
||||||
@@ -294,7 +292,7 @@ def bootstrap_script(
|
|||||||
if request_data:
|
if request_data:
|
||||||
sd.update(sensitive_data)
|
sd.update(sensitive_data)
|
||||||
replacements.update(sensitive_data)
|
replacements.update(sensitive_data)
|
||||||
return prepare_script(ans, sd, script_type), replacements, shm
|
return prepare_script(ans, sd, script_type), replacements, shm_name
|
||||||
|
|
||||||
|
|
||||||
def get_ssh_cli() -> Tuple[Set[str], Set[str]]:
|
def get_ssh_cli() -> Tuple[Set[str], Set[str]]:
|
||||||
@@ -499,15 +497,15 @@ def wrap_bootstrap_script(sh_script: str, interpreter: str) -> List[str]:
|
|||||||
|
|
||||||
def get_remote_command(
|
def get_remote_command(
|
||||||
remote_args: List[str], ssh_opts: SSHOptions, cli_hostname: str = '', cli_uname: str = '',
|
remote_args: List[str], ssh_opts: SSHOptions, cli_hostname: str = '', cli_uname: str = '',
|
||||||
echo_on: bool = True, request_data: bool = False
|
echo_on: bool = True, request_data: bool = False, literal_env: Dict[str, str] = {}
|
||||||
) -> Tuple[List[str], Dict[str, str], str]:
|
) -> Tuple[List[str], Dict[str, str], str]:
|
||||||
interpreter = ssh_opts.interpreter
|
interpreter = ssh_opts.interpreter
|
||||||
q = os.path.basename(interpreter).lower()
|
q = os.path.basename(interpreter).lower()
|
||||||
is_python = 'python' in q
|
is_python = 'python' in q
|
||||||
sh_script, replacements, shm = bootstrap_script(
|
sh_script, replacements, shm_name = bootstrap_script(
|
||||||
ssh_opts, script_type='py' if is_python else 'sh', remote_args=remote_args,
|
ssh_opts, script_type='py' if is_python else 'sh', remote_args=remote_args, literal_env=literal_env,
|
||||||
cli_hostname=cli_hostname, cli_uname=cli_uname, echo_on=echo_on, request_data=request_data)
|
cli_hostname=cli_hostname, cli_uname=cli_uname, echo_on=echo_on, request_data=request_data)
|
||||||
return wrap_bootstrap_script(sh_script, interpreter), replacements, shm.name
|
return wrap_bootstrap_script(sh_script, interpreter), replacements, shm_name
|
||||||
|
|
||||||
|
|
||||||
def connection_sharing_args(opts: SSHOptions, kitty_pid: int) -> List[str]:
|
def connection_sharing_args(opts: SSHOptions, kitty_pid: int) -> List[str]:
|
||||||
@@ -602,7 +600,9 @@ def drain_potential_tty_garbage(p: 'subprocess.Popen[bytes]', data_request: str)
|
|||||||
def change_colors(color_scheme: str) -> bool:
|
def change_colors(color_scheme: str) -> bool:
|
||||||
if not color_scheme:
|
if not color_scheme:
|
||||||
return False
|
return False
|
||||||
from kittens.themes.collection import load_themes, NoCacheFound, text_as_opts
|
from kittens.themes.collection import (
|
||||||
|
NoCacheFound, load_themes, text_as_opts
|
||||||
|
)
|
||||||
from kittens.themes.main import colors_as_escape_codes
|
from kittens.themes.main import colors_as_escape_codes
|
||||||
if color_scheme.endswith('.conf'):
|
if color_scheme.endswith('.conf'):
|
||||||
conf_file = resolve_abs_or_config_path(color_scheme)
|
conf_file = resolve_abs_or_config_path(color_scheme)
|
||||||
@@ -627,6 +627,14 @@ def change_colors(color_scheme: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def add_cloned_env(shm_name: str) -> Dict[str, str]:
|
||||||
|
try:
|
||||||
|
return cast(Dict[str, str], read_data_from_shared_memory(shm_name))
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def run_ssh(ssh_args: List[str], server_args: List[str], found_extra_args: Tuple[str, ...]) -> NoReturn:
|
def run_ssh(ssh_args: List[str], server_args: List[str], found_extra_args: Tuple[str, ...]) -> NoReturn:
|
||||||
cmd = [ssh_exe()] + ssh_args
|
cmd = [ssh_exe()] + ssh_args
|
||||||
hostname, remote_args = server_args[0], server_args[1:]
|
hostname, remote_args = server_args[0], server_args[1:]
|
||||||
@@ -646,12 +654,16 @@ def run_ssh(ssh_args: List[str], server_args: List[str], found_extra_args: Tuple
|
|||||||
else:
|
else:
|
||||||
hostname_for_match = hostname
|
hostname_for_match = hostname
|
||||||
hostname_for_match = hostname_for_match.split('@', 1)[-1].split(':', 1)[0]
|
hostname_for_match = hostname_for_match.split('@', 1)[-1].split(':', 1)[0]
|
||||||
overrides = []
|
overrides: List[str] = []
|
||||||
|
literal_env: Dict[str, str] = {}
|
||||||
pat = re.compile(r'^([a-zA-Z0-9_]+)[ \t]*=')
|
pat = re.compile(r'^([a-zA-Z0-9_]+)[ \t]*=')
|
||||||
for i, a in enumerate(found_extra_args):
|
for i, a in enumerate(found_extra_args):
|
||||||
if i % 2 == 1:
|
if i % 2 == 1:
|
||||||
aq = pat.sub(r'\1 ', a.lstrip())
|
aq = pat.sub(r'\1 ', a.lstrip())
|
||||||
if aq.split(maxsplit=1)[0] != 'hostname':
|
key = aq.split(maxsplit=1)[0]
|
||||||
|
if key == 'clone_env':
|
||||||
|
literal_env = add_cloned_env(aq.split(maxsplit=1)[1])
|
||||||
|
elif key != 'hostname':
|
||||||
overrides.append(aq)
|
overrides.append(aq)
|
||||||
if overrides:
|
if overrides:
|
||||||
overrides.insert(0, f'hostname {uname}@{hostname_for_match}')
|
overrides.insert(0, f'hostname {uname}@{hostname_for_match}')
|
||||||
@@ -677,7 +689,7 @@ def run_ssh(ssh_args: List[str], server_args: List[str], found_extra_args: Tuple
|
|||||||
need_to_request_data = False
|
need_to_request_data = False
|
||||||
with restore_terminal_state() as echo_on:
|
with restore_terminal_state() as echo_on:
|
||||||
rcmd, replacements, shm_name = get_remote_command(
|
rcmd, replacements, shm_name = get_remote_command(
|
||||||
remote_args, host_opts, hostname_for_match, uname, echo_on, request_data=need_to_request_data)
|
remote_args, host_opts, hostname_for_match, uname, echo_on, request_data=need_to_request_data, literal_env=literal_env)
|
||||||
cmd += rcmd
|
cmd += rcmd
|
||||||
colors_changed = change_colors(host_opts.color_scheme)
|
colors_changed = change_colors(host_opts.color_scheme)
|
||||||
try:
|
try:
|
||||||
@@ -716,6 +728,7 @@ def main(args: List[str]) -> NoReturn:
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv)
|
main(sys.argv)
|
||||||
elif __name__ == '__completer__':
|
elif __name__ == '__completer__':
|
||||||
|
from .completion import complete
|
||||||
setattr(sys, 'kitten_completer', complete)
|
setattr(sys, 'kitten_completer', complete)
|
||||||
elif __name__ == '__conf__':
|
elif __name__ == '__conf__':
|
||||||
from .options.definition import definition
|
from .options.definition import definition
|
||||||
|
|||||||
@@ -24,26 +24,26 @@ opt = definition.add_option
|
|||||||
agr('bootstrap', 'Host bootstrap configuration') # {{{
|
agr('bootstrap', 'Host bootstrap configuration') # {{{
|
||||||
|
|
||||||
opt('hostname', '*', option_type='hostname', long_text='''
|
opt('hostname', '*', option_type='hostname', long_text='''
|
||||||
The hostname that the following options apply to. A glob pattern to match multiple
|
The hostname that the following options apply to. A glob pattern to match
|
||||||
hosts can be used. Multiple hostnames can also be specified separated by spaces.
|
multiple hosts can be used. Multiple hostnames can also be specified, separated
|
||||||
The hostname can include an optional username in the form :code:`user@host`.
|
by spaces. The hostname can include an optional username in the form
|
||||||
When not specified options apply to all hosts, until the
|
:code:`user@host`. When not specified options apply to all hosts, until the
|
||||||
first hostname specification is found. Note that matching of hostname is done against
|
first hostname specification is found. Note that matching of hostname is done
|
||||||
the name you specify on the command line to connect to the remote host.
|
against the name you specify on the command line to connect to the remote host.
|
||||||
If you wish to include the same basic configuration for many
|
If you wish to include the same basic configuration for many different hosts,
|
||||||
different hosts, you can do so with the :ref:`include <include>` directive.
|
you can do so with the :ref:`include <include>` directive.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('interpreter', 'sh', long_text='''
|
opt('interpreter', 'sh', long_text='''
|
||||||
The interpreter to use on the remote host. Must be either a POSIX complaint shell
|
The interpreter to use on the remote host. Must be either a POSIX complaint
|
||||||
or a python executable. If the default sh is not available or broken, using
|
shell or a :program:`python` executable. If the default :program:`sh` is not
|
||||||
an alternate interpreter can be useful.
|
available or broken, using an alternate interpreter can be useful.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('remote_dir', '.local/share/kitty-ssh-kitten', option_type='relative_dir', long_text='''
|
opt('remote_dir', '.local/share/kitty-ssh-kitten', option_type='relative_dir', long_text='''
|
||||||
The location on the remote host where the files needed for this kitten
|
The location on the remote host where the files needed for this kitten are
|
||||||
are installed. The location is relative to the HOME directory. Absolute paths or paths
|
installed. The location is relative to the HOME directory. Absolute paths or
|
||||||
that resolve to a location outside the HOME are not allowed.
|
paths that resolve to a location outside the HOME are not allowed.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('+copy', '', option_type='copy', add_to_default=False, long_text=f'''
|
opt('+copy', '', option_type='copy', add_to_default=False, long_text=f'''
|
||||||
@@ -51,8 +51,7 @@ opt('+copy', '', option_type='copy', add_to_default=False, long_text=f'''
|
|||||||
|
|
||||||
copy .vimrc .zshrc .config/some-dir
|
copy .vimrc .zshrc .config/some-dir
|
||||||
|
|
||||||
If a file should be copied to some other destination on the remote host,
|
Use :code:`--dest` to copy a file to some other destination on the remote host::
|
||||||
use :code:`--dest`::
|
|
||||||
|
|
||||||
copy --dest some-other-name some-file
|
copy --dest some-other-name some-file
|
||||||
|
|
||||||
@@ -79,51 +78,54 @@ integration on a per-host basis.
|
|||||||
''')
|
''')
|
||||||
|
|
||||||
opt('login_shell', '', long_text='''
|
opt('login_shell', '', long_text='''
|
||||||
The login shell to execute on the remote host. By default, the remote user account's
|
The login shell to execute on the remote host. By default, the remote user
|
||||||
login shell is used.
|
account's login shell is used.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('+env', '', option_type='env', add_to_default=False, long_text='''
|
opt('+env', '', option_type='env', add_to_default=False, long_text='''
|
||||||
Specify environment variables to set on the remote host. Note that
|
Specify the environment variables to be set on the remote host. Using the
|
||||||
environment variables can refer to each other, so if you use::
|
name with an equal sign (e.g. :code:`env VAR=`) will set it to the empty string.
|
||||||
|
Specifying only the name (e.g. :code:`env VAR`) will remove the variable from
|
||||||
env MYVAR1=a
|
the remote shell environment. The special value :code:`_kitty_copy_env_var_`
|
||||||
env MYVAR2=$MYVAR1/$HOME/b
|
|
||||||
|
|
||||||
The value of MYVAR2 will be :code:`a/<path to home directory>/b`. Using
|
|
||||||
:code:`VAR=` will set it to the empty string and using just :code:`VAR`
|
|
||||||
will delete the variable from the child process' environment. The definitions
|
|
||||||
are processed alphabetically. The special value :code:`_kitty_copy_env_var_`
|
|
||||||
will cause the value of the variable to be copied from the local environment.
|
will cause the value of the variable to be copied from the local environment.
|
||||||
|
The definitions are processed alphabetically. Note that environment variables
|
||||||
|
are expanded recursively, for example::
|
||||||
|
|
||||||
|
env VAR1=a
|
||||||
|
env VAR2=${HOME}/${VAR1}/b
|
||||||
|
|
||||||
|
The value of :code:`VAR2` will be :code:`<path to home directory>/a/b`.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('cwd', '', long_text='''
|
opt('cwd', '', long_text='''
|
||||||
The working directory on the remote host to change to. Env vars in this
|
The working directory on the remote host to change to. Environment variables in
|
||||||
value are expanded. The default is empty so no changing is done, which
|
this value are expanded. The default is empty so no changing is done, which
|
||||||
usually means the home directory is used.
|
usually means the HOME directory is used.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('color_scheme', '', long_text='''
|
opt('color_scheme', '', long_text='''
|
||||||
Specify a color scheme to use when connecting to the remote host. If the
|
Specify a color scheme to use when connecting to the remote host. If this option
|
||||||
color_scheme ends with :code:`.conf` it is assumed to be the name of a config
|
ends with :code:`.conf`, it is assumed to be the name of a config file to load
|
||||||
file to load from the kitty config directory, otherwise it is assumed to be the
|
from the kitty config directory, otherwise it is assumed to be the name of a
|
||||||
name of a color theme to load via the themes kitten. Note that only colors
|
color theme to load via the :doc:`themes kitten </kittens/themes>`. Note that
|
||||||
applying to the text/background are changed, other config settings in the .conf
|
only colors applying to the text/background are changed, other config settings
|
||||||
files/themes are ignored.
|
in the .conf files/themes are ignored.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('remote_kitty', 'if-needed', choices=('if-needed', 'no', 'yes'), long_text='''
|
opt('remote_kitty', 'if-needed', choices=('if-needed', 'no', 'yes'), long_text='''
|
||||||
Make kitty available on the remote server. Useful to run kittens such as the
|
Make :program:`kitty` available on the remote host. Useful to run kittens such
|
||||||
icat kitten to display images or the transfer file kitten to transfer files.
|
as the :doc:`icat kitten </kittens/icat>` to display images or the
|
||||||
Only works if the remote server has an architecture for which pre-compiled
|
:doc:`transfer file kitten </kittens/transfer>` to transfer files. Only works if
|
||||||
kitty binaries are available. Note that kitty is not actually copied to the
|
the remote host has an architecture for which :link:`pre-compiled kitty binaries
|
||||||
remote server, instead a small bootstrap script is copied which will download
|
<https://github.com/kovidgoyal/kitty/releases>` are available. Note that kitty
|
||||||
and run kitty when kitty is first executed on the remote server. A value of
|
is not actually copied to the remote host, instead a small bootstrap script is
|
||||||
:code:`if-needed` means kitty is installed only if not already present in the
|
copied which will download and run kitty when kitty is first executed on the
|
||||||
system-wide PATH. A value of :code:`yes` means that kitty is installed even if
|
remote host. A value of :code:`if-needed` means kitty is installed only if not
|
||||||
already present, and the installed kitty takes precedence. Finally, :code:`no`
|
already present in the system-wide PATH. A value of :code:`yes` means that kitty
|
||||||
means no kitty is installed on the remote machine. The installed kitty
|
is installed even if already present, and the installed kitty takes precedence.
|
||||||
can be updated by running: :code:`kitty +update-kitty` on the remote machine.
|
Finally, :code:`no` means no kitty is installed on the remote host. The
|
||||||
|
installed kitty can be updated by running: :code:`kitty +update-kitty` on the
|
||||||
|
remote host.
|
||||||
''')
|
''')
|
||||||
egr() # }}}
|
egr() # }}}
|
||||||
|
|
||||||
@@ -131,20 +133,23 @@ agr('ssh', 'SSH configuration') # {{{
|
|||||||
|
|
||||||
opt('share_connections', 'yes', option_type='to_bool', long_text='''
|
opt('share_connections', 'yes', option_type='to_bool', long_text='''
|
||||||
Within a single kitty instance, all connections to a particular server can be
|
Within a single kitty instance, all connections to a particular server can be
|
||||||
shared. This reduces startup latency for subsequent connections and means that you have
|
shared. This reduces startup latency for subsequent connections and means that
|
||||||
to enter the password only once. Under the hood, it uses SSH ControlMasters and
|
you have to enter the password only once. Under the hood, it uses SSH
|
||||||
these are automatically cleaned up by kitty when it quits.
|
ControlMasters and these are automatically cleaned up by kitty when it quits.
|
||||||
|
You can map a shortcut to :ac:`close_shared_ssh_connections` to disconnect all
|
||||||
|
active shared connections.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('askpass', 'unless-set', choices=('unless-set', 'ssh', 'native'), long_text='''
|
opt('askpass', 'unless-set', choices=('unless-set', 'ssh', 'native'), long_text='''
|
||||||
Control the program SSH uses to ask for passwords or confirmation of host keys
|
Control the program SSH uses to ask for passwords or confirmation of host keys
|
||||||
etc. The default is to use kitty's native askpass, unless the SSH_ASKPASS
|
etc. The default is to use kitty's native :program:`askpass`, unless the
|
||||||
environment variable is set. Set it to :code:`ssh` to not interfere with the
|
:envvar:`SSH_ASKPASS` environment variable is set. Set this option to
|
||||||
normal ssh askpass mechanism at all, which typically means that ssh will prompt
|
:code:`ssh` to not interfere with the normal ssh askpass mechanism at all, which
|
||||||
at the terminal. Set it to :code:`native` to always use kitty's native,
|
typically means that ssh will prompt at the terminal. Set it to :code:`native`
|
||||||
built-in askpass implementation. Note that not using the kitty askpass implementation
|
to always use kitty's native, built-in askpass implementation. Note that not
|
||||||
means that SSH might need to use the terminal before the connection is established
|
using the kitty askpass implementation means that SSH might need to use the
|
||||||
so the kitten cannot use the terminal to send data without an extra roundtrip,
|
terminal before the connection is established, so the kitten cannot use the
|
||||||
adding to initial connection latency.
|
terminal to send data without an extra roundtrip, adding to initial connection
|
||||||
|
latency.
|
||||||
''')
|
''')
|
||||||
egr() # }}}
|
egr() # }}}
|
||||||
|
|||||||
92
kittens/ssh/utils.py
Normal file
92
kittens/ssh/utils.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
from kitty.types import run_once
|
||||||
|
|
||||||
|
|
||||||
|
@run_once
|
||||||
|
def ssh_options() -> Dict[str, str]:
|
||||||
|
try:
|
||||||
|
p = subprocess.run(['ssh'], stderr=subprocess.PIPE, encoding='utf-8')
|
||||||
|
raw = p.stderr or ''
|
||||||
|
except FileNotFoundError:
|
||||||
|
return {
|
||||||
|
'4': '', '6': '', 'A': '', 'a': '', 'C': '', 'f': '', 'G': '', 'g': '', 'K': '', 'k': '',
|
||||||
|
'M': '', 'N': '', 'n': '', 'q': '', 's': '', 'T': '', 't': '', 'V': '', 'v': '', 'X': '',
|
||||||
|
'x': '', 'Y': '', 'y': '', 'B': 'bind_interface', 'b': 'bind_address', 'c': 'cipher_spec',
|
||||||
|
'D': '[bind_address:]port', 'E': 'log_file', 'e': 'escape_char', 'F': 'configfile', 'I': 'pkcs11',
|
||||||
|
'i': 'identity_file', 'J': '[user@]host[:port]', 'L': 'address', 'l': 'login_name', 'm': 'mac_spec',
|
||||||
|
'O': 'ctl_cmd', 'o': 'option', 'p': 'port', 'Q': 'query_option', 'R': 'address',
|
||||||
|
'S': 'ctl_path', 'W': 'host:port', 'w': 'local_tun[:remote_tun]'
|
||||||
|
}
|
||||||
|
|
||||||
|
ans: Dict[str, str] = {}
|
||||||
|
pos = 0
|
||||||
|
while True:
|
||||||
|
pos = raw.find('[', pos)
|
||||||
|
if pos < 0:
|
||||||
|
break
|
||||||
|
num = 1
|
||||||
|
epos = pos
|
||||||
|
while num > 0:
|
||||||
|
epos += 1
|
||||||
|
if raw[epos] not in '[]':
|
||||||
|
continue
|
||||||
|
num += 1 if raw[epos] == '[' else -1
|
||||||
|
q = raw[pos+1:epos]
|
||||||
|
pos = epos
|
||||||
|
if len(q) < 2 or q[0] != '-':
|
||||||
|
continue
|
||||||
|
if ' ' in q:
|
||||||
|
opt, desc = q.split(' ', 1)
|
||||||
|
ans[opt[1:]] = desc
|
||||||
|
else:
|
||||||
|
ans.update(dict.fromkeys(q[1:], ''))
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def is_kitten_cmdline(q: List[str]) -> bool:
|
||||||
|
if len(q) < 4:
|
||||||
|
return False
|
||||||
|
if os.path.basename(q[0]).lower() != 'kitty':
|
||||||
|
return False
|
||||||
|
return q[1:3] == ['+kitten', 'ssh'] or q[1:4] == ['+', 'kitten', 'ssh']
|
||||||
|
|
||||||
|
|
||||||
|
def patch_cmdline(key: str, val: str, argv: List[str]) -> None:
|
||||||
|
for i, arg in enumerate(tuple(argv)):
|
||||||
|
if arg.startswith(f'--kitten={key}='):
|
||||||
|
argv[i] = f'--kitten={key}={val}'
|
||||||
|
return
|
||||||
|
elif i > 0 and argv[i-1] == '--kitten' and (arg.startswith(f'{key}=') or arg.startswith(f'{key} ')):
|
||||||
|
argv[i] = val
|
||||||
|
return
|
||||||
|
idx = argv.index('ssh')
|
||||||
|
argv.insert(idx + 1, f'--kitten={key}={val}')
|
||||||
|
|
||||||
|
|
||||||
|
def set_cwd_in_cmdline(cwd: str, argv: List[str]) -> None:
|
||||||
|
patch_cmdline('cwd', cwd, argv)
|
||||||
|
|
||||||
|
|
||||||
|
def create_shared_memory(data: Any, prefix: str) -> str:
|
||||||
|
import atexit
|
||||||
|
import json
|
||||||
|
import stat
|
||||||
|
|
||||||
|
from kitty.shm import SharedMemory
|
||||||
|
db = json.dumps(data).encode('utf-8')
|
||||||
|
with SharedMemory(size=len(db) + SharedMemory.num_bytes_for_size, mode=stat.S_IREAD, prefix=prefix) as shm:
|
||||||
|
shm.write_data_with_size(db)
|
||||||
|
shm.flush()
|
||||||
|
atexit.register(shm.unlink)
|
||||||
|
return shm.name
|
||||||
|
|
||||||
|
|
||||||
|
def set_env_in_cmdline(env: Dict[str, str], argv: List[str]) -> None:
|
||||||
|
patch_cmdline('clone_env', create_shared_memory(env, 'ksse-'), argv)
|
||||||
@@ -494,6 +494,7 @@ class Theme:
|
|||||||
is_dark: bool = False
|
is_dark: bool = False
|
||||||
blurb: str = ''
|
blurb: str = ''
|
||||||
num_settings: int = 0
|
num_settings: int = 0
|
||||||
|
is_user_defined: bool = False
|
||||||
|
|
||||||
def apply_dict(self, d: Dict[str, Any]) -> None:
|
def apply_dict(self, d: Dict[str, Any]) -> None:
|
||||||
self.name = str(d['name'])
|
self.name = str(d['name'])
|
||||||
@@ -501,7 +502,7 @@ class Theme:
|
|||||||
a = d.get(x)
|
a = d.get(x)
|
||||||
if isinstance(a, str):
|
if isinstance(a, str):
|
||||||
setattr(self, x, a)
|
setattr(self, x, a)
|
||||||
for x in ('is_dark', 'num_settings'):
|
for x in ('is_dark', 'num_settings', 'is_user_defined'):
|
||||||
a = d.get(x)
|
a = d.get(x)
|
||||||
if isinstance(a, int):
|
if isinstance(a, int):
|
||||||
setattr(self, x, a)
|
setattr(self, x, a)
|
||||||
@@ -594,6 +595,7 @@ class Themes:
|
|||||||
d = parse_theme(name, raw)
|
d = parse_theme(name, raw)
|
||||||
except (Exception, SystemExit):
|
except (Exception, SystemExit):
|
||||||
continue
|
continue
|
||||||
|
d['is_user_defined'] = True
|
||||||
t = Theme(raw.__str__)
|
t = Theme(raw.__str__)
|
||||||
t.apply_dict(d)
|
t.apply_dict(d)
|
||||||
if t.name:
|
if t.name:
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ def create_recent_filter(names: Iterable[str]) -> Callable[[Theme], bool]:
|
|||||||
return recent_filter
|
return recent_filter
|
||||||
|
|
||||||
|
|
||||||
|
def user_filter(q: Theme) -> bool:
|
||||||
|
return q.is_user_defined
|
||||||
|
|
||||||
|
|
||||||
def mark_shortcut(text: str, acc: str) -> str:
|
def mark_shortcut(text: str, acc: str) -> str:
|
||||||
acc_idx = text.lower().index(acc.lower())
|
acc_idx = text.lower().index(acc.lower())
|
||||||
return text[:acc_idx] + styled(text[acc_idx], underline='straight', bold=True, fg_intense=True) + text[acc_idx+1:]
|
return text[:acc_idx] + styled(text[acc_idx], underline='straight', bold=True, fg_intense=True) + text[acc_idx+1:]
|
||||||
@@ -153,12 +157,13 @@ class ThemesHandler(Handler):
|
|||||||
self.report_traceback_on_exit: Optional[str] = None
|
self.report_traceback_on_exit: Optional[str] = None
|
||||||
self.filter_map: Dict[str, Callable[[Theme], bool]] = {
|
self.filter_map: Dict[str, Callable[[Theme], bool]] = {
|
||||||
'dark': dark_filter, 'light': light_filter, 'all': all_filter,
|
'dark': dark_filter, 'light': light_filter, 'all': all_filter,
|
||||||
'recent': create_recent_filter(self.cached_values.get('recent', ()))
|
'recent': create_recent_filter(self.cached_values.get('recent', ())),
|
||||||
|
'user': user_filter
|
||||||
}
|
}
|
||||||
self.themes_list = ThemesList()
|
self.themes_list = ThemesList()
|
||||||
self.colors_set_once = False
|
self.colors_set_once = False
|
||||||
self.line_edit = LineEdit()
|
self.line_edit = LineEdit()
|
||||||
self.tabs = tuple('all dark light recent'.split())
|
self.tabs = tuple('all dark light recent user'.split())
|
||||||
self.quit_on_next_key_release = -1
|
self.quit_on_next_key_release = -1
|
||||||
|
|
||||||
def update_recent(self) -> None:
|
def update_recent(self) -> None:
|
||||||
@@ -389,7 +394,7 @@ class ThemesHandler(Handler):
|
|||||||
if key_event.matches('esc') or key_event.matches_text('q'):
|
if key_event.matches('esc') or key_event.matches_text('q'):
|
||||||
self.quit_on_next_key_release = 0
|
self.quit_on_next_key_release = 0
|
||||||
return
|
return
|
||||||
for cat in 'all dark light recent'.split():
|
for cat in self.tabs:
|
||||||
if key_event.matches_text(cat[0]) or key_event.matches(f'alt+{cat[0]}'):
|
if key_event.matches_text(cat[0]) or key_event.matches(f'alt+{cat[0]}'):
|
||||||
if cat != self.current_category:
|
if cat != self.current_category:
|
||||||
self.current_category = cat
|
self.current_category = cat
|
||||||
|
|||||||
@@ -35,26 +35,28 @@ destination path on the receiving computer.
|
|||||||
|
|
||||||
|
|
||||||
--permissions-bypass -p
|
--permissions-bypass -p
|
||||||
The password to use to skip the transfer confirmation popup in kitty. Must match the
|
The password to use to skip the transfer confirmation popup in kitty. Must match
|
||||||
password set for the :opt:`file_transfer_confirmation_bypass` option in kitty.conf. Note that
|
the password set for the :opt:`file_transfer_confirmation_bypass` option in
|
||||||
leading and trailing whitespace is removed from the password. A password starting with
|
:file:`kitty.conf`. Note that leading and trailing whitespace is removed from
|
||||||
., / or ~ characters is assumed to be a file name to read the password from. A value
|
the password. A password starting with :code:`.`, :code:`/` or :code:`~`
|
||||||
of - means read the password from STDIN. A password that is purely a number less than 256
|
characters is assumed to be a file name to read the password from. A value of
|
||||||
is assumed to be the number of a file descriptor from which to read the actual password.
|
:code:`-` means read the password from STDIN. A password that is purely a number
|
||||||
|
less than 256 is assumed to be the number of a file descriptor from which to
|
||||||
|
read the actual password.
|
||||||
|
|
||||||
|
|
||||||
--confirm-paths -c
|
--confirm-paths -c
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Before actually transferring files, show a mapping of local file names to remote file names
|
Before actually transferring files, show a mapping of local file names to remote
|
||||||
and ask for confirmation.
|
file names and ask for confirmation.
|
||||||
|
|
||||||
|
|
||||||
--transmit-deltas -x
|
--transmit-deltas -x
|
||||||
type=bool-set
|
type=bool-set
|
||||||
If a file on the receiving side already exists, use the rsync algorithm to update it to match
|
If a file on the receiving side already exists, use the rsync algorithm to
|
||||||
the file on the sending side, potentially saving lots of bandwidth and also automatically resuming
|
update it to match the file on the sending side, potentially saving lots of
|
||||||
partial transfers. Note that this will actually degrade performance on fast links with small
|
bandwidth and also automatically resuming partial transfers. Note that this will
|
||||||
files, so use with care.
|
actually degrade performance on fast links with small files, so use with care.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,15 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import Tuple
|
from typing import TYPE_CHECKING, Tuple
|
||||||
|
|
||||||
|
from kitty.types import run_once
|
||||||
|
|
||||||
from .operations import raw_mode, set_cursor_visible
|
from .operations import raw_mode, set_cursor_visible
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from kitty.options.types import Options
|
||||||
|
|
||||||
|
|
||||||
def get_key_press(allowed: str, default: str) -> str:
|
def get_key_press(allowed: str, default: str) -> str:
|
||||||
response = default
|
response = default
|
||||||
@@ -50,6 +55,14 @@ def human_size(
|
|||||||
return format_number(size / 1024**exponent, max_num_of_decimals) + sep + unit_list[exponent]
|
return format_number(size / 1024**exponent, max_num_of_decimals) + sep + unit_list[exponent]
|
||||||
|
|
||||||
|
|
||||||
|
@run_once
|
||||||
|
def kitty_opts() -> 'Options':
|
||||||
|
from kitty.cli import create_default_opts
|
||||||
|
from kitty.utils import suppress_error_logging
|
||||||
|
with suppress_error_logging():
|
||||||
|
return create_default_opts()
|
||||||
|
|
||||||
|
|
||||||
def report_error(msg: str = '', return_code: int = 1, print_exc: bool = False) -> None:
|
def report_error(msg: str = '', return_code: int = 1, print_exc: bool = False) -> None:
|
||||||
' Report an error also sending the overlay ready message to ensure kitten is visible '
|
' Report an error also sending the overlay ready message to ensure kitten is visible '
|
||||||
from .operations import overlay_ready
|
from .operations import overlay_ready
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ class UnicodeInput(Handler):
|
|||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
|
||||||
help_text = 'Input a unicode character'
|
help_text = 'Input a Unicode character'
|
||||||
usage = ''
|
usage = ''
|
||||||
OPTIONS = '''
|
OPTIONS = '''
|
||||||
--emoji-variation
|
--emoji-variation
|
||||||
@@ -554,7 +554,7 @@ type=choices
|
|||||||
default=none
|
default=none
|
||||||
choices=none,graphic,text
|
choices=none,graphic,text
|
||||||
Whether to use the textual or the graphical form for emoji. By default the
|
Whether to use the textual or the graphical form for emoji. By default the
|
||||||
default form specified in the unicode standard for the symbol is used.
|
default form specified in the Unicode standard for the symbol is used.
|
||||||
|
|
||||||
|
|
||||||
'''.format
|
'''.format
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# empty
|
|
||||||
|
|||||||
33
kitty/backtrace.h
Normal file
33
kitty/backtrace.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if __has_include(<execinfo.h>)
|
||||||
|
#include <execinfo.h>
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
print_stack_trace(void) {
|
||||||
|
void *array[256];
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
// get void*'s for all entries on the stack
|
||||||
|
size = backtrace(array, 256);
|
||||||
|
|
||||||
|
// print out all the frames to stderr
|
||||||
|
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline void
|
||||||
|
print_stack_trace(void) {
|
||||||
|
fprintf(stderr, "Stack trace functionality not available.\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
176
kitty/boss.py
176
kitty/boss.py
@@ -11,8 +11,8 @@ from functools import partial
|
|||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Container, Dict, Iterable, Iterator, List, Optional,
|
Any, Callable, Container, Dict, Iterable, Iterator, List, Optional, Set,
|
||||||
Sequence, Tuple, Union
|
Tuple, Union
|
||||||
)
|
)
|
||||||
from weakref import WeakValueDictionary
|
from weakref import WeakValueDictionary
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ from .utils import (
|
|||||||
platform_window_id, remove_socket_file, safe_print, set_primary_selection,
|
platform_window_id, remove_socket_file, safe_print, set_primary_selection,
|
||||||
single_instance, startup_notification_handler, which
|
single_instance, startup_notification_handler, which
|
||||||
)
|
)
|
||||||
from .window import CommandOutput, CwdRequest, MatchPatternType, Window
|
from .window import CommandOutput, CwdRequest, Window
|
||||||
|
|
||||||
|
|
||||||
class OSWindowDict(TypedDict):
|
class OSWindowDict(TypedDict):
|
||||||
@@ -230,6 +230,7 @@ class Boss:
|
|||||||
global_shortcuts: Dict[str, SingleKey]
|
global_shortcuts: Dict[str, SingleKey]
|
||||||
):
|
):
|
||||||
set_layout_options(opts)
|
set_layout_options(opts)
|
||||||
|
self.update_check_started = False
|
||||||
self.clipboard_buffers: Dict[str, str] = {}
|
self.clipboard_buffers: Dict[str, str] = {}
|
||||||
self.update_check_process: Optional['PopenType[bytes]'] = None
|
self.update_check_process: Optional['PopenType[bytes]'] = None
|
||||||
self.window_id_map: WeakValueDictionary[int, Window] = WeakValueDictionary()
|
self.window_id_map: WeakValueDictionary[int, Window] = WeakValueDictionary()
|
||||||
@@ -273,7 +274,7 @@ class Boss:
|
|||||||
for sc in self.global_shortcuts.values():
|
for sc in self.global_shortcuts.values():
|
||||||
self.keymap.pop(sc, None)
|
self.keymap.pop(sc, None)
|
||||||
|
|
||||||
def startup_first_child(self, os_window_id: Optional[int], startup_sessions: Sequence[Optional[Session]] = ()) -> None:
|
def startup_first_child(self, os_window_id: Optional[int], startup_sessions: Iterable[Session] = ()) -> None:
|
||||||
si = startup_sessions or create_sessions(get_options(), self.args, default_session=get_options().startup_session)
|
si = startup_sessions or create_sessions(get_options(), self.args, default_session=get_options().startup_session)
|
||||||
for startup_session in si:
|
for startup_session in si:
|
||||||
self.add_os_window(startup_session, os_window_id=os_window_id)
|
self.add_os_window(startup_session, os_window_id=os_window_id)
|
||||||
@@ -340,42 +341,19 @@ class Boss:
|
|||||||
yield from tab
|
yield from tab
|
||||||
|
|
||||||
def match_windows(self, match: str) -> Iterator[Window]:
|
def match_windows(self, match: str) -> Iterator[Window]:
|
||||||
try:
|
if match == 'all':
|
||||||
field, exp = match.split(':', 1)
|
yield from self.all_windows
|
||||||
except ValueError:
|
|
||||||
return
|
return
|
||||||
if field == 'num':
|
from .search_query_parser import search
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
if tab is not None:
|
|
||||||
try:
|
def get_matches(location: str, query: str, candidates: Set[int]) -> Set[int]:
|
||||||
w = tab.get_nth_window(int(exp))
|
return {wid for wid in candidates if self.window_id_map[wid].matches_query(location, query, tab)}
|
||||||
except Exception:
|
|
||||||
return
|
for wid in search(match, (
|
||||||
if w is not None:
|
'id', 'title', 'pid', 'cwd', 'cmdline', 'num', 'env', 'recent', 'state'
|
||||||
yield w
|
), set(self.window_id_map), get_matches):
|
||||||
return
|
yield self.window_id_map[wid]
|
||||||
if field == 'recent':
|
|
||||||
tab = self.active_tab
|
|
||||||
if tab is not None:
|
|
||||||
try:
|
|
||||||
num = int(exp)
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
w = self.window_id_map.get(tab.nth_active_window_id(num))
|
|
||||||
if w is not None:
|
|
||||||
yield w
|
|
||||||
return
|
|
||||||
if field != 'env':
|
|
||||||
pat: MatchPatternType = re.compile(exp)
|
|
||||||
else:
|
|
||||||
kp, vp = exp.partition('=')[::2]
|
|
||||||
if vp:
|
|
||||||
pat = re.compile(kp), re.compile(vp)
|
|
||||||
else:
|
|
||||||
pat = re.compile(kp), None
|
|
||||||
for window in self.all_windows:
|
|
||||||
if window.matches(field, pat):
|
|
||||||
yield window
|
|
||||||
|
|
||||||
def tab_for_window(self, window: Window) -> Optional[Tab]:
|
def tab_for_window(self, window: Window) -> Optional[Tab]:
|
||||||
for tab in self.all_tabs:
|
for tab in self.all_tabs:
|
||||||
@@ -385,55 +363,36 @@ class Boss:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def match_tabs(self, match: str) -> Iterator[Tab]:
|
def match_tabs(self, match: str) -> Iterator[Tab]:
|
||||||
try:
|
if match == 'all':
|
||||||
field, exp = match.split(':', 1)
|
return self.all_tabs
|
||||||
except ValueError:
|
from .search_query_parser import search
|
||||||
return
|
tm = self.active_tab_manager
|
||||||
pat = re.compile(exp)
|
tim = {t.id: t for t in self.all_tabs}
|
||||||
|
|
||||||
|
def get_matches(location: str, query: str, candidates: Set[int]) -> Set[int]:
|
||||||
|
return {wid for wid in candidates if tim[wid].matches_query(location, query, tm)}
|
||||||
|
|
||||||
found = False
|
found = False
|
||||||
if field in ('title', 'id'):
|
for tid in search(match, (
|
||||||
for tab in self.all_tabs:
|
'id', 'index', 'title', 'window_id', 'window_title', 'pid', 'cwd', 'env', 'cmdline', 'recent', 'state'
|
||||||
if tab.matches(field, pat):
|
), set(tim), get_matches):
|
||||||
yield tab
|
found = True
|
||||||
found = True
|
yield tim[tid]
|
||||||
elif field in ('window_id', 'window_title'):
|
|
||||||
wf = field.split('_')[1]
|
|
||||||
tabs = {self.tab_for_window(w) for w in self.match_windows(f'{wf}:{exp}')}
|
|
||||||
for q in tabs:
|
|
||||||
if q:
|
|
||||||
found = True
|
|
||||||
yield q
|
|
||||||
elif field == 'index':
|
|
||||||
tm = self.active_tab_manager
|
|
||||||
if tm is not None and len(tm.tabs) > 0:
|
|
||||||
idx = (int(pat.pattern) + len(tm.tabs)) % len(tm.tabs)
|
|
||||||
found = True
|
|
||||||
yield tm.tabs[idx]
|
|
||||||
elif field == 'recent':
|
|
||||||
tm = self.active_tab_manager
|
|
||||||
if tm is not None and len(tm.tabs) > 0:
|
|
||||||
try:
|
|
||||||
num = int(exp)
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
q = tm.nth_active_tab(num)
|
|
||||||
if q is not None:
|
|
||||||
found = True
|
|
||||||
yield q
|
|
||||||
if not found:
|
if not found:
|
||||||
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
|
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
|
||||||
for q in tabs:
|
for q in tabs:
|
||||||
if q:
|
if q:
|
||||||
yield q
|
yield q
|
||||||
|
|
||||||
def set_active_window(self, window: Window, switch_os_window_if_needed: bool = False) -> Optional[int]:
|
def set_active_window(self, window: Window, switch_os_window_if_needed: bool = False, for_keep_focus: bool = False) -> Optional[int]:
|
||||||
for os_window_id, tm in self.os_window_map.items():
|
for os_window_id, tm in self.os_window_map.items():
|
||||||
for tab in tm:
|
for tab in tm:
|
||||||
for w in tab:
|
for w in tab:
|
||||||
if w.id == window.id:
|
if w.id == window.id:
|
||||||
if tab is not self.active_tab:
|
if tab is not self.active_tab:
|
||||||
tm.set_active_tab(tab)
|
tm.set_active_tab(tab, for_keep_focus=window.tabref() if for_keep_focus else None)
|
||||||
tab.set_active_window(w)
|
tab.set_active_window(w, for_keep_focus=window if for_keep_focus else None)
|
||||||
if switch_os_window_if_needed and current_os_window() != os_window_id:
|
if switch_os_window_if_needed and current_os_window() != os_window_id:
|
||||||
focus_os_window(os_window_id, True)
|
focus_os_window(os_window_id, True)
|
||||||
return os_window_id
|
return os_window_id
|
||||||
@@ -692,8 +651,9 @@ class Boss:
|
|||||||
) -> None:
|
) -> None:
|
||||||
def callback_(res: Dict[str, Any], x: int, boss: Boss) -> None:
|
def callback_(res: Dict[str, Any], x: int, boss: Boss) -> None:
|
||||||
callback(res.get('response') == 'y', *args)
|
callback(res.get('response') == 'y', *args)
|
||||||
self._run_kitten('ask', ['--type=yesno', '--message', msg, '--default', 'y' if confirm_on_accept else 'n'],
|
self.run_kitten_with_metadata(
|
||||||
window=window, custom_callback=callback_, default_data={'response': 'y' if confirm_on_cancel else 'n'})
|
'ask', ['--type=yesno', '--message', msg, '--default', 'y' if confirm_on_accept else 'n'],
|
||||||
|
window=window, custom_callback=callback_, default_data={'response': 'y' if confirm_on_cancel else 'n'})
|
||||||
|
|
||||||
def choose(
|
def choose(
|
||||||
self, msg: str, # can contain newlines and ANSI formatting
|
self, msg: str, # can contain newlines and ANSI formatting
|
||||||
@@ -709,7 +669,7 @@ class Boss:
|
|||||||
cmd += ['-d', default]
|
cmd += ['-d', default]
|
||||||
for c in choices:
|
for c in choices:
|
||||||
cmd += ['-c', c]
|
cmd += ['-c', c]
|
||||||
self._run_kitten('ask', cmd, window=window, custom_callback=callback_, default_data={'response': ''})
|
self.run_kitten_with_metadata('ask', cmd, window=window, custom_callback=callback_, default_data={'response': ''})
|
||||||
|
|
||||||
def get_line(
|
def get_line(
|
||||||
self, msg: str, # can contain newlines and ANSI formatting
|
self, msg: str, # can contain newlines and ANSI formatting
|
||||||
@@ -721,7 +681,7 @@ class Boss:
|
|||||||
def callback_(res: Dict[str, Any], x: int, boss: Boss) -> None:
|
def callback_(res: Dict[str, Any], x: int, boss: Boss) -> None:
|
||||||
callback(res.get('response') or '')
|
callback(res.get('response') or '')
|
||||||
cmd = ['--type', 'password' if is_password else 'line', '--message', msg, '--prompt', prompt]
|
cmd = ['--type', 'password' if is_password else 'line', '--message', msg, '--prompt', prompt]
|
||||||
self._run_kitten('ask', cmd, window=window, custom_callback=callback_, default_data={'response': ''})
|
self.run_kitten_with_metadata('ask', cmd, window=window, custom_callback=callback_, default_data={'response': ''})
|
||||||
|
|
||||||
def confirm_tab_close(self, tab: Tab) -> None:
|
def confirm_tab_close(self, tab: Tab) -> None:
|
||||||
x = get_options().confirm_os_window_close
|
x = get_options().confirm_os_window_close
|
||||||
@@ -769,20 +729,20 @@ class Boss:
|
|||||||
def toggle_macos_secure_keyboard_entry(self) -> None:
|
def toggle_macos_secure_keyboard_entry(self) -> None:
|
||||||
toggle_secure_input()
|
toggle_secure_input()
|
||||||
|
|
||||||
def start(self, first_os_window_id: int) -> None:
|
def start(self, first_os_window_id: int, startup_sessions: Iterable[Session]) -> None:
|
||||||
if not getattr(self, 'io_thread_started', False):
|
if not getattr(self, 'io_thread_started', False):
|
||||||
self.child_monitor.start()
|
self.child_monitor.start()
|
||||||
self.io_thread_started = True
|
self.io_thread_started = True
|
||||||
urls: List[str] = getattr(sys, 'cmdline_args_for_open', [])
|
urls: List[str] = getattr(sys, 'cmdline_args_for_open', [])
|
||||||
sess = create_sessions(get_options(), self.args, special_window=SpecialWindow([kitty_exe(), '+runpy', 'input()']))
|
|
||||||
if urls:
|
if urls:
|
||||||
delattr(sys, 'cmdline_args_for_open')
|
delattr(sys, 'cmdline_args_for_open')
|
||||||
|
sess = create_sessions(get_options(), self.args, special_window=SpecialWindow([kitty_exe(), '+runpy', 'input()']))
|
||||||
self.startup_first_child(first_os_window_id, startup_sessions=tuple(sess))
|
self.startup_first_child(first_os_window_id, startup_sessions=tuple(sess))
|
||||||
self.launch_urls(*urls)
|
self.launch_urls(*urls)
|
||||||
else:
|
else:
|
||||||
self.startup_first_child(first_os_window_id)
|
self.startup_first_child(first_os_window_id, startup_sessions=startup_sessions)
|
||||||
|
|
||||||
if get_options().update_check_interval > 0 and not hasattr(self, 'update_check_started') and getattr(sys, 'frozen', False):
|
if get_options().update_check_interval > 0 and not self.update_check_started and getattr(sys, 'frozen', False):
|
||||||
from .update_check import run_update_check
|
from .update_check import run_update_check
|
||||||
run_update_check(get_options().update_check_interval * 60 * 60)
|
run_update_check(get_options().update_check_interval * 60 * 60)
|
||||||
self.update_check_started = True
|
self.update_check_started = True
|
||||||
@@ -1114,7 +1074,7 @@ class Boss:
|
|||||||
w = self.active_window
|
w = self.active_window
|
||||||
if w is None:
|
if w is None:
|
||||||
return
|
return
|
||||||
overlay_window = self._run_kitten('resize_window', args=[
|
overlay_window = self.run_kitten_with_metadata('resize_window', args=[
|
||||||
f'--horizontal-increment={get_options().window_resize_step_cells}',
|
f'--horizontal-increment={get_options().window_resize_step_cells}',
|
||||||
f'--vertical-increment={get_options().window_resize_step_lines}'
|
f'--vertical-increment={get_options().window_resize_step_lines}'
|
||||||
])
|
])
|
||||||
@@ -1215,6 +1175,8 @@ class Boss:
|
|||||||
if len(actions) > 1:
|
if len(actions) > 1:
|
||||||
self.drain_actions(list(actions[1:]), window_for_dispatch, dispatch_type)
|
self.drain_actions(list(actions[1:]), window_for_dispatch, dispatch_type)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
self.show_error('Key action failed', f'{actions[0].pretty()}\n{e}')
|
self.show_error('Key action failed', f'{actions[0].pretty()}\n{e}')
|
||||||
consumed = True
|
consumed = True
|
||||||
return consumed
|
return consumed
|
||||||
@@ -1377,7 +1339,7 @@ class Boss:
|
|||||||
cmd = [kitty_exe(), '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + get_editor(get_options()) + [confpath]
|
cmd = [kitty_exe(), '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + get_editor(get_options()) + [confpath]
|
||||||
self.new_os_window(*cmd)
|
self.new_os_window(*cmd)
|
||||||
|
|
||||||
def _run_kitten(
|
def run_kitten_with_metadata(
|
||||||
self,
|
self,
|
||||||
kitten: str,
|
kitten: str,
|
||||||
args: Iterable[str] = (),
|
args: Iterable[str] = (),
|
||||||
@@ -1454,13 +1416,14 @@ class Boss:
|
|||||||
action_on_removal(wid, self)
|
action_on_removal(wid, self)
|
||||||
overlay_window.actions_on_removal.append(callback_wrapper)
|
overlay_window.actions_on_removal.append(callback_wrapper)
|
||||||
return overlay_window
|
return overlay_window
|
||||||
|
_run_kitten = run_kitten_with_metadata
|
||||||
|
|
||||||
@ac('misc', 'Run the specified kitten. See :doc:`/kittens/custom` for details')
|
@ac('misc', 'Run the specified kitten. See :doc:`/kittens/custom` for details')
|
||||||
def kitten(self, kitten: str, *kargs: str) -> None:
|
def kitten(self, kitten: str, *kargs: str) -> None:
|
||||||
self._run_kitten(kitten, kargs)
|
self.run_kitten_with_metadata(kitten, kargs)
|
||||||
|
|
||||||
def run_kitten(self, kitten: str, *args: str) -> None:
|
def run_kitten(self, kitten: str, *args: str) -> None:
|
||||||
self._run_kitten(kitten, args)
|
self.run_kitten_with_metadata(kitten, args)
|
||||||
|
|
||||||
def on_kitten_finish(
|
def on_kitten_finish(
|
||||||
self, target_window_id: int, end_kitten: Callable[[Dict[str, Any], int, 'Boss'], None],
|
self, target_window_id: int, end_kitten: Callable[[Dict[str, Any], int, 'Boss'], None],
|
||||||
@@ -1475,7 +1438,7 @@ class Boss:
|
|||||||
|
|
||||||
@ac('misc', 'Input an arbitrary unicode character. See :doc:`/kittens/unicode_input` for details.')
|
@ac('misc', 'Input an arbitrary unicode character. See :doc:`/kittens/unicode_input` for details.')
|
||||||
def input_unicode_character(self) -> None:
|
def input_unicode_character(self) -> None:
|
||||||
self._run_kitten('unicode_input')
|
self.run_kitten_with_metadata('unicode_input')
|
||||||
|
|
||||||
@ac('tab', 'Change the title of the active tab')
|
@ac('tab', 'Change the title of the active tab')
|
||||||
def set_tab_title(self) -> None:
|
def set_tab_title(self) -> None:
|
||||||
@@ -1484,7 +1447,7 @@ class Boss:
|
|||||||
args = [
|
args = [
|
||||||
'--name=tab-title', '--message', _('Enter the new title for this tab below.'),
|
'--name=tab-title', '--message', _('Enter the new title for this tab below.'),
|
||||||
'--default', tab.name or tab.title, 'do_set_tab_title', str(tab.id)]
|
'--default', tab.name or tab.title, 'do_set_tab_title', str(tab.id)]
|
||||||
self._run_kitten('ask', args)
|
self.run_kitten_with_metadata('ask', args)
|
||||||
|
|
||||||
def do_set_tab_title(self, title: str, tab_id: int) -> None:
|
def do_set_tab_title(self, title: str, tab_id: int) -> None:
|
||||||
tm = self.active_tab_manager
|
tm = self.active_tab_manager
|
||||||
@@ -1496,7 +1459,12 @@ class Boss:
|
|||||||
break
|
break
|
||||||
|
|
||||||
def show_error(self, title: str, msg: str) -> None:
|
def show_error(self, title: str, msg: str) -> None:
|
||||||
self._run_kitten('show_error', args=['--title', title], input_data=msg)
|
ec = sys.exc_info()
|
||||||
|
tb = ''
|
||||||
|
if ec != (None, None, None):
|
||||||
|
import traceback
|
||||||
|
tb = traceback.format_exc()
|
||||||
|
self.run_kitten_with_metadata('show_error', args=['--title', title], input_data=json.dumps({'msg': msg, 'tb': tb}))
|
||||||
|
|
||||||
@ac('mk', 'Create a new marker')
|
@ac('mk', 'Create a new marker')
|
||||||
def create_marker(self) -> None:
|
def create_marker(self) -> None:
|
||||||
@@ -1516,7 +1484,7 @@ class Boss:
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.show_error(_('Invalid marker specification'), str(err))
|
self.show_error(_('Invalid marker specification'), str(err))
|
||||||
|
|
||||||
self._run_kitten('ask', [
|
self.run_kitten_with_metadata('ask', [
|
||||||
'--name=create-marker', '--message',
|
'--name=create-marker', '--message',
|
||||||
_('Create marker, for example:\ntext 1 ERROR\nSee {}\n').format(website_url('marks'))
|
_('Create marker, for example:\ntext 1 ERROR\nSee {}\n').format(website_url('marks'))
|
||||||
],
|
],
|
||||||
@@ -1559,6 +1527,10 @@ class Boss:
|
|||||||
if tab:
|
if tab:
|
||||||
tab.set_active_window(window_id)
|
tab.set_active_window(window_id)
|
||||||
|
|
||||||
|
def open_kitty_website(self) -> None:
|
||||||
|
self.open_url(website_url())
|
||||||
|
|
||||||
|
@ac('misc', 'Open the specified URL')
|
||||||
def open_url(self, url: str, program: Optional[Union[str, List[str]]] = None, cwd: Optional[str] = None) -> None:
|
def open_url(self, url: str, program: Optional[Union[str, List[str]]] = None, cwd: Optional[str] = None) -> None:
|
||||||
if not url:
|
if not url:
|
||||||
return
|
return
|
||||||
@@ -1578,7 +1550,7 @@ class Boss:
|
|||||||
|
|
||||||
@ac('misc', 'Click a URL using the keyboard')
|
@ac('misc', 'Click a URL using the keyboard')
|
||||||
def open_url_with_hints(self) -> None:
|
def open_url_with_hints(self) -> None:
|
||||||
self._run_kitten('hints')
|
self.run_kitten_with_metadata('hints')
|
||||||
|
|
||||||
def drain_actions(self, actions: List[KeyAction], window_for_dispatch: Optional[Window] = None, dispatch_type: str = 'KeyPress') -> None:
|
def drain_actions(self, actions: List[KeyAction], window_for_dispatch: Optional[Window] = None, dispatch_type: str = 'KeyPress') -> None:
|
||||||
|
|
||||||
@@ -1616,7 +1588,7 @@ class Boss:
|
|||||||
def current_primary_selection_or_clipboard(self) -> str:
|
def current_primary_selection_or_clipboard(self) -> str:
|
||||||
return get_primary_selection() if supports_primary_selection else get_clipboard_string()
|
return get_primary_selection() if supports_primary_selection else get_clipboard_string()
|
||||||
|
|
||||||
@ac('cp', 'Paste from the clipboard to the active window')
|
@ac('cp', 'Paste from the primary selection, if present, otherwise the clipboard to the active window')
|
||||||
def paste_from_selection(self) -> None:
|
def paste_from_selection(self) -> None:
|
||||||
text = self.current_primary_selection_or_clipboard()
|
text = self.current_primary_selection_or_clipboard()
|
||||||
self.paste_to_active_window(text)
|
self.paste_to_active_window(text)
|
||||||
@@ -1994,8 +1966,10 @@ class Boss:
|
|||||||
''')
|
''')
|
||||||
def load_config_file(self, *paths: str, apply_overrides: bool = True) -> None:
|
def load_config_file(self, *paths: str, apply_overrides: bool = True) -> None:
|
||||||
from .config import load_config
|
from .config import load_config
|
||||||
|
from .cli import default_config_paths
|
||||||
old_opts = get_options()
|
old_opts = get_options()
|
||||||
paths = paths or old_opts.config_paths
|
prev_paths = old_opts.config_paths or default_config_paths(self.args.config)
|
||||||
|
paths = paths or prev_paths
|
||||||
bad_lines: List[BadLine] = []
|
bad_lines: List[BadLine] = []
|
||||||
opts = load_config(*paths, overrides=old_opts.config_overrides if apply_overrides else None, accumulate_bad_lines=bad_lines)
|
opts = load_config(*paths, overrides=old_opts.config_overrides if apply_overrides else None, accumulate_bad_lines=bad_lines)
|
||||||
if bad_lines:
|
if bad_lines:
|
||||||
@@ -2166,7 +2140,7 @@ class Boss:
|
|||||||
def done2(target_window_id: int, self: Boss) -> None:
|
def done2(target_window_id: int, self: Boss) -> None:
|
||||||
callback(ans)
|
callback(ans)
|
||||||
|
|
||||||
q = self._run_kitten(
|
q = self.run_kitten_with_metadata(
|
||||||
'hints', args=(
|
'hints', args=(
|
||||||
'--ascending', '--customize-processing=::import::kitty.choose_entry',
|
'--ascending', '--customize-processing=::import::kitty.choose_entry',
|
||||||
'--window-title', title,
|
'--window-title', title,
|
||||||
@@ -2276,7 +2250,11 @@ class Boss:
|
|||||||
output = '\n'.join(f'{k}={v}' for k, v in os.environ.items())
|
output = '\n'.join(f'{k}={v}' for k, v in os.environ.items())
|
||||||
self.display_scrollback(w, output, title=_('Current kitty env vars'), report_cursor=False)
|
self.display_scrollback(w, output, title=_('Current kitty env vars'), report_cursor=False)
|
||||||
|
|
||||||
@ac('debug', 'Close all shared SSH connections')
|
@ac('debug', '''
|
||||||
|
Close all shared SSH connections
|
||||||
|
|
||||||
|
See :opt:`share_connections <kitten-ssh.share_connections>` for details.
|
||||||
|
''')
|
||||||
def close_shared_ssh_connections(self) -> None:
|
def close_shared_ssh_connections(self) -> None:
|
||||||
cleanup_ssh_control_masters()
|
cleanup_ssh_control_masters()
|
||||||
|
|
||||||
@@ -2308,8 +2286,8 @@ class Boss:
|
|||||||
|
|
||||||
if failures:
|
if failures:
|
||||||
from kittens.tui.operations import styled
|
from kittens.tui.operations import styled
|
||||||
spec = '\n '.join(styled(u, fg='red') for u in failures)
|
spec = '\n '.join(styled(u, fg='yellow') for u in failures)
|
||||||
bdata = f"Unknown URL type, cannot open:\n\n {spec}".encode('utf-8')
|
bdata = json.dumps({'msg': f"Unknown URL type, cannot open:\n {spec}"}).encode('utf-8')
|
||||||
special_window = SpecialWindow([kitty_exe(), '+kitten', 'show_error', '--title', 'Open URL Error'], bdata, 'Open URL Error')
|
special_window = SpecialWindow([kitty_exe(), '+kitten', 'show_error', '--title', 'Open URL Error'], bdata, 'Open URL Error')
|
||||||
if needs_window_replaced and tab is not None:
|
if needs_window_replaced and tab is not None:
|
||||||
tab.new_special_window(special_window)
|
tab.new_special_window(special_window)
|
||||||
|
|||||||
@@ -23,6 +23,10 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
extern PyTypeObject Screen_Type;
|
extern PyTypeObject Screen_Type;
|
||||||
|
|
||||||
|
#if defined(__APPLE__) || defined(__OpenBSD__)
|
||||||
|
#define NO_SIGQUEUE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_EVENT_LOOP
|
#ifdef DEBUG_EVENT_LOOP
|
||||||
#define EVDBG(...) log_event(__VA_ARGS__)
|
#define EVDBG(...) log_event(__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
@@ -133,8 +137,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
||||||
if (!init_loop_data(&self->io_loop_data)) return PyErr_SetFromErrno(PyExc_OSError);
|
if (!init_loop_data(&self->io_loop_data, 5, SIGINT, SIGTERM, SIGCHLD, SIGUSR1, SIGUSR2)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
if (!install_signal_handlers(&self->io_loop_data)) return PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
self->talk_fd = talk_fd;
|
self->talk_fd = talk_fd;
|
||||||
self->listen_fd = listen_fd;
|
self->listen_fd = listen_fd;
|
||||||
if (self == NULL) return PyErr_NoMemory();
|
if (self == NULL) return PyErr_NoMemory();
|
||||||
@@ -424,7 +427,7 @@ parse_input(ChildMonitor *self) {
|
|||||||
}
|
}
|
||||||
if (resp) {
|
if (resp) {
|
||||||
if (PyBytes_Check(resp)) send_response_to_peer(msg->peer_id, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp));
|
if (PyBytes_Check(resp)) send_response_to_peer(msg->peer_id, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp));
|
||||||
else if (resp == Py_None) send_response_to_peer(msg->peer_id, NULL, 0);
|
else if (resp == Py_None || resp == Py_True) send_response_to_peer(msg->peer_id, NULL, 0);
|
||||||
Py_CLEAR(resp);
|
Py_CLEAR(resp);
|
||||||
} else send_response_to_peer(msg->peer_id, NULL, 0);
|
} else send_response_to_peer(msg->peer_id, NULL, 0);
|
||||||
}
|
}
|
||||||
@@ -801,11 +804,25 @@ free_twd(ThreadWriteData *x) {
|
|||||||
free(x);
|
free(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
sig_queue(PyObject *self UNUSED, PyObject *args) {
|
||||||
|
int pid, signal, value;
|
||||||
|
if (!PyArg_ParseTuple(args, "iii", &pid, &signal, &value)) return NULL;
|
||||||
|
#ifdef NO_SIGQUEUE
|
||||||
|
if (kill(pid, signal) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||||
|
#else
|
||||||
|
union sigval v;
|
||||||
|
v.sival_int = value;
|
||||||
|
if (sigqueue(pid, signal, v) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||||
|
#endif
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
monitor_pid(PyObject *self UNUSED, PyObject *args) {
|
monitor_pid(PyObject *self UNUSED, PyObject *args) {
|
||||||
long pid;
|
int pid;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (!PyArg_ParseTuple(args, "l", &pid)) return NULL;
|
if (!PyArg_ParseTuple(args, "i", &pid)) return NULL;
|
||||||
children_mutex(lock);
|
children_mutex(lock);
|
||||||
if (monitored_pids_count >= arraysz(monitored_pids)) {
|
if (monitored_pids_count >= arraysz(monitored_pids)) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Too many monitored pids");
|
PyErr_SetString(PyExc_RuntimeError, "Too many monitored pids");
|
||||||
@@ -1041,6 +1058,7 @@ process_cocoa_pending_actions(void) {
|
|||||||
if (cocoa_pending_actions[RELOAD_CONFIG]) { call_boss(load_config_file, NULL); }
|
if (cocoa_pending_actions[RELOAD_CONFIG]) { call_boss(load_config_file, NULL); }
|
||||||
if (cocoa_pending_actions[TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY]) { call_boss(toggle_macos_secure_keyboard_entry, NULL); }
|
if (cocoa_pending_actions[TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY]) { call_boss(toggle_macos_secure_keyboard_entry, NULL); }
|
||||||
if (cocoa_pending_actions[TOGGLE_FULLSCREEN]) { call_boss(toggle_fullscreen, NULL); }
|
if (cocoa_pending_actions[TOGGLE_FULLSCREEN]) { call_boss(toggle_fullscreen, NULL); }
|
||||||
|
if (cocoa_pending_actions[OPEN_KITTY_WEBSITE]) { call_boss(open_kitty_website, NULL); }
|
||||||
if (cocoa_pending_actions_data.wd) {
|
if (cocoa_pending_actions_data.wd) {
|
||||||
if (cocoa_pending_actions[NEW_OS_WINDOW_WITH_WD]) { call_boss(new_os_window_with_wd, "sO", cocoa_pending_actions_data.wd, Py_True); }
|
if (cocoa_pending_actions[NEW_OS_WINDOW_WITH_WD]) { call_boss(new_os_window_with_wd, "sO", cocoa_pending_actions_data.wd, Py_True); }
|
||||||
if (cocoa_pending_actions[NEW_TAB_WITH_WD]) { call_boss(new_tab_with_wd, "sO", cocoa_pending_actions_data.wd, Py_True); }
|
if (cocoa_pending_actions[NEW_TAB_WITH_WD]) { call_boss(new_tab_with_wd, "sO", cocoa_pending_actions_data.wd, Py_True); }
|
||||||
@@ -1223,9 +1241,9 @@ read_bytes(int fd, Screen *screen) {
|
|||||||
typedef struct { bool kill_signal, child_died, reload_config; } SignalSet;
|
typedef struct { bool kill_signal, child_died, reload_config; } SignalSet;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_signal(int signum, void *data) {
|
handle_signal(const siginfo_t *siginfo, void *data) {
|
||||||
SignalSet *ss = data;
|
SignalSet *ss = data;
|
||||||
switch(signum) {
|
switch(siginfo->si_signo) {
|
||||||
case SIGINT:
|
case SIGINT:
|
||||||
case SIGTERM:
|
case SIGTERM:
|
||||||
ss->kill_signal = true;
|
ss->kill_signal = true;
|
||||||
@@ -1236,6 +1254,9 @@ handle_signal(int signum, void *data) {
|
|||||||
case SIGUSR1:
|
case SIGUSR1:
|
||||||
ss->reload_config = true;
|
ss->reload_config = true;
|
||||||
break;
|
break;
|
||||||
|
case SIGUSR2:
|
||||||
|
log_error("Received SIGUSR2: %d\n", siginfo->si_value.sival_int);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1415,7 +1436,7 @@ io_loop(void *data) {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
id_type id;
|
id_type id;
|
||||||
size_t num_of_unresponded_messages_sent_to_main_thread;
|
size_t num_of_unresponded_messages_sent_to_main_thread, fd_array_idx;
|
||||||
bool finished_reading;
|
bool finished_reading;
|
||||||
int fd;
|
int fd;
|
||||||
struct {
|
struct {
|
||||||
@@ -1569,8 +1590,7 @@ wakeup_talk_loop(bool in_signal_handler) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
prune_peers(void) {
|
prune_peers(void) {
|
||||||
for (size_t i = 0; i < talk_data.num_peers; i++) {
|
for (size_t idx = talk_data.num_peers; idx-- > 0;) {
|
||||||
size_t idx = talk_data.num_peers - 1 - i;
|
|
||||||
Peer *p = talk_data.peers + idx;
|
Peer *p = talk_data.peers + idx;
|
||||||
if (p->read.finished && !p->num_of_unresponded_messages_sent_to_main_thread && !p->write.used) {
|
if (p->read.finished && !p->num_of_unresponded_messages_sent_to_main_thread && !p->write.used) {
|
||||||
free_peer(p);
|
free_peer(p);
|
||||||
@@ -1584,7 +1604,7 @@ talk_loop(void *data) {
|
|||||||
// The talk thread loop
|
// The talk thread loop
|
||||||
ChildMonitor *self = (ChildMonitor*)data;
|
ChildMonitor *self = (ChildMonitor*)data;
|
||||||
set_thread_name("KittyPeerMon");
|
set_thread_name("KittyPeerMon");
|
||||||
if (!init_loop_data(&talk_data.loop_data)) { log_error("Failed to create wakeup fd for talk thread with error: %s", strerror(errno)); }
|
if (!init_loop_data(&talk_data.loop_data, 0)) { log_error("Failed to create wakeup fd for talk thread with error: %s", strerror(errno)); }
|
||||||
PollFD fds[PEER_LIMIT + 8] = {{0}};
|
PollFD fds[PEER_LIMIT + 8] = {{0}};
|
||||||
size_t num_listen_fds = 0, num_peer_fds = 0;
|
size_t num_listen_fds = 0, num_peer_fds = 0;
|
||||||
#define add_listener(which) \
|
#define add_listener(which) \
|
||||||
@@ -1603,13 +1623,14 @@ talk_loop(void *data) {
|
|||||||
for (size_t i = 0; i < talk_data.num_peers; i++) {
|
for (size_t i = 0; i < talk_data.num_peers; i++) {
|
||||||
Peer *p = talk_data.peers + i;
|
Peer *p = talk_data.peers + i;
|
||||||
if (!p->read.finished || p->write.used) {
|
if (!p->read.finished || p->write.used) {
|
||||||
fds[num_listen_fds + num_peer_fds].fd = p->fd;
|
p->fd_array_idx = num_listen_fds + num_peer_fds++;
|
||||||
fds[num_listen_fds + num_peer_fds].revents = 0;
|
fds[p->fd_array_idx].fd = p->fd;
|
||||||
|
fds[p->fd_array_idx].revents = 0;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (!p->read.finished) flags |= POLLIN;
|
if (!p->read.finished) flags |= POLLIN;
|
||||||
if (p->write.used) flags |= POLLOUT;
|
if (p->write.used) flags |= POLLOUT;
|
||||||
fds[num_listen_fds + num_peer_fds++].events = flags;
|
fds[p->fd_array_idx].events = flags;
|
||||||
}
|
} else p->fd_array_idx = 0;
|
||||||
}
|
}
|
||||||
talk_mutex(unlock);
|
talk_mutex(unlock);
|
||||||
}
|
}
|
||||||
@@ -1624,13 +1645,16 @@ talk_loop(void *data) {
|
|||||||
if (fds[num_listen_fds - 1].revents & POLLIN) {
|
if (fds[num_listen_fds - 1].revents & POLLIN) {
|
||||||
drain_fd(fds[num_listen_fds - 1].fd); // wakeup
|
drain_fd(fds[num_listen_fds - 1].fd); // wakeup
|
||||||
}
|
}
|
||||||
for (size_t i = num_listen_fds, k = 0; i < num_peer_fds + num_listen_fds; i++, k++) {
|
for (size_t k = 0; k < talk_data.num_peers; k++) {
|
||||||
Peer *p = talk_data.peers + k;
|
Peer *p = talk_data.peers + k;
|
||||||
if (fds[i].revents & (POLLIN | POLLHUP)) read_from_peer(self, p);
|
if (p->fd_array_idx) {
|
||||||
if (fds[i].revents & POLLOUT) write_to_peer(p);
|
if (fds[p->fd_array_idx].revents & (POLLIN | POLLHUP)) read_from_peer(self, p);
|
||||||
if (fds[i].revents & POLLNVAL) {
|
if (fds[p->fd_array_idx].revents & POLLOUT) write_to_peer(p);
|
||||||
p->read.finished = true;
|
if (fds[p->fd_array_idx].revents & POLLNVAL) {
|
||||||
p->write.failed = true; p->write.used = 0;
|
p->read.finished = true;
|
||||||
|
p->write.failed = true; p->write.used = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ret < 0) { if (errno != EAGAIN && errno != EINTR) perror("poll() on talk fds failed"); }
|
} else if (ret < 0) { if (errno != EAGAIN && errno != EINTR) perror("poll() on talk fds failed"); }
|
||||||
@@ -1735,6 +1759,7 @@ static PyMethodDef module_methods[] = {
|
|||||||
METHODB(monitor_pid, METH_VARARGS),
|
METHODB(monitor_pid, METH_VARARGS),
|
||||||
METHODB(send_data_to_peer, METH_VARARGS),
|
METHODB(send_data_to_peer, METH_VARARGS),
|
||||||
METHODB(cocoa_set_menubar_title, METH_VARARGS),
|
METHODB(cocoa_set_menubar_title, METH_VARARGS),
|
||||||
|
{"sigqueue", (PyCFunction)sig_queue, METH_VARARGS, ""},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1744,6 +1769,11 @@ init_child_monitor(PyObject *module) {
|
|||||||
if (PyModule_AddObject(module, "ChildMonitor", (PyObject *)&ChildMonitor_Type) != 0) return false;
|
if (PyModule_AddObject(module, "ChildMonitor", (PyObject *)&ChildMonitor_Type) != 0) return false;
|
||||||
Py_INCREF(&ChildMonitor_Type);
|
Py_INCREF(&ChildMonitor_Type);
|
||||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
|
#ifdef NO_SIGQUEUE
|
||||||
|
PyModule_AddIntConstant(module, "has_sigqueue", 0);
|
||||||
|
#else
|
||||||
|
PyModule_AddIntConstant(module, "has_sigqueue", 1);
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,10 +98,17 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
|||||||
#if PY_VERSION_HEX >= 0x03070000
|
#if PY_VERSION_HEX >= 0x03070000
|
||||||
PyOS_AfterFork_Child();
|
PyOS_AfterFork_Child();
|
||||||
#endif
|
#endif
|
||||||
|
// See _Py_RestoreSignals in signalmodule.c for a list of signals python nukes
|
||||||
sigset_t signals = {0};
|
sigset_t signals = {0};
|
||||||
struct sigaction act = {.sa_handler=SIG_DFL};
|
struct sigaction act = {.sa_handler=SIG_DFL};
|
||||||
#define SA(which) { if (sigaction(which, &act, NULL) != 0) exit_on_err("sigaction() in child process failed"); }
|
#define SA(which) { if (sigaction(which, &act, NULL) != 0) exit_on_err("sigaction() in child process failed"); }
|
||||||
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD);
|
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD); SA(SIGPIPE); SA(SIGUSR1); SA(SIGUSR2);
|
||||||
|
#ifdef SIGXFSZ
|
||||||
|
SA(SIGXFSZ);
|
||||||
|
#endif
|
||||||
|
#ifdef SIGXFZ
|
||||||
|
SA(SIGXFZ);
|
||||||
|
#endif
|
||||||
#undef SA
|
#undef SA
|
||||||
if (sigprocmask(SIG_SETMASK, &signals, NULL) != 0) exit_on_err("sigprocmask() in child process failed");
|
if (sigprocmask(SIG_SETMASK, &signals, NULL) != 0) exit_on_err("sigprocmask() in child process failed");
|
||||||
// Use only signal-safe functions (man 7 signal-safety)
|
// Use only signal-safe functions (man 7 signal-safety)
|
||||||
@@ -139,9 +146,6 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
|||||||
for (int c = 3; c < 201; c++) safe_close(c, __FILE__, __LINE__);
|
for (int c = 3; c < 201; c++) safe_close(c, __FILE__, __LINE__);
|
||||||
|
|
||||||
environ = env;
|
environ = env;
|
||||||
// for some reason SIGPIPE is set to SIG_IGN, so reset it, needed by bash,
|
|
||||||
// which does not reset signal handlers on its own
|
|
||||||
signal(SIGPIPE, SIG_DFL);
|
|
||||||
execvp(exe, argv);
|
execvp(exe, argv);
|
||||||
// Report the failure and exec a shell instead, so that we are not left
|
// Report the failure and exec a shell instead, so that we are not left
|
||||||
// with a forked but not exec'ed process
|
// with a forked but not exec'ed process
|
||||||
@@ -176,8 +180,27 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
|||||||
return PyLong_FromLong(pid);
|
return PyLong_FromLong(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
establish_controlling_tty(PyObject *self UNUSED, PyObject *args) {
|
||||||
|
const char *ttyname;
|
||||||
|
int stdin_fd, stdout_fd, stderr_fd;
|
||||||
|
if (!PyArg_ParseTuple(args, "siii", &ttyname, &stdin_fd, &stdout_fd, &stderr_fd)) return NULL;
|
||||||
|
int tfd = safe_open(ttyname, O_RDWR, 0);
|
||||||
|
if (tfd == -1) return PyErr_SetFromErrnoWithFilename(PyExc_OSError, ttyname);
|
||||||
|
#ifdef TIOCSCTTY
|
||||||
|
// On BSD open() does not establish the controlling terminal
|
||||||
|
if (ioctl(tfd, TIOCSCTTY, 0) == -1) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
#endif
|
||||||
|
if (dup2(tfd, stdin_fd) == -1) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
if (dup2(tfd, stdout_fd) == -1) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
if (dup2(tfd, stderr_fd) == -1) return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
safe_close(tfd, __FILE__, __LINE__);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
METHODB(spawn, METH_VARARGS),
|
METHODB(spawn, METH_VARARGS),
|
||||||
|
METHODB(establish_controlling_tty, METH_VARARGS),
|
||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
144
kitty/child.py
144
kitty/child.py
@@ -1,18 +1,20 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import fcntl
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from contextlib import contextmanager, suppress
|
from contextlib import contextmanager, suppress
|
||||||
from typing import (
|
from typing import (
|
||||||
DefaultDict, Dict, Generator, List, Optional, Sequence, Tuple, TYPE_CHECKING
|
TYPE_CHECKING, DefaultDict, Dict, Generator, List, Optional, Sequence,
|
||||||
|
Tuple
|
||||||
)
|
)
|
||||||
|
|
||||||
import kitty.fast_data_types as fast_data_types
|
import kitty.fast_data_types as fast_data_types
|
||||||
|
|
||||||
from .constants import is_macos, kitty_base_dir, shell_path, terminfo_dir
|
from .constants import (
|
||||||
|
is_freebsd, is_macos, kitty_base_dir, shell_path, terminfo_dir
|
||||||
|
)
|
||||||
from .types import run_once
|
from .types import run_once
|
||||||
from .utils import log_error, which
|
from .utils import log_error, which
|
||||||
|
|
||||||
@@ -40,17 +42,26 @@ if is_macos:
|
|||||||
ans[pgid].append(pid)
|
ans[pgid].append(pid)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def cmdline_of_process(pid: int) -> List[str]:
|
def cmdline_of_pid(pid: int) -> List[str]:
|
||||||
return cmdline_(pid)
|
return cmdline_(pid)
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def cmdline_of_process(pid: int) -> List[str]:
|
def cmdline_of_pid(pid: int) -> List[str]:
|
||||||
with open(f'/proc/{pid}/cmdline', 'rb') as f:
|
with open(f'/proc/{pid}/cmdline', 'rb') as f:
|
||||||
return list(filter(None, f.read().decode('utf-8').split('\0')))
|
return list(filter(None, f.read().decode('utf-8').split('\0')))
|
||||||
|
|
||||||
def cwd_of_process(pid: int) -> str:
|
if is_freebsd:
|
||||||
ans = f'/proc/{pid}/cwd'
|
def cwd_of_process(pid: int) -> str:
|
||||||
return os.path.realpath(ans)
|
import subprocess
|
||||||
|
cp = subprocess.run(['pwdx', str(pid)], capture_output=True)
|
||||||
|
if cp.returncode != 0:
|
||||||
|
raise ValueError(f'Failed to find cwd of process with pid: {pid}')
|
||||||
|
ans = cp.stdout.decode('utf-8', 'replace').split()[1]
|
||||||
|
return os.path.realpath(ans)
|
||||||
|
else:
|
||||||
|
def cwd_of_process(pid: int) -> str:
|
||||||
|
ans = f'/proc/{pid}/cwd'
|
||||||
|
return os.path.realpath(ans)
|
||||||
|
|
||||||
def _environ_of_process(pid: int) -> str:
|
def _environ_of_process(pid: int) -> str:
|
||||||
with open(f'/proc/{pid}/environ', 'rb') as f:
|
with open(f'/proc/{pid}/environ', 'rb') as f:
|
||||||
@@ -132,7 +143,7 @@ def environ_of_process(pid: int) -> Dict[str, str]:
|
|||||||
|
|
||||||
|
|
||||||
def remove_cloexec(fd: int) -> None:
|
def remove_cloexec(fd: int) -> None:
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.fcntl(fd, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC)
|
os.set_inheritable(fd, True)
|
||||||
|
|
||||||
|
|
||||||
def remove_blocking(fd: int) -> None:
|
def remove_blocking(fd: int) -> None:
|
||||||
@@ -195,9 +206,11 @@ class Child:
|
|||||||
stdin: Optional[bytes] = None,
|
stdin: Optional[bytes] = None,
|
||||||
env: Optional[Dict[str, str]] = None,
|
env: Optional[Dict[str, str]] = None,
|
||||||
cwd_from: Optional['CwdRequest'] = None,
|
cwd_from: Optional['CwdRequest'] = None,
|
||||||
allow_remote_control: bool = False
|
allow_remote_control: bool = False,
|
||||||
|
is_clone_launch: str = '',
|
||||||
):
|
):
|
||||||
self.allow_remote_control = allow_remote_control
|
self.allow_remote_control = allow_remote_control
|
||||||
|
self.is_clone_launch = is_clone_launch
|
||||||
self.argv = list(argv)
|
self.argv = list(argv)
|
||||||
if cwd_from:
|
if cwd_from:
|
||||||
try:
|
try:
|
||||||
@@ -210,33 +223,36 @@ class Child:
|
|||||||
self.stdin = stdin
|
self.stdin = stdin
|
||||||
self.env = env or {}
|
self.env = env or {}
|
||||||
|
|
||||||
@property
|
|
||||||
def final_env(self) -> Dict[str, str]:
|
def final_env(self) -> Dict[str, str]:
|
||||||
from kitty.options.utils import DELETE_ENV_VAR
|
from kitty.options.utils import DELETE_ENV_VAR
|
||||||
env: Optional[Dict[str, str]] = getattr(self, '_final_env', None)
|
env = default_env().copy()
|
||||||
if env is None:
|
if is_macos and env.get('LC_CTYPE') == 'UTF-8' and not sys._xoptions.get(
|
||||||
env = self._final_env = default_env().copy()
|
'lc_ctype_before_python') and not getattr(default_env, 'lc_ctype_set_by_user', False):
|
||||||
if is_macos and env.get('LC_CTYPE') == 'UTF-8' and not sys._xoptions.get(
|
del env['LC_CTYPE']
|
||||||
'lc_ctype_before_python') and not getattr(default_env, 'lc_ctype_set_by_user', False):
|
env.update(self.env)
|
||||||
del env['LC_CTYPE']
|
env['TERM'] = fast_data_types.get_options().term
|
||||||
env.update(self.env)
|
env['COLORTERM'] = 'truecolor'
|
||||||
env['TERM'] = fast_data_types.get_options().term
|
env['KITTY_PID'] = getpid()
|
||||||
env['COLORTERM'] = 'truecolor'
|
if self.cwd:
|
||||||
env['KITTY_PID'] = getpid()
|
# needed in case cwd is a symlink, in which case shells
|
||||||
if self.cwd:
|
# can use it to display the current directory name rather
|
||||||
# needed in case cwd is a symlink, in which case shells
|
# than the resolved path
|
||||||
# can use it to display the current directory name rather
|
env['PWD'] = self.cwd
|
||||||
# than the resolved path
|
tdir = checked_terminfo_dir()
|
||||||
env['PWD'] = self.cwd
|
if tdir:
|
||||||
tdir = checked_terminfo_dir()
|
env['TERMINFO'] = tdir
|
||||||
if tdir:
|
env['KITTY_INSTALLATION_DIR'] = kitty_base_dir
|
||||||
env['TERMINFO'] = tdir
|
opts = fast_data_types.get_options()
|
||||||
env['KITTY_INSTALLATION_DIR'] = kitty_base_dir
|
self.unmodified_argv = list(self.argv)
|
||||||
opts = fast_data_types.get_options()
|
if 'disabled' not in opts.shell_integration:
|
||||||
if 'disabled' not in opts.shell_integration:
|
from .shell_integration import modify_shell_environ
|
||||||
from .shell_integration import modify_shell_environ
|
modify_shell_environ(opts, env, self.argv)
|
||||||
modify_shell_environ(opts, env, self.argv)
|
env = {k: v for k, v in env.items() if v is not DELETE_ENV_VAR}
|
||||||
env = {k: v for k, v in env.items() if v is not DELETE_ENV_VAR}
|
if self.is_clone_launch:
|
||||||
|
env['KITTY_IS_CLONE_LAUNCH'] = self.is_clone_launch
|
||||||
|
self.is_clone_launch = '1' # free memory
|
||||||
|
else:
|
||||||
|
env.pop('KITTY_IS_CLONE_LAUNCH', None)
|
||||||
return env
|
return env
|
||||||
|
|
||||||
def fork(self) -> Optional[int]:
|
def fork(self) -> Optional[int]:
|
||||||
@@ -252,7 +268,7 @@ class Child:
|
|||||||
remove_cloexec(stdin_read_fd)
|
remove_cloexec(stdin_read_fd)
|
||||||
else:
|
else:
|
||||||
stdin_read_fd = stdin_write_fd = -1
|
stdin_read_fd = stdin_write_fd = -1
|
||||||
env = tuple(f'{k}={v}' for k, v in self.final_env.items())
|
env = tuple(f'{k}={v}' for k, v in self.final_env().items())
|
||||||
argv = list(self.argv)
|
argv = list(self.argv)
|
||||||
exe = argv[0]
|
exe = argv[0]
|
||||||
if is_macos and exe == shell_path:
|
if is_macos and exe == shell_path:
|
||||||
@@ -271,8 +287,9 @@ class Child:
|
|||||||
# xterm, urxvt, konsole and gnome-terminal do not do it in my
|
# xterm, urxvt, konsole and gnome-terminal do not do it in my
|
||||||
# testing.
|
# testing.
|
||||||
argv[0] = (f'-{exe.split("/")[-1]}')
|
argv[0] = (f'-{exe.split("/")[-1]}')
|
||||||
exe = which(exe) or exe
|
self.final_exe = which(exe) or exe
|
||||||
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd)
|
self.final_argv0 = argv[0]
|
||||||
|
pid = fast_data_types.spawn(self.final_exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd)
|
||||||
os.close(slave)
|
os.close(slave)
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
self.child_fd = master
|
self.child_fd = master
|
||||||
@@ -289,6 +306,15 @@ class Child:
|
|||||||
os.close(self.terminal_ready_fd)
|
os.close(self.terminal_ready_fd)
|
||||||
self.terminal_ready_fd = -1
|
self.terminal_ready_fd = -1
|
||||||
|
|
||||||
|
def cmdline_of_pid(self, pid: int) -> List[str]:
|
||||||
|
try:
|
||||||
|
ans = cmdline_of_pid(pid)
|
||||||
|
except Exception:
|
||||||
|
ans = []
|
||||||
|
if not ans and pid == self.pid:
|
||||||
|
ans = list(self.argv)
|
||||||
|
return ans
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def foreground_processes(self) -> List[ProcessDesc]:
|
def foreground_processes(self) -> List[ProcessDesc]:
|
||||||
if self.child_fd is None:
|
if self.child_fd is None:
|
||||||
@@ -300,7 +326,7 @@ class Child:
|
|||||||
def process_desc(pid: int) -> ProcessDesc:
|
def process_desc(pid: int) -> ProcessDesc:
|
||||||
ans: ProcessDesc = {'pid': pid, 'cmdline': None, 'cwd': None}
|
ans: ProcessDesc = {'pid': pid, 'cmdline': None, 'cwd': None}
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
ans['cmdline'] = cmdline_of_process(pid)
|
ans['cmdline'] = self.cmdline_of_pid(pid)
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
ans['cwd'] = cwd_of_process(pid) or None
|
ans['cwd'] = cwd_of_process(pid) or None
|
||||||
return ans
|
return ans
|
||||||
@@ -313,7 +339,7 @@ class Child:
|
|||||||
def cmdline(self) -> List[str]:
|
def cmdline(self) -> List[str]:
|
||||||
try:
|
try:
|
||||||
assert self.pid is not None
|
assert self.pid is not None
|
||||||
return cmdline_of_process(self.pid) or list(self.argv)
|
return self.cmdline_of_pid(self.pid) or list(self.argv)
|
||||||
except Exception:
|
except Exception:
|
||||||
return list(self.argv)
|
return list(self.argv)
|
||||||
|
|
||||||
@@ -321,7 +347,7 @@ class Child:
|
|||||||
def foreground_cmdline(self) -> List[str]:
|
def foreground_cmdline(self) -> List[str]:
|
||||||
try:
|
try:
|
||||||
assert self.pid_for_cwd is not None
|
assert self.pid_for_cwd is not None
|
||||||
return cmdline_of_process(self.pid_for_cwd) or self.cmdline
|
return self.cmdline_of_pid(self.pid_for_cwd) or self.cmdline
|
||||||
except Exception:
|
except Exception:
|
||||||
return self.cmdline
|
return self.cmdline
|
||||||
|
|
||||||
@@ -340,8 +366,7 @@ class Child:
|
|||||||
return cwd_of_process(self.pid)
|
return cwd_of_process(self.pid)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
def get_pid_for_cwd(self, oldest: bool = False) -> Optional[int]:
|
||||||
def pid_for_cwd(self) -> Optional[int]:
|
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
assert self.child_fd is not None
|
assert self.child_fd is not None
|
||||||
pgrp = os.tcgetpgrp(self.child_fd)
|
pgrp = os.tcgetpgrp(self.child_fd)
|
||||||
@@ -357,25 +382,32 @@ class Child:
|
|||||||
# vim
|
# vim
|
||||||
# With this script , the foreground process group will contain
|
# With this script , the foreground process group will contain
|
||||||
# both the bash instance running the script and vim.
|
# both the bash instance running the script and vim.
|
||||||
return max(foreground_processes)
|
return min(foreground_processes) if oldest else max(foreground_processes)
|
||||||
return self.pid
|
return self.pid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def foreground_cwd(self) -> Optional[str]:
|
def pid_for_cwd(self) -> Optional[int]:
|
||||||
|
return self.get_pid_for_cwd()
|
||||||
|
|
||||||
|
def get_foreground_cwd(self, oldest: bool = False) -> Optional[str]:
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
assert self.pid_for_cwd is not None
|
pid = self.get_pid_for_cwd(oldest)
|
||||||
return cwd_of_process(self.pid_for_cwd) or None
|
if pid is not None:
|
||||||
|
return cwd_of_process(pid) or None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def foreground_cwd(self) -> Optional[str]:
|
||||||
|
return self.get_foreground_cwd()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def foreground_environ(self) -> Dict[str, str]:
|
def foreground_environ(self) -> Dict[str, str]:
|
||||||
try:
|
pid = self.pid_for_cwd
|
||||||
assert self.pid_for_cwd is not None
|
if pid is not None:
|
||||||
return environ_of_process(self.pid_for_cwd)
|
with suppress(Exception):
|
||||||
except Exception:
|
return environ_of_process(pid)
|
||||||
try:
|
pid = self.pid
|
||||||
assert self.pid is not None
|
if pid is not None:
|
||||||
return environ_of_process(self.pid)
|
with suppress(Exception):
|
||||||
except Exception:
|
return environ_of_process(pid)
|
||||||
pass
|
|
||||||
return {}
|
return {}
|
||||||
|
|||||||
144
kitty/cli.py
144
kitty/cli.py
@@ -28,26 +28,27 @@ class OptionDict(TypedDict):
|
|||||||
|
|
||||||
CONFIG_HELP = '''\
|
CONFIG_HELP = '''\
|
||||||
Specify a path to the configuration file(s) to use. All configuration files are
|
Specify a path to the configuration file(s) to use. All configuration files are
|
||||||
merged onto the builtin {conf_name}.conf, overriding the builtin values. This option
|
merged onto the builtin :file:`{conf_name}.conf`, overriding the builtin values.
|
||||||
can be specified multiple times to read multiple configuration files in
|
This option can be specified multiple times to read multiple configuration files
|
||||||
sequence, which are merged. Use the special value NONE to not load a config
|
in sequence, which are merged. Use the special value :code:`NONE` to not load
|
||||||
file.
|
any config file.
|
||||||
|
|
||||||
If this option is not specified, config files are searched for in the order:
|
If this option is not specified, config files are searched for in the order:
|
||||||
:file:`$XDG_CONFIG_HOME/{appname}/{conf_name}.conf`, :file:`~/.config/{appname}/{conf_name}.conf`, {macos_confpath}
|
:file:`$XDG_CONFIG_HOME/{appname}/{conf_name}.conf`,
|
||||||
:file:`$XDG_CONFIG_DIRS/{appname}/{conf_name}.conf`. The first one that exists is used as the
|
:file:`~/.config/{appname}/{conf_name}.conf`,{macos_confpath}
|
||||||
config file.
|
:file:`$XDG_CONFIG_DIRS/{appname}/{conf_name}.conf`. The first one that exists
|
||||||
|
is used as the config file.
|
||||||
|
|
||||||
If the environment variable :envvar:`KITTY_CONFIG_DIRECTORY` is specified, that
|
If the environment variable :envvar:`KITTY_CONFIG_DIRECTORY` is specified, that
|
||||||
directory is always used and the above searching does not happen.
|
directory is always used and the above searching does not happen.
|
||||||
|
|
||||||
If :file:`/etc/xdg/{appname}/{conf_name}.conf` exists it is merged before (i.e. with lower
|
If :file:`/etc/xdg/{appname}/{conf_name}.conf` exists, it is merged before (i.e.
|
||||||
priority) than any user config files. It can be used to specify system-wide
|
with lower priority) than any user config files. It can be used to specify
|
||||||
defaults for all users. You can use either :code:`-` or :code:`/dev/stdin` to read the
|
system-wide defaults for all users. You can use either :code:`-` or
|
||||||
config from STDIN.
|
:file:`/dev/stdin` to read the config from STDIN.
|
||||||
'''.replace(
|
'''.replace(
|
||||||
'{macos_confpath}',
|
'{macos_confpath}',
|
||||||
(':file:`~/Library/Preferences/{appname}/{conf_name}.conf`,' if is_macos else ''), 1
|
(' :file:`~/Library/Preferences/{appname}/{conf_name}.conf`,' if is_macos else ''), 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -149,6 +150,11 @@ def doc(x: str) -> str:
|
|||||||
return website_url(x)
|
return website_url(x)
|
||||||
|
|
||||||
|
|
||||||
|
@role
|
||||||
|
def ref(x: str) -> str:
|
||||||
|
return re.sub(r'\s*<\S+?>', '', x)
|
||||||
|
|
||||||
|
|
||||||
OptionSpecSeq = List[Union[str, OptionDict]]
|
OptionSpecSeq = List[Union[str, OptionDict]]
|
||||||
|
|
||||||
|
|
||||||
@@ -289,9 +295,10 @@ def get_defaults_from_seq(seq: OptionSpecSeq) -> Dict[str, Any]:
|
|||||||
|
|
||||||
|
|
||||||
default_msg = ('''\
|
default_msg = ('''\
|
||||||
Run the :italic:`{appname}` terminal emulator. You can also specify the :italic:`program`
|
Run the :italic:`{appname}` terminal emulator. You can also specify the
|
||||||
to run inside :italic:`{appname}` as normal arguments following the :italic:`options`.
|
:italic:`program` to run inside :italic:`{appname}` as normal arguments
|
||||||
For example: {appname} sh -c "echo hello, world. Press ENTER to quit; read"
|
following the :italic:`options`.
|
||||||
|
For example: {appname} --hold sh -c "echo hello, world"
|
||||||
|
|
||||||
For comprehensive documentation for kitty, please see: {url}''').format(
|
For comprehensive documentation for kitty, please see: {url}''').format(
|
||||||
appname=appname, url=website_url())
|
appname=appname, url=website_url())
|
||||||
@@ -578,17 +585,20 @@ def options_spec() -> str:
|
|||||||
dest=cls
|
dest=cls
|
||||||
default={appname}
|
default={appname}
|
||||||
condition=not is_macos
|
condition=not is_macos
|
||||||
Set the class part of the :italic:`WM_CLASS` window property. On Wayland, it sets the app id.
|
Set the class part of the :italic:`WM_CLASS` window property. On Wayland, it
|
||||||
|
sets the app id.
|
||||||
|
|
||||||
|
|
||||||
--name
|
--name
|
||||||
condition=not is_macos
|
condition=not is_macos
|
||||||
Set the name part of the :italic:`WM_CLASS` property (defaults to using the value from :option:`{appname} --class`)
|
Set the name part of the :italic:`WM_CLASS` property. Defaults to using the
|
||||||
|
value from :option:`{appname} --class`.
|
||||||
|
|
||||||
|
|
||||||
--title -T
|
--title -T
|
||||||
Set the OS window title. This will override any title set by the program running inside kitty, permanently
|
Set the OS window title. This will override any title set by the program running
|
||||||
fixing the OS Window's title. So only use this if you are running a program that does not set titles.
|
inside kitty, permanently fixing the OS window's title. So only use this if you
|
||||||
|
are running a program that does not set titles.
|
||||||
|
|
||||||
|
|
||||||
--config -c
|
--config -c
|
||||||
@@ -599,68 +609,74 @@ type=list
|
|||||||
--override -o
|
--override -o
|
||||||
type=list
|
type=list
|
||||||
Override individual configuration options, can be specified multiple times.
|
Override individual configuration options, can be specified multiple times.
|
||||||
Syntax: :italic:`name=value`. For example: :option:`kitty -o` font_size=20
|
Syntax: :italic:`name=value`. For example: :option:`{appname} -o` font_size=20
|
||||||
|
|
||||||
|
|
||||||
--directory --working-directory -d
|
--directory --working-directory -d
|
||||||
default=.
|
default=.
|
||||||
Change to the specified directory when launching
|
Change to the specified directory when launching.
|
||||||
|
|
||||||
|
|
||||||
--detach
|
--detach
|
||||||
type=bool-set
|
type=bool-set
|
||||||
condition=not is_macos
|
condition=not is_macos
|
||||||
Detach from the controlling terminal, if any
|
Detach from the controlling terminal, if any.
|
||||||
|
|
||||||
|
|
||||||
--session
|
--session
|
||||||
Path to a file containing the startup :italic:`session` (tabs, windows, layout, programs).
|
Path to a file containing the startup :italic:`session` (tabs, windows, layout,
|
||||||
Use - to read from STDIN. See the README file for details and an example.
|
programs). Use - to read from STDIN. See the :file:`README` file for details and
|
||||||
|
an example.
|
||||||
|
|
||||||
|
|
||||||
--hold
|
--hold
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Remain open after child process exits. Note that this only affects the first
|
Remain open after child process exits. Note that this only affects the first
|
||||||
window. You can quit by either using the close window shortcut or pressing any key.
|
window. You can quit by either using the close window shortcut or pressing any
|
||||||
|
key.
|
||||||
|
|
||||||
|
|
||||||
--single-instance -1
|
--single-instance -1
|
||||||
type=bool-set
|
type=bool-set
|
||||||
If specified only a single instance of :italic:`{appname}` will run. New invocations will
|
If specified only a single instance of :italic:`{appname}` will run. New
|
||||||
instead create a new top-level window in the existing :italic:`{appname}` instance. This
|
invocations will instead create a new top-level window in the existing
|
||||||
allows :italic:`{appname}` to share a single sprite cache on the GPU and also reduces
|
:italic:`{appname}` instance. This allows :italic:`{appname}` to share a single
|
||||||
startup time. You can also have separate groups of :italic:`{appname}` instances by using the
|
sprite cache on the GPU and also reduces startup time. You can also have
|
||||||
:option:`kitty --instance-group` option
|
separate groups of :italic:`{appname}` instances by using the :option:`{appname}
|
||||||
|
--instance-group` option.
|
||||||
|
|
||||||
|
|
||||||
--instance-group
|
--instance-group
|
||||||
Used in combination with the :option:`kitty --single-instance` option. All :italic:`{appname}` invocations
|
Used in combination with the :option:`{appname} --single-instance` option. All
|
||||||
with the same :option:`kitty --instance-group` will result in new windows being created
|
:italic:`{appname}` invocations with the same :option:`{appname}
|
||||||
in the first :italic:`{appname}` instance within that group
|
--instance-group` will result in new windows being created in the first
|
||||||
|
:italic:`{appname}` instance within that group.
|
||||||
|
|
||||||
|
|
||||||
--wait-for-single-instance-window-close
|
--wait-for-single-instance-window-close
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Normally, when using :option:`--single-instance`, :italic:`{appname}` will open a new window in an existing
|
Normally, when using :option:`{appname} --single-instance`, :italic:`{appname}`
|
||||||
instance and quit immediately. With this option, it will not quit till the newly opened
|
will open a new window in an existing instance and quit immediately. With this
|
||||||
window is closed. Note that if no previous instance is found, then :italic:`{appname}` will wait anyway,
|
option, it will not quit till the newly opened window is closed. Note that if no
|
||||||
|
previous instance is found, then :italic:`{appname}` will wait anyway,
|
||||||
regardless of this option.
|
regardless of this option.
|
||||||
|
|
||||||
|
|
||||||
--listen-on
|
--listen-on
|
||||||
Tell kitty to listen on the specified address for control
|
Listen on the specified socket address for control messages. For example,
|
||||||
messages. For example, :option:`{appname} --listen-on`=unix:/tmp/mykitty or
|
:option:`{appname} --listen-on`=unix:/tmp/mykitty or
|
||||||
:option:`{appname} --listen-on`=tcp:localhost:12345. On Linux systems, you can
|
:option:`{appname} --listen-on`=tcp:localhost:12345. On Linux systems, you can
|
||||||
also use abstract UNIX sockets, not associated with a file, like this:
|
also use abstract UNIX sockets, not associated with a file, like this:
|
||||||
:option:`{appname} --listen-on`=unix:@mykitty. Environment variables
|
:option:`{appname} --listen-on`=unix:@mykitty. Environment variables are
|
||||||
in the setting are expanded and relative paths are resolved with
|
expanded and relative paths are resolved with respect to the temporary
|
||||||
respect to the temporary directory. To control kitty, you can send
|
directory. To control kitty, you can send commands to it with
|
||||||
it commands with :italic:`kitty @` using the :option:`kitty @ --to` option to
|
:italic:`{appname} @` using the :option:`{appname} @ --to` option to specify
|
||||||
specify this address. This option will be ignored, unless you set
|
this address. Unless you enabled :opt:`allow_remote_control` in
|
||||||
:opt:`allow_remote_control` to yes in :file:`kitty.conf`. Note that if you run
|
:file:`{conf_name}.conf`, this option will be ignored. Note that if you run
|
||||||
:italic:`kitty @` within a kitty window, there is no need to specify the :option:`kitty @ --to`
|
:italic:`{appname} @` within a kitty window, there is
|
||||||
option as it is read automatically from the environment. For UNIX sockets, this
|
no need to specify the :option:`{appname} @ --to` option as it will
|
||||||
can also be specified in :file:`kitty.conf`.
|
automatically read from the environment. For UNIX sockets, this can also be
|
||||||
|
specified in :file:`{conf_name}.conf`.
|
||||||
|
|
||||||
|
|
||||||
--start-as
|
--start-as
|
||||||
@@ -674,45 +690,48 @@ Control how the initial kitty window is created.
|
|||||||
|
|
||||||
--version -v
|
--version -v
|
||||||
type=bool-set
|
type=bool-set
|
||||||
The current {appname} version
|
The current {appname} version.
|
||||||
|
|
||||||
|
|
||||||
--dump-commands
|
--dump-commands
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Output commands received from child process to stdout
|
Output commands received from child process to STDOUT.
|
||||||
|
|
||||||
|
|
||||||
--replay-commands
|
--replay-commands
|
||||||
Replay previously dumped commands. Specify the path to a dump file previously created by :option:`kitty --dump-commands`. You
|
Replay previously dumped commands. Specify the path to a dump file previously
|
||||||
|
created by :option:`{appname} --dump-commands`. You
|
||||||
can open a new kitty window to replay the commands with::
|
can open a new kitty window to replay the commands with::
|
||||||
|
|
||||||
kitty sh -c "kitty --replay-commands /path/to/dump/file; read"
|
{appname} sh -c "{appname} --replay-commands /path/to/dump/file; read"
|
||||||
|
|
||||||
|
|
||||||
--dump-bytes
|
--dump-bytes
|
||||||
Path to file in which to store the raw bytes received from the child process
|
Path to file in which to store the raw bytes received from the child process.
|
||||||
|
|
||||||
|
|
||||||
--debug-rendering --debug-gl
|
--debug-rendering --debug-gl
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Debug rendering commands. This will cause all OpenGL calls to check for errors
|
Debug rendering commands. This will cause all OpenGL calls to check for errors
|
||||||
instead of ignoring them. Also prints out miscellaneous debug information.
|
instead of ignoring them. Also prints out miscellaneous debug information.
|
||||||
Useful when debugging rendering problems
|
Useful when debugging rendering problems.
|
||||||
|
|
||||||
|
|
||||||
--debug-input --debug-keyboard
|
--debug-input --debug-keyboard
|
||||||
dest=debug_keyboard
|
dest=debug_keyboard
|
||||||
type=bool-set
|
type=bool-set
|
||||||
This option will cause kitty to print out key and mouse events as they are received
|
Print out key and mouse events as they are received.
|
||||||
|
|
||||||
|
|
||||||
--debug-font-fallback
|
--debug-font-fallback
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Print out information about the selection of fallback fonts for characters not present in the main font.
|
Print out information about the selection of fallback fonts for characters not
|
||||||
|
present in the main font.
|
||||||
|
|
||||||
|
|
||||||
--watcher
|
--watcher
|
||||||
This option is deprecated in favor of the :opt:`watcher` option in kitty.conf and should not be used.
|
This option is deprecated in favor of the :opt:`watcher` option in
|
||||||
|
:file:`{conf_name}.conf` and should not be used.
|
||||||
|
|
||||||
|
|
||||||
--execute -e
|
--execute -e
|
||||||
@@ -720,7 +739,8 @@ type=bool-set
|
|||||||
!
|
!
|
||||||
'''
|
'''
|
||||||
setattr(options_spec, 'ans', OPTIONS.format(
|
setattr(options_spec, 'ans', OPTIONS.format(
|
||||||
appname=appname, config_help=CONFIG_HELP.format(appname=appname, conf_name=appname),
|
appname=appname, conf_name=appname,
|
||||||
|
config_help=CONFIG_HELP.format(appname=appname, conf_name=appname),
|
||||||
))
|
))
|
||||||
ans: str = getattr(options_spec, 'ans')
|
ans: str = getattr(options_spec, 'ans')
|
||||||
return ans
|
return ans
|
||||||
@@ -764,12 +784,16 @@ def parse_args(
|
|||||||
return ans, parse_cmdline(oc, disabled, ans, args=args)
|
return ans, parse_cmdline(oc, disabled, ans, args=args)
|
||||||
|
|
||||||
|
|
||||||
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
SYSTEM_CONF = f'/etc/xdg/{appname}/{appname}.conf'
|
||||||
|
|
||||||
|
|
||||||
|
def default_config_paths(conf_paths: Sequence[str]) -> Tuple[str, ...]:
|
||||||
|
return tuple(resolve_config(SYSTEM_CONF, defconf, conf_paths))
|
||||||
|
|
||||||
|
|
||||||
def create_opts(args: CLIOptions, accumulate_bad_lines: Optional[List[BadLineType]] = None) -> KittyOpts:
|
def create_opts(args: CLIOptions, accumulate_bad_lines: Optional[List[BadLineType]] = None) -> KittyOpts:
|
||||||
from .config import load_config
|
from .config import load_config
|
||||||
config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config))
|
config = default_config_paths(args.config)
|
||||||
# Does not cover the case where `name =` when `=` is the value.
|
# Does not cover the case where `name =` when `=` is the value.
|
||||||
pat = re.compile(r'^([a-zA-Z0-9_]+)[ \t]*=')
|
pat = re.compile(r'^([a-zA-Z0-9_]+)[ \t]*=')
|
||||||
overrides = (pat.sub(r'\1 ', a.lstrip()) for a in args.override or ())
|
overrides = (pat.sub(r'\1 ', a.lstrip()) for a in args.override or ())
|
||||||
@@ -779,6 +803,6 @@ def create_opts(args: CLIOptions, accumulate_bad_lines: Optional[List[BadLineTyp
|
|||||||
|
|
||||||
def create_default_opts() -> KittyOpts:
|
def create_default_opts() -> KittyOpts:
|
||||||
from .config import load_config
|
from .config import load_config
|
||||||
config = tuple(resolve_config(SYSTEM_CONF, defconf, ()))
|
config = default_config_paths(())
|
||||||
opts = load_config(*config)
|
opts = load_config(*config)
|
||||||
return opts
|
return opts
|
||||||
|
|||||||
@@ -235,11 +235,7 @@ PENDING(clear_terminal_and_scrollback, CLEAR_TERMINAL_AND_SCROLLBACK)
|
|||||||
PENDING(reload_config, RELOAD_CONFIG)
|
PENDING(reload_config, RELOAD_CONFIG)
|
||||||
PENDING(toggle_macos_secure_keyboard_entry, TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY)
|
PENDING(toggle_macos_secure_keyboard_entry, TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY)
|
||||||
PENDING(toggle_fullscreen, TOGGLE_FULLSCREEN)
|
PENDING(toggle_fullscreen, TOGGLE_FULLSCREEN)
|
||||||
|
PENDING(open_kitty_website, OPEN_KITTY_WEBSITE)
|
||||||
- (void)open_kitty_website_url:(id)sender {
|
|
||||||
(void)sender;
|
|
||||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://sw.kovidgoyal.net/kitty/"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)validateMenuItem:(NSMenuItem *)item {
|
- (BOOL)validateMenuItem:(NSMenuItem *)item {
|
||||||
if (item.action == @selector(toggle_macos_secure_keyboard_entry:)) {
|
if (item.action == @selector(toggle_macos_secure_keyboard_entry:)) {
|
||||||
@@ -275,7 +271,7 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
GlobalShortcut new_os_window, close_os_window, close_tab, edit_config_file, reload_config;
|
GlobalShortcut new_os_window, close_os_window, close_tab, edit_config_file, reload_config;
|
||||||
GlobalShortcut previous_tab, next_tab, new_tab, new_window, close_window, reset_terminal, clear_terminal_and_scrollback;
|
GlobalShortcut previous_tab, next_tab, new_tab, new_window, close_window, reset_terminal, clear_terminal_and_scrollback;
|
||||||
GlobalShortcut toggle_macos_secure_keyboard_entry, toggle_fullscreen;
|
GlobalShortcut toggle_macos_secure_keyboard_entry, toggle_fullscreen, open_kitty_website;
|
||||||
} GlobalShortcuts;
|
} GlobalShortcuts;
|
||||||
static GlobalShortcuts global_shortcuts;
|
static GlobalShortcuts global_shortcuts;
|
||||||
|
|
||||||
@@ -290,7 +286,7 @@ cocoa_set_global_shortcut(PyObject *self UNUSED, PyObject *args) {
|
|||||||
Q(new_os_window); else Q(close_os_window); else Q(close_tab); else Q(edit_config_file);
|
Q(new_os_window); else Q(close_os_window); else Q(close_tab); else Q(edit_config_file);
|
||||||
else Q(new_tab); else Q(next_tab); else Q(previous_tab);
|
else Q(new_tab); else Q(next_tab); else Q(previous_tab);
|
||||||
else Q(new_window); else Q(close_window); else Q(reset_terminal); else Q(clear_terminal_and_scrollback); else Q(reload_config);
|
else Q(new_window); else Q(close_window); else Q(reset_terminal); else Q(clear_terminal_and_scrollback); else Q(reload_config);
|
||||||
else Q(toggle_macos_secure_keyboard_entry); else Q(toggle_fullscreen);
|
else Q(toggle_macos_secure_keyboard_entry); else Q(toggle_fullscreen); else Q(open_kitty_website);
|
||||||
#undef Q
|
#undef Q
|
||||||
if (gs == NULL) { PyErr_SetString(PyExc_KeyError, "Unknown shortcut name"); return NULL; }
|
if (gs == NULL) { PyErr_SetString(PyExc_KeyError, "Unknown shortcut name"); return NULL; }
|
||||||
int cocoa_mods;
|
int cocoa_mods;
|
||||||
@@ -617,7 +613,7 @@ cocoa_create_global_menu(void) {
|
|||||||
[shellMenu release];
|
[shellMenu release];
|
||||||
|
|
||||||
NSMenuItem* windowMenuItem =
|
NSMenuItem* windowMenuItem =
|
||||||
[bar addItemWithTitle:@""
|
[bar addItemWithTitle:@"Window"
|
||||||
action:NULL
|
action:NULL
|
||||||
keyEquivalent:@""];
|
keyEquivalent:@""];
|
||||||
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||||
@@ -652,10 +648,8 @@ cocoa_create_global_menu(void) {
|
|||||||
keyEquivalent:@""];
|
keyEquivalent:@""];
|
||||||
NSMenu* helpMenu = [[NSMenu alloc] initWithTitle:@"Help"];
|
NSMenu* helpMenu = [[NSMenu alloc] initWithTitle:@"Help"];
|
||||||
[helpMenuItem setSubmenu:helpMenu];
|
[helpMenuItem setSubmenu:helpMenu];
|
||||||
[[helpMenu addItemWithTitle:[NSString stringWithFormat:@"Visit %@ Website", app_name]
|
|
||||||
action:@selector(open_kitty_website_url:)
|
MENU_ITEM(helpMenu, @"Visit kitty Website", open_kitty_website);
|
||||||
keyEquivalent:@"?"]
|
|
||||||
setTarget:global_menu_target];
|
|
||||||
[NSApp setHelpMenu:helpMenu];
|
[NSApp setHelpMenu:helpMenu];
|
||||||
[helpMenu release];
|
[helpMenu release];
|
||||||
|
|
||||||
|
|||||||
@@ -505,6 +505,16 @@ def complete_remote_command(ans: Completions, cmd_name: str, words: Sequence[str
|
|||||||
complete_alias_map(ans, words, new_word, alias_map, complete_args=args_completer)
|
complete_alias_map(ans, words, new_word, alias_map, complete_args=args_completer)
|
||||||
|
|
||||||
|
|
||||||
|
def complete_launch_wrapper(ans: Completions, words: Sequence[str], new_word: bool, allow_files: bool = True) -> None:
|
||||||
|
from kitty.launch import clone_safe_opts
|
||||||
|
aliases, alias_map = options_for_cmd('launch')
|
||||||
|
alias_map = {k: v for k, v in alias_map.items() if v['dest'] in clone_safe_opts()}
|
||||||
|
args_completer: Optional[CompleteArgsFunc] = None
|
||||||
|
if allow_files:
|
||||||
|
args_completer = remote_files_completer('Files', ('*',))
|
||||||
|
complete_alias_map(ans, words, new_word, alias_map, complete_args=args_completer)
|
||||||
|
|
||||||
|
|
||||||
def path_completion(prefix: str = '') -> Tuple[List[str], List[str]]:
|
def path_completion(prefix: str = '') -> Tuple[List[str], List[str]]:
|
||||||
prefix = prefix.replace(r'\ ', ' ')
|
prefix = prefix.replace(r'\ ', ' ')
|
||||||
dirs, files = [], []
|
dirs, files = [], []
|
||||||
@@ -664,7 +674,12 @@ def complete_kitten(ans: Completions, kitten: str, words: Sequence[str], new_wor
|
|||||||
|
|
||||||
def find_completions(words: Sequence[str], new_word: bool, entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> Completions:
|
def find_completions(words: Sequence[str], new_word: bool, entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> Completions:
|
||||||
ans = Completions()
|
ans = Completions()
|
||||||
if not words or words[0] != 'kitty':
|
if not words:
|
||||||
|
return ans
|
||||||
|
if words[0] in ('edit-in-kitty', 'clone-in-kitty'):
|
||||||
|
complete_launch_wrapper(ans, words[1:], new_word, allow_files=words[0] != 'clone-in-kitty')
|
||||||
|
return ans
|
||||||
|
if words[0] != 'kitty':
|
||||||
return ans
|
return ans
|
||||||
words = words[1:]
|
words = words[1:]
|
||||||
if not words or (len(words) == 1 and not new_word):
|
if not words or (len(words) == 1 and not new_word):
|
||||||
|
|||||||
@@ -125,7 +125,11 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]:
|
|||||||
if func.__module__ != 'builtins':
|
if func.__module__ != 'builtins':
|
||||||
tc_imports.add((func.__module__, func.__name__))
|
tc_imports.add((func.__module__, func.__name__))
|
||||||
|
|
||||||
defval = repr(func(option.defval_as_string))
|
defval_as_obj = func(option.defval_as_string)
|
||||||
|
if isinstance(defval_as_obj, frozenset):
|
||||||
|
defval = 'frozenset({' + ', '.join(repr(x) for x in sorted(defval_as_obj)) + '})'
|
||||||
|
else:
|
||||||
|
defval = repr(defval_as_obj)
|
||||||
if len(defval) > 100:
|
if len(defval) > 100:
|
||||||
defval += ' # noqa'
|
defval += ' # noqa'
|
||||||
if option.macos_defval is not unset:
|
if option.macos_defval is not unset:
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ def ref_map() -> Dict[str, str]:
|
|||||||
'functional': f'{website_url("keyboard-protocol")}#functional-key-definitions',
|
'functional': f'{website_url("keyboard-protocol")}#functional-key-definitions',
|
||||||
'ssh_copy_command': f'{website_url("kittens/ssh")}#ssh-copy-command',
|
'ssh_copy_command': f'{website_url("kittens/ssh")}#ssh-copy-command',
|
||||||
'shell_integration': website_url("shell-integration"),
|
'shell_integration': website_url("shell-integration"),
|
||||||
|
'clone_shell': f'{website_url("shell-integration")}#clone-shell',
|
||||||
'github_discussions': 'https://github.com/kovidgoyal/kitty/discussions',
|
'github_discussions': 'https://github.com/kovidgoyal/kitty/discussions',
|
||||||
}
|
}
|
||||||
for actions in get_all_actions().values():
|
for actions in get_all_actions().values():
|
||||||
@@ -65,23 +66,40 @@ def ref_map() -> Dict[str, str]:
|
|||||||
|
|
||||||
def remove_markup(text: str) -> str:
|
def remove_markup(text: str) -> str:
|
||||||
|
|
||||||
|
def extract(m: 'Match[str]') -> Tuple[str, str]:
|
||||||
|
parts = m.group(2).split('<')
|
||||||
|
t = parts[0].strip()
|
||||||
|
q = parts[-1].rstrip('>')
|
||||||
|
return t, q
|
||||||
|
|
||||||
def sub(m: 'Match[str]') -> str:
|
def sub(m: 'Match[str]') -> str:
|
||||||
if m.group(1) == 'ref':
|
if m.group(1) == 'ref':
|
||||||
q = m.group(2).split('<')[-1].rstrip('>')
|
t, q = extract(m)
|
||||||
return ref_map()[q]
|
return f'{t} <{ref_map()[q]}>'
|
||||||
|
if m.group(1) == 'doc':
|
||||||
|
t, q = extract(m)
|
||||||
|
return f'{t} <{website_url(q.lstrip("/"))}>'
|
||||||
if m.group(1) == 'ac':
|
if m.group(1) == 'ac':
|
||||||
q = m.group(2).split('<')[-1].rstrip('>')
|
q = m.group(2).split('<')[-1].rstrip('>')
|
||||||
return ref_map()[f'action-{q}']
|
return q
|
||||||
|
if m.group(1) == 'term':
|
||||||
|
t, _ = extract(m)
|
||||||
|
return t
|
||||||
|
if m.group(1) == 'option':
|
||||||
|
t, _ = extract(m)
|
||||||
|
return t
|
||||||
if m.group(1) == 'disc':
|
if m.group(1) == 'disc':
|
||||||
parts = m.group(2).split('<')
|
t, q = extract(m)
|
||||||
t = parts[0].strip()
|
|
||||||
q = parts[-1].rstrip('>')
|
|
||||||
return f'{t} {ref_map()["github_discussions"]}/{q}'
|
return f'{t} {ref_map()["github_discussions"]}/{q}'
|
||||||
return str(m.group(2))
|
return str(m.group(2))
|
||||||
|
|
||||||
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)
|
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
|
def strip_inline_literal(text: str) -> str:
|
||||||
|
return re.sub(r'``([^`]+)``', r'`\1`', text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
def iter_blocks(lines: Iterable[str]) -> Iterator[Tuple[List[str], int]]:
|
def iter_blocks(lines: Iterable[str]) -> Iterator[Tuple[List[str], int]]:
|
||||||
current_block: List[str] = []
|
current_block: List[str] = []
|
||||||
prev_indent = 0
|
prev_indent = 0
|
||||||
@@ -123,6 +141,7 @@ def wrapped_block(lines: Iterable[str], comment_symbol: str = '#: ') -> Iterator
|
|||||||
|
|
||||||
def render_block(text: str, comment_symbol: str = '#: ') -> str:
|
def render_block(text: str, comment_symbol: str = '#: ') -> str:
|
||||||
text = remove_markup(text)
|
text = remove_markup(text)
|
||||||
|
text = strip_inline_literal(text)
|
||||||
lines = text.splitlines()
|
lines = text.splitlines()
|
||||||
return '\n'.join(wrapped_block(lines, comment_symbol))
|
return '\n'.join(wrapped_block(lines, comment_symbol))
|
||||||
|
|
||||||
@@ -270,7 +289,11 @@ class MultiOption:
|
|||||||
for k in self.items:
|
for k in self.items:
|
||||||
if k.documented:
|
if k.documented:
|
||||||
documented = True
|
documented = True
|
||||||
a(f'{self.name} {k.defval_as_str if k.add_to_default else ""}'.rstrip())
|
if k.add_to_default:
|
||||||
|
a(f'{self.name} {k.defval_as_str}'.rstrip())
|
||||||
|
else:
|
||||||
|
# Comment out multi-options that have no default values
|
||||||
|
a(f'# {self.name}'.rstrip())
|
||||||
if not k.add_to_default and k.defval_as_str:
|
if not k.add_to_default and k.defval_as_str:
|
||||||
a('')
|
a('')
|
||||||
a(f'#: E.g. {self.name} {k.defval_as_str}'.rstrip())
|
a(f'#: E.g. {self.name} {k.defval_as_str}'.rstrip())
|
||||||
@@ -546,6 +569,9 @@ class Group:
|
|||||||
|
|
||||||
if commented:
|
if commented:
|
||||||
ans = [x if x.startswith('#') or not x.strip() else (f'# {x}') for x in ans]
|
ans = [x if x.startswith('#') or not x.strip() else (f'# {x}') for x in ans]
|
||||||
|
else:
|
||||||
|
# Comment out any invalid options that have no value
|
||||||
|
ans = [f'# {x}' if not x.startswith('#') and len(x.strip().split()) == 1 else x for x in ans]
|
||||||
|
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ class Version(NamedTuple):
|
|||||||
|
|
||||||
appname: str = 'kitty'
|
appname: str = 'kitty'
|
||||||
kitty_face = '🐱'
|
kitty_face = '🐱'
|
||||||
version: Version = Version(0, 25, 0)
|
version: Version = Version(0, 25, 2)
|
||||||
str_version: str = '.'.join(map(str, version))
|
str_version: str = '.'.join(map(str, version))
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
is_macos: bool = 'darwin' in _plat
|
is_macos: bool = 'darwin' in _plat
|
||||||
|
is_freebsd: bool = 'freebsd' in _plat
|
||||||
is_running_from_develop: bool = False
|
is_running_from_develop: bool = False
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
extensions_dir: str = getattr(sys, 'kitty_extensions_dir')
|
extensions_dir: str = getattr(sys, 'kitty_extensions_dir')
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#define PY_SSIZE_T_CLEAN
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <assert.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
@@ -163,12 +164,14 @@ typedef struct {
|
|||||||
sprite_index sprite_x, sprite_y, sprite_z;
|
sprite_index sprite_x, sprite_y, sprite_z;
|
||||||
CellAttrs attrs;
|
CellAttrs attrs;
|
||||||
} GPUCell;
|
} GPUCell;
|
||||||
|
static_assert(sizeof(GPUCell) == 20, "Fix the ordering of GPUCell");
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char_type ch;
|
char_type ch;
|
||||||
combining_type cc_idx[2];
|
|
||||||
hyperlink_id_type hyperlink_id;
|
hyperlink_id_type hyperlink_id;
|
||||||
|
combining_type cc_idx[3];
|
||||||
} CPUCell;
|
} CPUCell;
|
||||||
|
static_assert(sizeof(CPUCell) == 12, "Fix the ordering of CPUCell");
|
||||||
|
|
||||||
typedef enum { UNKNOWN_PROMPT_KIND = 0, PROMPT_START = 1, SECONDARY_PROMPT = 2, OUTPUT_START = 3 } PromptKind;
|
typedef enum { UNKNOWN_PROMPT_KIND = 0, PROMPT_START = 1, SECONDARY_PROMPT = 2, OUTPUT_START = 3 } PromptKind;
|
||||||
typedef union LineAttrs {
|
typedef union LineAttrs {
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ ensure_state(DiskCache *self) {
|
|||||||
int ret;
|
int ret;
|
||||||
if (self->fully_initialized) return true;
|
if (self->fully_initialized) return true;
|
||||||
if (!self->loop_data_inited) {
|
if (!self->loop_data_inited) {
|
||||||
if (!init_loop_data(&self->loop_data)) { PyErr_SetFromErrno(PyExc_OSError); return false; }
|
if (!init_loop_data(&self->loop_data, 0)) { PyErr_SetFromErrno(PyExc_OSError); return false; }
|
||||||
self->loop_data_inited = true;
|
self->loop_data_inited = true;
|
||||||
}
|
}
|
||||||
if (!self->currently_writing.hash_key) {
|
if (!self->currently_writing.hash_key) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user