Compare commits

...

37 Commits

Author SHA1 Message Date
Kovid Goyal
04807453ec version 0.24.1 2022-01-06 10:40:59 +05:30
Kovid Goyal
1b7582e85c Merge branch 'patch-1' of https://github.com/Silent-Crafter/kitty 2022-01-06 10:03:45 +05:30
Silent Crafter
e106562ef1 Bash: for loop array iteration fix
the array iteration wasn't implemented correctly for KITTY_SHELL_INTEGRATION variable. the i variable in for loop wasn't iterating through all the elements of that array
2022-01-06 09:52:59 +05:30
Kovid Goyal
d3a20f00d6 Add a comment explaining where we use PS2 marks 2022-01-06 09:25:41 +05:30
Kovid Goyal
de5443a4db Add a reference to the discussion of using custom tab bar themes to the docs 2022-01-06 09:16:22 +05:30
Kovid Goyal
e8b19e08fa Fix non-renderable combining chars causing some text to not be rendered on Linux
The test for non-renderable chars was broken and the variation selectors
were not included in the test. Fixes #4444
2022-01-05 22:33:53 +05:30
Kovid Goyal
a574081012 Merge branch 'zsh-integration' of https://github.com/romkatv/kitty 2022-01-05 21:29:30 +05:30
Roman Perepelitsa
4f06ce9d72 Once again start embedding marks in PS2 on zsh 2022-01-05 16:36:00 +01:00
Kovid Goyal
6713580455 Explicitly turn off aliases in a more functions
Not really needed, but I like to be explicit
2022-01-05 21:04:27 +05:30
Kovid Goyal
86f6c946b3 Merge branch 'zsh-integration-aliases' of https://github.com/romkatv/kitty 2022-01-05 20:49:08 +05:30
Roman Perepelitsa
9194d1db6b Disable aliases when patching functions in zsh integration 2022-01-05 16:16:18 +01:00
Kovid Goyal
420a1b018a Fix #4448 2022-01-05 20:14:23 +05:30
Roman Perepelitsa
8009b85073 Make writing to the TTY more robust in zsh integration
See https://github.com/kovidgoyal/kitty/issues/4440.
2022-01-05 09:55:19 +01:00
Kovid Goyal
2a58af2be9 Ignore all errors rendering /etc/issue 2022-01-05 14:04:56 +05:30
Roman Perepelitsa
4decac26f6 Fix a typo in _ksi_debug_print 2022-01-05 09:22:00 +01:00
Roman Perepelitsa
a6b74f190f Revert "Get _ksi_debug_print working again"
This reverts commit ff63e58f95.
2022-01-05 09:20:33 +01:00
Kovid Goyal
bfbe60c90e Update changelog 2022-01-05 09:05:20 +05:30
Kovid Goyal
384b2f8462 Add roundtrip and cursor positioning tests for all three zero width chars 2022-01-05 08:57:14 +05:30
Kovid Goyal
d875615c03 Fix a regression in the handling of some combining characters such as zero width joiners
Fixes #4439
2022-01-05 08:50:55 +05:30
Kovid Goyal
9aefcfe56f Make --hold a bit more robust 2022-01-05 08:14:22 +05:30
Kovid Goyal
266e70222f Avoid hooking LE widgets at all when using no-cursor 2022-01-05 07:46:20 +05:30
Kovid Goyal
d4639e2aea Merge branch 'zsh-integration' of https://github.com/romkatv/kitty 2022-01-05 07:40:10 +05:30
Roman Perepelitsa
ca8975891b Replace add-zle-hook-widget with a series of hacks in zsh integration 2022-01-04 19:56:57 +01:00
Kovid Goyal
bed3d2eb50 Fix #4434 2022-01-04 23:38:58 +05:30
Kovid Goyal
78495a7f64 spelling 2022-01-04 20:35:21 +05:30
Kovid Goyal
f66914f7bb ... 2022-01-04 20:29:53 +05:30
Kovid Goyal
58e1f6ee1f Use ctermid() for tty_name 2022-01-04 20:25:16 +05:30
Kovid Goyal
0625035202 oops forgot to re-order second tab group 2022-01-04 20:18:49 +05:30
Kovid Goyal
f8fc272f58 Make loading IssueData more robust
Fixes #4424
2022-01-04 19:57:23 +05:30
Kovid Goyal
6d61de8a8f Merge branch 'fladson/fix-documentation-typo' of https://github.com/fladson/kitty 2022-01-04 19:52:27 +05:30
Kovid Goyal
4c08709df1 Use same orders for all tab groups 2022-01-04 19:49:55 +05:30
Fladson Gomes
58a28da94d Fix documentation typo 2022-01-04 14:46:25 +01:00
Kovid Goyal
26b976d17d Merge branch 'fix-mouse-scroll' of https://github.com/page-down/kitty 2022-01-04 18:01:30 +05:30
pagedown
3141fc3f05 Update mouse cursor position when scrolling without OS window focus 2022-01-04 19:39:36 +08:00
Kovid Goyal
a45d6c8b55 Fix #4421 2022-01-04 16:21:00 +05:30
Kovid Goyal
dadf496a68 remove leftover debug print 2022-01-04 12:01:23 +05:30
Kovid Goyal
dae8ae33f0 hints kitten: Fix matching of filenames enclosed in quotes or brackets not stripping the surrounding quotes properly. Fixes #4419 2022-01-04 10:58:31 +05:30
21 changed files with 668 additions and 504 deletions

View File

@@ -31,14 +31,26 @@ def runpy(args: List[str]) -> None:
def hold(args: List[str]) -> None: def hold(args: List[str]) -> None:
import subprocess import subprocess
import tty import termios
from contextlib import suppress from contextlib import suppress
from kittens.tui.operations import init_state, set_cursor_visible
ret = subprocess.Popen(args[1:]).wait() ret = subprocess.Popen(args[1:]).wait()
with suppress(BaseException): with suppress(BaseException):
print('\n\x1b[1;32mPress any key to exit', end='', flush=True) print(
'\n\x1b[1;32mPress Enter to exit',
end=init_state(alternate_screen=False, kitty_keyboard_mode=False) + set_cursor_visible(False),
flush=True)
with suppress(BaseException): with suppress(BaseException):
tty.setraw(sys.stdin.fileno()) fd = sys.stdin.fileno()
sys.stdin.buffer.read(1) old = termios.tcgetattr(fd)
new = old[:]
new[3] &= ~termios.ECHO # 3 == 'lflags'
tcsetattr_flags = termios.TCSAFLUSH
if hasattr(termios, 'TCSASOFT'):
tcsetattr_flags |= getattr(termios, 'TCSASOFT')
termios.tcsetattr(fd, tcsetattr_flags, new)
with suppress(KeyboardInterrupt):
input()
raise SystemExit(ret) raise SystemExit(ret)

View File

@@ -28,3 +28,9 @@ details > summary {
text-decoration-color: var(--color-link-underline); text-decoration-color: var(--color-link-underline);
text-decoration-line: underline; text-decoration-line: underline;
} }
/* pygments adds an underline to some white-space, this is particularly visible in
* dark mode. Remove it. */
.highlight .w {
text-decoration: none
}

View File

@@ -73,6 +73,23 @@ command.
Detailed list of changes Detailed list of changes
------------------------------------- -------------------------------------
0.24.1 [2022-01-06]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Shell integration: Work around conflicts with some zsh plugins (:iss:`4428`)
- Have the zero width space and various other characters from the *Other,
formatting* Unicode category be treated as combining characters (:iss:`4439`)
- Fix using ``--shell-integration`` with :file:`setup.py` broken (:iss:`4434`)
- Fix showing debug information not working if kitty's :file:`STDIN` is not a tty
(:iss:`4424`)
- Linux: Fix a regression that broke rendering of emoji with variation selectors
(:iss:`4444`)
0.24.0 [2022-01-04] 0.24.0 [2022-01-04]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -129,11 +129,6 @@ different shells.
and sources the original :file:`.zshenv`. It then loads the shell integration code. and sources the original :file:`.zshenv`. It then loads the shell integration code.
The remainder of zsh's startup process proceeds as normal. The remainder of zsh's startup process proceeds as normal.
.. tab:: bash
For bash, kitty adds a couple of lines to the bottom of :file:`~/.bashrc`
(in an atomic manner) to load the shell integration code.
.. 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
@@ -142,6 +137,12 @@ different shells.
process and will be cleaned up by the integration script after startup. No files process and will be cleaned up by the integration script after startup. No files
are added or modified. are added or modified.
.. tab:: bash
For bash, kitty adds a couple of lines to the bottom of :file:`~/.bashrc`
(in an atomic manner) to load the shell integration code.
Then, when launching the shell, kitty sets the environment variable Then, when launching the shell, kitty sets the environment variable
:envvar:`KITTY_SHELL_INTEGRATION` to the value of the :opt:`shell_integration` :envvar:`KITTY_SHELL_INTEGRATION` to the value of the :opt:`shell_integration`
option. The shell integration code reads the environment variable, turns on the option. The shell integration code reads the environment variable, turns on the
@@ -197,15 +198,6 @@ First, in :file:`kitty.conf` set:
Then in your shell's rc file, add the lines: Then in your shell's rc file, add the lines:
.. tab:: bash
.. code-block:: sh
if test -n "$KITTY_INSTALLATION_DIR"; then
export KITTY_SHELL_INTEGRATION="enabled"
source "$KITTY_INSTALLATION_DIR/shell-integration/bash/kitty.bash"
fi
.. tab:: zsh .. tab:: zsh
.. code-block:: sh .. code-block:: sh
@@ -228,6 +220,15 @@ Then in your shell's rc file, add the lines:
end end
.. tab:: bash
.. code-block:: sh
if test -n "$KITTY_INSTALLATION_DIR"; then
export KITTY_SHELL_INTEGRATION="enabled"
source "$KITTY_INSTALLATION_DIR/shell-integration/bash/kitty.bash"
fi
The value of :envvar:`KITTY_SHELL_INTEGRATION` is the same as that for The value of :envvar:`KITTY_SHELL_INTEGRATION` is the same as that for
:opt:`shell_integration`, except if you want to disable shell integration :opt:`shell_integration`, except if you want to disable shell integration
completely, in which case simply do not set the completely, in which case simply do not set the

View File

@@ -49,11 +49,10 @@ class_maps: Dict[str, Set[int]] = {}
all_symbols: Set[int] = set() all_symbols: Set[int] = set()
name_map: Dict[int, str] = {} name_map: Dict[int, str] = {}
word_search_map: DefaultDict[str, Set[int]] = defaultdict(set) word_search_map: DefaultDict[str, Set[int]] = defaultdict(set)
zwj = 0x200d
soft_hyphen = 0xad soft_hyphen = 0xad
flag_codepoints = frozenset(range(0x1F1E6, 0x1F1E6 + 26)) flag_codepoints = frozenset(range(0x1F1E6, 0x1F1E6 + 26))
# See https://github.com/harfbuzz/harfbuzz/issues/169 # See https://github.com/harfbuzz/harfbuzz/issues/169
marks = set(emoji_skin_tone_modifiers) | {zwj} | flag_codepoints marks = set(emoji_skin_tone_modifiers) | flag_codepoints
not_assigned = set(range(0, sys.maxunicode)) not_assigned = set(range(0, sys.maxunicode))
property_maps: Dict[str, Set[int]] = defaultdict(set) property_maps: Dict[str, Set[int]] = defaultdict(set)
@@ -69,7 +68,6 @@ def parse_prop_list() -> None:
property_maps[name] |= chars property_maps[name] |= chars
# see https://www.unicode.org/faq/unsup_char.html#3 # see https://www.unicode.org/faq/unsup_char.html#3
marks |= property_maps['Other_Default_Ignorable_Code_Point'] marks |= property_maps['Other_Default_Ignorable_Code_Point']
marks.add(soft_hyphen)
def parse_ucd() -> None: def parse_ucd() -> None:
@@ -113,6 +111,12 @@ def parse_ucd() -> None:
marks.add(codepoint) marks.add(codepoint)
elif category.startswith('S'): elif category.startswith('S'):
all_symbols.add(codepoint) all_symbols.add(codepoint)
elif category == 'Cf':
# we add Cf to marks as it contains things like tags and zero
# width chars. Not sure if *all* of Cf should be treated as
# combining chars, might need to add individual exceptions in
# the future.
marks.add(codepoint)
with open('nerd-fonts-glyphs.txt') as f: with open('nerd-fonts-glyphs.txt') as f:
for line in f: for line in f:
@@ -382,9 +386,9 @@ def gen_ucd() -> None:
ascii_range='false' ascii_range='false'
) )
category_test( category_test(
'is_non_rendered_char', p, 'Cc Cs'.split(), 'is_non_rendered_char', p, 'Cc Cs Cf'.split(),
'Other_Default_Ignorable_Code_Point and soft hyphen', 'Other_Default_Ignorable_Code_Point and soft hyphen',
extra_chars=property_maps['Other_Default_Ignorable_Code_Point'] | {soft_hyphen}, extra_chars=property_maps['Other_Default_Ignorable_Code_Point'] | set(range(0xfe00, 0xfe0f + 1)),
ascii_range='false' ascii_range='false'
) )
category_test('is_word_char', p, {c for c in class_maps if c[0] in 'LN'}, 'L and N categories') category_test('is_word_char', p, {c for c in class_maps if c[0] in 'LN'}, 'L and N categories')

View File

@@ -271,9 +271,13 @@ def brackets(text: str, s: int, e: int) -> Tuple[int, int]:
# Remove matching brackets # Remove matching brackets
if s < e <= len(text): if s < e <= len(text):
before = text[s] before = text[s]
if before in '({[<' and text[e-1] == closing_bracket_map[before]: if before in '({[<':
s += 1 q = closing_bracket_map[before]
e -= 1 if text[e-1] == q:
s += 1
e -= 1
elif text[e:e+1] == q:
s += 1
return s, e return s, e
@@ -282,9 +286,12 @@ def quotes(text: str, s: int, e: int) -> Tuple[int, int]:
# Remove matching quotes # Remove matching quotes
if s < e <= len(text): if s < e <= len(text):
before = text[s] before = text[s]
if before in '\'"' and text[e-1] == before: if before in '\'"':
s += 1 if text[e-1] == before:
e -= 1 s += 1
e -= 1
elif text[e:e+1] == before:
s += 1
return s, e return s, e

View File

@@ -291,7 +291,7 @@ class MouseTracking(Enum):
full = auto() full = auto()
def init_state(alternate_screen: bool = True, mouse_tracking: MouseTracking = MouseTracking.none) -> str: def init_state(alternate_screen: bool = True, mouse_tracking: MouseTracking = MouseTracking.none, kitty_keyboard_mode: bool = True) -> str:
sc = SAVE_CURSOR if alternate_screen else '' sc = SAVE_CURSOR if alternate_screen else ''
ans = ( ans = (
S7C1T + sc + SAVE_PRIVATE_MODE_VALUES + reset_mode(Mode.LNM) + S7C1T + sc + SAVE_PRIVATE_MODE_VALUES + reset_mode(Mode.LNM) +
@@ -314,7 +314,10 @@ def init_state(alternate_screen: bool = True, mouse_tracking: MouseTracking = Mo
ans += set_mode(Mode.MOUSE_MOTION_TRACKING) ans += set_mode(Mode.MOUSE_MOTION_TRACKING)
elif mouse_tracking is MouseTracking.full: elif mouse_tracking is MouseTracking.full:
ans += set_mode(Mode.MOUSE_MOVE_TRACKING) ans += set_mode(Mode.MOUSE_MOVE_TRACKING)
ans += '\033[>31u' # extended keyboard mode if kitty_keyboard_mode:
ans += '\033[>31u' # extended keyboard mode
else:
ans += '\033[>u' # legacy keyboard mode
return ans return ans

View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2021-10-07 // unicode data, built from the unicode standard on: 2022-01-05
// see gen-wcwidth.py // see gen-wcwidth.py
#pragma once #pragma once
#include "data-types.h" #include "data-types.h"

View File

@@ -22,7 +22,7 @@ class Version(NamedTuple):
appname: str = 'kitty' appname: str = 'kitty'
kitty_face = '🐱' kitty_face = '🐱'
version: Version = Version(0, 24, 0) version: Version = Version(0, 24, 1)
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

View File

@@ -7,11 +7,10 @@ import socket
import sys import sys
import termios import termios
import time import time
from contextlib import suppress
from functools import partial from functools import partial
from pprint import pformat from pprint import pformat
from typing import ( from typing import IO, Callable, Dict, Iterable, Iterator, Optional, Set, Tuple
IO, Callable, Dict, Generator, Iterable, Iterator, Optional, Set, Tuple
)
from kittens.tui.operations import colored, styled from kittens.tui.operations import colored, styled
@@ -19,7 +18,7 @@ from .cli import version
from .constants import ( from .constants import (
extensions_dir, is_macos, is_wayland, kitty_base_dir, kitty_exe, shell_path extensions_dir, is_macos, is_wayland, kitty_base_dir, kitty_exe, shell_path
) )
from .fast_data_types import num_users, Color from .fast_data_types import Color, num_users
from .options.types import Options as KittyOpts, defaults from .options.types import Options as KittyOpts, defaults
from .options.utils import MouseMap from .options.utils import MouseMap
from .rgb import color_as_sharp from .rgb import color_as_sharp
@@ -41,7 +40,7 @@ def title(x: str) -> str:
return colored(x, 'blue', intense=True) return colored(x, 'blue', intense=True)
def mod_to_names(mods: int) -> Generator[str, None, None]: def mod_to_names(mods: int) -> Iterator[str]:
from .fast_data_types import ( from .fast_data_types import (
GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER, GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER,
GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER
@@ -162,16 +161,22 @@ class IssueData:
def __init__(self) -> None: def __init__(self) -> None:
self.uname = os.uname() self.uname = os.uname()
self.s, self.n, self.r, self.v, self.m = self.uname self.s, self.n, self.r, self.v, self.m = self.uname
self.hostname = self.o = socket.gethostname() try:
self.hostname = self.o = socket.gethostname()
except Exception:
self.hostname = self.o = 'localhost'
_time = time.localtime() _time = time.localtime()
self.formatted_time = self.d = time.strftime('%a %b %d %Y', _time) self.formatted_time = self.d = time.strftime('%a %b %d %Y', _time)
self.formatted_date = self.t = time.strftime('%H:%M:%S', _time) self.formatted_date = self.t = time.strftime('%H:%M:%S', _time)
try: try:
self.tty_name = format_tty_name(os.ttyname(sys.stdin.fileno())) self.tty_name = format_tty_name(os.ctermid())
except OSError: except OSError:
self.tty_name = '(none)' self.tty_name = '(none)'
self.l = self.tty_name # noqa self.l = self.tty_name # noqa
self.baud_rate = termios.tcgetattr(sys.stdin.fileno())[5] self.baud_rate = 0
if sys.stdin.isatty():
with suppress(OSError):
self.baud_rate = termios.tcgetattr(sys.stdin.fileno())[5]
self.b = str(self.baud_rate) self.b = str(self.baud_rate)
try: try:
self.num_users = num_users() self.num_users = num_users()
@@ -218,8 +223,18 @@ def debug_config(opts: KittyOpts) -> str:
import subprocess import subprocess
p(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip()) p(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip())
if os.path.exists('/etc/issue'): if os.path.exists('/etc/issue'):
with open('/etc/issue', encoding='utf-8', errors='replace') as f: try:
p(end=''.join(IssueData().parse_issue_file(f))) idata = IssueData()
except Exception:
pass
else:
with open('/etc/issue', encoding='utf-8', errors='replace') as f:
try:
datums = idata.parse_issue_file(f)
except Exception:
pass
else:
p(end=''.join(datums))
if os.path.exists('/etc/lsb-release'): if os.path.exists('/etc/lsb-release'):
with open('/etc/lsb-release', encoding='utf-8', errors='replace') as f: with open('/etc/lsb-release', encoding='utf-8', errors='replace') as f:
p(f.read().strip()) p(f.read().strip())

26
kitty/emoji.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2021-10-07 // unicode data, built from the unicode standard on: 2022-01-05
// see gen-wcwidth.py // see gen-wcwidth.py
#pragma once #pragma once
#include "data-types.h" #include "data-types.h"
@@ -276,7 +276,7 @@ is_emoji(char_type code) {
return true; return true;
case 0x1f6d5 ... 0x1f6d7: case 0x1f6d5 ... 0x1f6d7:
return true; return true;
case 0x1f6e0 ... 0x1f6e5: case 0x1f6dd ... 0x1f6e5:
return true; return true;
case 0x1f6e9: case 0x1f6e9:
return true; return true;
@@ -288,29 +288,31 @@ is_emoji(char_type code) {
return true; return true;
case 0x1f7e0 ... 0x1f7eb: case 0x1f7e0 ... 0x1f7eb:
return true; return true;
case 0x1f7f0:
return true;
case 0x1f90c ... 0x1f93a: case 0x1f90c ... 0x1f93a:
return true; return true;
case 0x1f93c ... 0x1f945: case 0x1f93c ... 0x1f945:
return true; return true;
case 0x1f947 ... 0x1f978: case 0x1f947 ... 0x1f9ff:
return true;
case 0x1f97a ... 0x1f9cb:
return true;
case 0x1f9cd ... 0x1f9ff:
return true; return true;
case 0x1fa70 ... 0x1fa74: case 0x1fa70 ... 0x1fa74:
return true; return true;
case 0x1fa78 ... 0x1fa7a: case 0x1fa78 ... 0x1fa7c:
return true; return true;
case 0x1fa80 ... 0x1fa86: case 0x1fa80 ... 0x1fa86:
return true; return true;
case 0x1fa90 ... 0x1faa8: case 0x1fa90 ... 0x1faac:
return true; return true;
case 0x1fab0 ... 0x1fab6: case 0x1fab0 ... 0x1faba:
return true; return true;
case 0x1fac0 ... 0x1fac2: case 0x1fac0 ... 0x1fac5:
return true; return true;
case 0x1fad0 ... 0x1fad6: case 0x1fad0 ... 0x1fad9:
return true;
case 0x1fae0 ... 0x1fae7:
return true;
case 0x1faf0 ... 0x1faf6:
return true; return true;
default: return false; default: return false;
} }

View File

@@ -387,7 +387,8 @@ has_cell_text(Font *self, CPUCell *cell) {
char_type combining_chars[arraysz(cell->cc_idx)]; char_type combining_chars[arraysz(cell->cc_idx)];
unsigned num_cc = 0; unsigned num_cc = 0;
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) { for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
if (!is_non_rendered_char(cell->cc_idx[i])) combining_chars[num_cc++] = codepoint_for_mark(cell->cc_idx[i]); const char_type ccp = codepoint_for_mark(cell->cc_idx[i]);
if (!is_non_rendered_char(ccp)) combining_chars[num_cc++] = ccp;
} }
if (num_cc == 0) return true; if (num_cc == 0) return true;
if (num_cc == 1) { if (num_cc == 1) {

View File

@@ -795,6 +795,17 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags, int modifiers) {
if (t) w = t->windows + t->active_window; if (t) w = t->windows + t->active_window;
} }
if (!w) return; if (!w) return;
// Also update mouse cursor position while kitty OS window is not focused.
// Allows scroll events to be delivered to the child with correct pointer co-ordinates even when
// the window is not focused on macOS
if (!osw->is_focused) {
unsigned int x = 0, y = 0;
bool in_left_half_of_cell;
if (cell_for_pos(w, &x, &y, &in_left_half_of_cell, osw)) {
w->mouse_pos.cell_x = x; w->mouse_pos.cell_y = y;
w->mouse_pos.in_left_half_of_cell = in_left_half_of_cell;
}
}
Screen *screen = w->render_data.screen; Screen *screen = w->render_data.screen;
enum MomentumData { NoMomentumData, MomentumPhaseBegan, MomentumPhaseStationary, MomentumPhaseActive, MomentumPhaseEnded, MomentumPhaseCancelled, MomentumPhaseMayBegin }; enum MomentumData { NoMomentumData, MomentumPhaseBegan, MomentumPhaseStationary, MomentumPhaseActive, MomentumPhaseEnded, MomentumPhaseCancelled, MomentumPhaseMayBegin };

View File

@@ -232,7 +232,7 @@ opt('cursor_text_color', '#111111',
Choose the color of text under the cursor. If you want it rendered with the Choose the color of text under the cursor. If you want it rendered with the
background color of the cell underneath instead, use the special keyword: background color of the cell underneath instead, use the special keyword:
background. Note that if :opt:`cursor` is set to :code:`none` then this setting background. Note that if :opt:`cursor` is set to :code:`none` then this setting
if ignored. is ignored.
''' '''
) )
@@ -987,7 +987,9 @@ The tab bar style, can be one of:
A user-supplied Python function called draw_tab is loaded from the file A user-supplied Python function called draw_tab is loaded from the file
:file:`tab_bar.py` in the kitty config directory. For examples of how to :file:`tab_bar.py` in the kitty config directory. For examples of how to
write such a function, see the functions named :code:`draw_tab_with_*` in write such a function, see the functions named :code:`draw_tab_with_*` in
kitty's source code: :file:`kitty/tab_bar.py`. kitty's source code: :file:`kitty/tab_bar.py`. See also `this discussion
<https://github.com/kovidgoyal/kitty/discussions/4447>`_ for examples from
kitty users.
:code:`hidden` :code:`hidden`
The tab bar is hidden. If you use this, you might want to create a The tab bar is hidden. If you use this, you might want to create a
mapping for the :ref:`action-select_tab` mapping for the :ref:`action-select_tab`

701
kitty/unicode-data.c generated

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
#include "data-types.h" #include "data-types.h"
#include "state.h" #include "state.h"
// START_KNOWN_MARKS // START_KNOWN_MARKS
static const combining_type VS15 = 1325, VS16 = 1326; static const combining_type VS15 = 1362, VS16 = 1363;
// END_KNOWN_MARKS // END_KNOWN_MARKS
bool is_combining_char(char_type ch); bool is_combining_char(char_type ch);

118
kitty/wcwidth-std.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2021-10-07 // unicode data, built from the unicode standard on: 2022-01-05
// see gen-wcwidth.py // see gen-wcwidth.py
#pragma once #pragma once
#include "data-types.h" #include "data-types.h"
@@ -14,7 +14,7 @@ wcwidth_std(int32_t code) {
return 2; return 2;
// }}} // }}}
// Marks (6189 codepoints) {{{ // Marks (6350 codepoints) {{{
case 0x0: case 0x0:
return 0; return 0;
case 0xad: case 0xad:
@@ -33,13 +33,17 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x5c7: case 0x5c7:
return 0; return 0;
case 0x600 ... 0x605:
return 0;
case 0x610 ... 0x61a: case 0x610 ... 0x61a:
return 0; return 0;
case 0x61c:
return 0;
case 0x64b ... 0x65f: case 0x64b ... 0x65f:
return 0; return 0;
case 0x670: case 0x670:
return 0; return 0;
case 0x6d6 ... 0x6dc: case 0x6d6 ... 0x6dd:
return 0; return 0;
case 0x6df ... 0x6e4: case 0x6df ... 0x6e4:
return 0; return 0;
@@ -47,6 +51,8 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x6ea ... 0x6ed: case 0x6ea ... 0x6ed:
return 0; return 0;
case 0x70f:
return 0;
case 0x711: case 0x711:
return 0; return 0;
case 0x730 ... 0x74a: case 0x730 ... 0x74a:
@@ -67,11 +73,11 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x859 ... 0x85b: case 0x859 ... 0x85b:
return 0; return 0;
case 0x890 ... 0x891:
return 0;
case 0x898 ... 0x89f: case 0x898 ... 0x89f:
return 0; return 0;
case 0x8ca ... 0x8e1: case 0x8ca ... 0x903:
return 0;
case 0x8e3 ... 0x903:
return 0; return 0;
case 0x93a ... 0x93c: case 0x93a ... 0x93c:
return 0; return 0;
@@ -271,9 +277,7 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x17dd: case 0x17dd:
return 0; return 0;
case 0x180b ... 0x180d: case 0x180b ... 0x180f:
return 0;
case 0x180f:
return 0; return 0;
case 0x1885 ... 0x1886: case 0x1885 ... 0x1886:
return 0; return 0;
@@ -319,9 +323,11 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x1dc0 ... 0x1dff: case 0x1dc0 ... 0x1dff:
return 0; return 0;
case 0x200d: case 0x200b ... 0x200f:
return 0; return 0;
case 0x2065: case 0x202a ... 0x202e:
return 0;
case 0x2060 ... 0x206f:
return 0; return 0;
case 0x20d0 ... 0x20f0: case 0x20d0 ... 0x20f0:
return 0; return 0;
@@ -405,9 +411,11 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0xfe20 ... 0xfe2f: case 0xfe20 ... 0xfe2f:
return 0; return 0;
case 0xfeff:
return 0;
case 0xffa0: case 0xffa0:
return 0; return 0;
case 0xfff0 ... 0xfff8: case 0xfff0 ... 0xfffb:
return 0; return 0;
case 0x101fd: case 0x101fd:
return 0; return 0;
@@ -447,8 +455,12 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x110b0 ... 0x110ba: case 0x110b0 ... 0x110ba:
return 0; return 0;
case 0x110bd:
return 0;
case 0x110c2: case 0x110c2:
return 0; return 0;
case 0x110cd:
return 0;
case 0x11100 ... 0x11102: case 0x11100 ... 0x11102:
return 0; return 0;
case 0x11127 ... 0x11134: case 0x11127 ... 0x11134:
@@ -563,6 +575,8 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x11ef3 ... 0x11ef6: case 0x11ef3 ... 0x11ef6:
return 0; return 0;
case 0x13430 ... 0x13438:
return 0;
case 0x16af0 ... 0x16af4: case 0x16af0 ... 0x16af4:
return 0; return 0;
case 0x16b30 ... 0x16b36: case 0x16b30 ... 0x16b36:
@@ -579,15 +593,15 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x1bc9d ... 0x1bc9e: case 0x1bc9d ... 0x1bc9e:
return 0; return 0;
case 0x1bca0 ... 0x1bca3:
return 0;
case 0x1cf00 ... 0x1cf2d: case 0x1cf00 ... 0x1cf2d:
return 0; return 0;
case 0x1cf30 ... 0x1cf46: case 0x1cf30 ... 0x1cf46:
return 0; return 0;
case 0x1d165 ... 0x1d169: case 0x1d165 ... 0x1d169:
return 0; return 0;
case 0x1d16d ... 0x1d172: case 0x1d16d ... 0x1d182:
return 0;
case 0x1d17b ... 0x1d182:
return 0; return 0;
case 0x1d185 ... 0x1d18b: case 0x1d185 ... 0x1d18b:
return 0; return 0;
@@ -629,63 +643,17 @@ wcwidth_std(int32_t code) {
return 0; return 0;
case 0x1f3fb ... 0x1f3ff: case 0x1f3fb ... 0x1f3ff:
return 0; return 0;
case 0xe0000: case 0xe0000 ... 0xe0fff:
return 0;
case 0xe0002 ... 0xe001f:
return 0;
case 0xe0080 ... 0xe0fff:
return 0; return 0;
// }}} // }}}
// Non-printing characters (2273 codepoints) {{{ // Non-printing characters (2112 codepoints) {{{
case 0x1 ... 0x1f: case 0x1 ... 0x1f:
return -1; return -1;
case 0x7f ... 0x9f: case 0x7f ... 0x9f:
return -1; return -1;
case 0x600 ... 0x605:
return -1;
case 0x61c:
return -1;
case 0x6dd:
return -1;
case 0x70f:
return -1;
case 0x890 ... 0x891:
return -1;
case 0x8e2:
return -1;
case 0x180e:
return -1;
case 0x200b ... 0x200c:
return -1;
case 0x200e ... 0x200f:
return -1;
case 0x202a ... 0x202e:
return -1;
case 0x2060 ... 0x2064:
return -1;
case 0x2066 ... 0x206f:
return -1;
case 0xd800 ... 0xdfff: case 0xd800 ... 0xdfff:
return -1; return -1;
case 0xfeff:
return -1;
case 0xfff9 ... 0xfffb:
return -1;
case 0x110bd:
return -1;
case 0x110cd:
return -1;
case 0x13430 ... 0x13438:
return -1;
case 0x1bca0 ... 0x1bca3:
return -1;
case 0x1d173 ... 0x1d17a:
return -1;
case 0xe0001:
return -1;
case 0xe0020 ... 0xe007f:
return -1;
// }}} // }}}
// Private use (137468 codepoints) {{{ // Private use (137468 codepoints) {{{
@@ -3203,7 +3171,7 @@ is_emoji_presentation_base(uint32_t code) {
return true; return true;
case 0x1f6d5 ... 0x1f6d7: case 0x1f6d5 ... 0x1f6d7:
return true; return true;
case 0x1f6e0 ... 0x1f6e5: case 0x1f6dd ... 0x1f6e5:
return true; return true;
case 0x1f6e9: case 0x1f6e9:
return true; return true;
@@ -3215,29 +3183,31 @@ is_emoji_presentation_base(uint32_t code) {
return true; return true;
case 0x1f7e0 ... 0x1f7eb: case 0x1f7e0 ... 0x1f7eb:
return true; return true;
case 0x1f7f0:
return true;
case 0x1f90c ... 0x1f93a: case 0x1f90c ... 0x1f93a:
return true; return true;
case 0x1f93c ... 0x1f945: case 0x1f93c ... 0x1f945:
return true; return true;
case 0x1f947 ... 0x1f978: case 0x1f947 ... 0x1f9ff:
return true;
case 0x1f97a ... 0x1f9cb:
return true;
case 0x1f9cd ... 0x1f9ff:
return true; return true;
case 0x1fa70 ... 0x1fa74: case 0x1fa70 ... 0x1fa74:
return true; return true;
case 0x1fa78 ... 0x1fa7a: case 0x1fa78 ... 0x1fa7c:
return true; return true;
case 0x1fa80 ... 0x1fa86: case 0x1fa80 ... 0x1fa86:
return true; return true;
case 0x1fa90 ... 0x1faa8: case 0x1fa90 ... 0x1faac:
return true; return true;
case 0x1fab0 ... 0x1fab6: case 0x1fab0 ... 0x1faba:
return true; return true;
case 0x1fac0 ... 0x1fac2: case 0x1fac0 ... 0x1fac5:
return true; return true;
case 0x1fad0 ... 0x1fad6: case 0x1fad0 ... 0x1fad9:
return true;
case 0x1fae0 ... 0x1fae7:
return true;
case 0x1faf0 ... 0x1faf6:
return true; return true;
default: return false; default: return false;
} }

View File

@@ -127,6 +127,12 @@ class TestScreen(BaseTest):
s.draw(q) s.draw(q)
self.ae(q, str(s.line(0))) self.ae(q, str(s.line(0)))
self.ae(s.cursor.x, 8) self.ae(s.cursor.x, 8)
for x in '\u200b\u200c\u200d':
s = self.create_screen()
q = f'X{x}Y'
s.draw(q)
self.ae(q, str(s.line(0)))
self.ae(s.cursor.x, 2)
def test_char_manipulation(self): def test_char_manipulation(self):
s = self.create_screen() s = self.create_screen()

View File

@@ -19,8 +19,8 @@ from contextlib import suppress
from functools import lru_cache, partial from functools import lru_cache, partial
from pathlib import Path from pathlib import Path
from typing import ( from typing import (
Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Set, Tuple, Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, Sequence,
Union Set, Tuple, Union
) )
from glfw import glfw from glfw import glfw
@@ -231,7 +231,7 @@ def test_compile(
with open(os.path.join(tdir, f'source.{source_ext}'), 'w', encoding='utf-8') as srcf: with open(os.path.join(tdir, f'source.{source_ext}'), 'w', encoding='utf-8') as srcf:
print(src, file=srcf) print(src, file=srcf)
return subprocess.Popen( return subprocess.Popen(
cc + list(cflags) + ([] if link_also else ['-c']) + cc + ['-Werror=implicit-function-declaration'] + list(cflags) + ([] if link_also else ['-c']) +
['-o', os.path.join(tdir, 'source.output'), srcf.name] + ['-o', os.path.join(tdir, 'source.output'), srcf.name] +
[f'-l{x}' for x in libraries] + list(ldflags), [f'-l{x}' for x in libraries] + list(ldflags),
stdout=subprocess.DEVNULL, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
@@ -1161,10 +1161,13 @@ def package(args: Options, bundle_type: str) -> None:
if for_freeze: if for_freeze:
shutil.copytree('kitty_tests', os.path.join(libdir, 'kitty_tests')) shutil.copytree('kitty_tests', os.path.join(libdir, 'kitty_tests'))
def repl(name: str, raw: str, defval: Union[str, float], val: Union[str, float]) -> str: def repl(name: str, raw: str, defval: Union[str, float, FrozenSet[str]], val: Union[str, float, FrozenSet[str]]) -> str:
if defval == val: if defval == val:
return raw return raw
prefix = f'{name}: {type(defval).__name__} =' tname = type(defval).__name__
if tname == 'frozenset':
tname = 'typing.FrozenSet[str]'
prefix = f'{name}: {tname} ='
nraw = raw.replace(f'{prefix} {defval!r}', f'{prefix} {val!r}', 1) nraw = raw.replace(f'{prefix} {defval!r}', f'{prefix} {val!r}', 1)
if nraw == raw: if nraw == raw:
raise SystemExit(f'Failed to change the value of {name}') raise SystemExit(f'Failed to change the value of {name}')
@@ -1173,7 +1176,7 @@ def package(args: Options, bundle_type: str) -> None:
with open(os.path.join(libdir, 'kitty/options/types.py'), 'r+', encoding='utf-8') as f: with open(os.path.join(libdir, 'kitty/options/types.py'), 'r+', encoding='utf-8') as f:
oraw = raw = f.read() oraw = raw = f.read()
raw = repl('update_check_interval', raw, Options.update_check_interval, args.update_check_interval) raw = repl('update_check_interval', raw, Options.update_check_interval, args.update_check_interval)
raw = repl('shell_integration', raw, Options.shell_integration, args.shell_integration) raw = repl('shell_integration', raw, frozenset(Options.shell_integration.split()), frozenset(args.shell_integration.split()))
if raw != oraw: if raw != oraw:
f.seek(0), f.truncate(), f.write(raw) f.seek(0), f.truncate(), f.write(raw)

View File

@@ -5,7 +5,7 @@ _ksi_main() {
if [[ -z "$KITTY_SHELL_INTEGRATION" ]]; then return; fi if [[ -z "$KITTY_SHELL_INTEGRATION" ]]; then return; fi
declare -A _ksi_prompt=( [cursor]='y' [title]='y' [mark]='y' [complete]='y' ) declare -A _ksi_prompt=( [cursor]='y' [title]='y' [mark]='y' [complete]='y' )
set -f set -f
for i in ${KITTY_SHELL_INTEGRATION}; do for i in ${KITTY_SHELL_INTEGRATION[@]}; do
set +f set +f
if [[ "$i" == "no-cursor" ]]; then _ksi_prompt[cursor]='n'; fi if [[ "$i" == "no-cursor" ]]; then _ksi_prompt[cursor]='n'; fi
if [[ "$i" == "no-title" ]]; then _ksi_prompt[title]='n'; fi if [[ "$i" == "no-title" ]]; then _ksi_prompt[title]='n'; fi

View File

@@ -21,7 +21,7 @@
# builtins with `builtin` to avoid accidentally invoking user-defined functions. # builtins with `builtin` to avoid accidentally invoking user-defined functions.
# We avoid `function` reserved word as an additional defensive measure. # We avoid `function` reserved word as an additional defensive measure.
builtin emulate -L zsh -o no_warn_create_global builtin emulate -L zsh -o no_warn_create_global -o no_aliases
[[ -o interactive ]] || builtin return 0 # non-interactive shell [[ -o interactive ]] || builtin return 0 # non-interactive shell
[[ -n $KITTY_SHELL_INTEGRATION ]] || builtin return 0 # integration disabled [[ -n $KITTY_SHELL_INTEGRATION ]] || builtin return 0 # integration disabled
@@ -32,14 +32,42 @@ builtin emulate -L zsh -o no_warn_create_global
# 2: none of the above. # 2: none of the above.
builtin typeset -gi _ksi_state builtin typeset -gi _ksi_state
# Attempt to create a writable file descriptor to the TTY so that we can print
# to the TTY later even when STDOUT is redirected. This code is fairly subtle.
#
# - It's tempting to do `[[ -t 1 ]] && exec {_ksi_state}>&1` but we cannot do this
# because it'll create a file descriptor >= 10 without O_CLOEXEC. This file
# descriptor will leak to child processes.
# - If we do `exec {3}>&1`, the file descriptor won't leak to the child processes
# but it'll still leak if the current process is replaced with another. In
# addition, it'll break user code that relies on fd 3 being available.
# - Zsh doesn't expose dup3, which would have allowed us to copy STDOUT with
# O_CLOEXEC. The only way to create a file descriptor with O_CLOEXEC is via
# sysopen.
# - `zmodload zsh/system` and `sysopen -o cloexec -wu _ksi_fd -- /dev/tty` can
# fail with an error message to STDERR (the latter can happen even if /dev/tty
# is writable), hence the redirection of STDERR. We do it for the whole block
# for performance reasons (redirections are slow).
# - We must open the file descriptor right here rather than in _ksi_deferred_init
# because there are broken zsh plugins out there that run `exec {fd}< <(cmd)`
# and then close the file descriptor more than once while suppressing errors.
# This could end up closing our file descriptor if we opened it in
# _ksi_deferred_init.
typeset -gi _ksi_fd
{
zmodload zsh/system && (( $+builtins[sysopen] )) && {
{ [[ -w $TTY ]] && sysopen -o cloexec -wu _ksi_fd -- $TTY } ||
{ [[ -w /dev/tty ]] && sysopen -o cloexec -wu _ksi_fd -- /dev/tty }
}
} 2>/dev/null || (( _ksi_fd = 1 ))
# Asks kitty to print $@ to its STDOUT. This is for debugging. # Asks kitty to print $@ to its STDOUT. This is for debugging.
_ksi_debug_print() { _ksi_debug_print() {
builtin local data saved builtin local data
saved="$IFS" data=$(command base64 <<<"${(j: :)@}") || builtin return
IFS=" " # Removing all spaces rather than just \n allows this code to
data=$(command base64 <<< "$*") # work on broken systems where base64 outputs \r\n.
IFS="$saved" builtin print -nu "$_ksi_fd" '\eP@kitty-print|'"${data//[[:space:]]}"'\e\\'
builtin printf '\eP@kitty-print|%s\e\\' "${data//$'\n'}"
} }
# We defer initialization until precmd for several reasons: # We defer initialization until precmd for several reasons:
@@ -49,12 +77,12 @@ _ksi_debug_print() {
# - By deferring initialization we allow user rc files to opt out from some # - By deferring initialization we allow user rc files to opt out from some
# parts of integration. For example, if a zshrc theme prints OSC 133 # parts of integration. For example, if a zshrc theme prints OSC 133
# marks, it can append " no-prompt-mark" to KITTY_SHELL_INTEGRATION during # marks, it can append " no-prompt-mark" to KITTY_SHELL_INTEGRATION during
# intialization to avoid redundant marks from our code. # initialization to avoid redundant marks from our code.
builtin typeset -ag precmd_functions builtin typeset -ag precmd_functions
precmd_functions+=(_ksi_deferred_init) precmd_functions+=(_ksi_deferred_init)
_ksi_deferred_init() { _ksi_deferred_init() {
builtin emulate -L zsh -o no_warn_create_global builtin emulate -L zsh -o no_warn_create_global -o no_aliases
# Recognized options: no-cursor, no-title, no-prompt-mark, no-complete. # Recognized options: no-cursor, no-title, no-prompt-mark, no-complete.
builtin local -a opt builtin local -a opt
@@ -69,7 +97,6 @@ _ksi_deferred_init() {
builtin local comp_dir=$self_dir/completions builtin local comp_dir=$self_dir/completions
# Enable completions for `kitty` command. # Enable completions for `kitty` command.
_ksi_debug_print "$self_dir"
if (( ! opt[(Ie)no-complete] )) && [[ -r $comp_dir/_kitty ]]; then if (( ! opt[(Ie)no-complete] )) && [[ -r $comp_dir/_kitty ]]; then
if (( $+functions[compdef] )); then if (( $+functions[compdef] )); then
# If compdef is defined, then either compinit has already run or it's # If compdef is defined, then either compinit has already run or it's
@@ -99,8 +126,10 @@ _ksi_deferred_init() {
# themselves with a blinking block cursor within fzf. # themselves with a blinking block cursor within fzf.
_ksi_zle_line_init _ksi_zle_line_finish _ksi_zle_keymap_select() { _ksi_zle_line_init _ksi_zle_line_finish _ksi_zle_keymap_select() {
case ${KEYMAP-} in case ${KEYMAP-} in
vicmd|visual) builtin print -n '\e[1 q';; # blinking block cursor # Blinking block cursor.
*) builtin print -n '\e[5 q';; # blinking bar cursor vicmd|visual) builtin print -nu "$_ksi_fd" '\e[1 q';;
# Blinking bar cursor.
*) builtin print -nu "$_ksi_fd" '\e[5 q';;
esac esac
} }
fi fi
@@ -109,7 +138,7 @@ _ksi_deferred_init() {
if (( ! opt[(Ie)no-prompt-mark] )); then if (( ! opt[(Ie)no-prompt-mark] )); then
_ksi_precmd() { _ksi_precmd() {
builtin local -i cmd_status=$? builtin local -i cmd_status=$?
builtin emulate -L zsh -o no_warn_create_global builtin emulate -L zsh -o no_warn_create_global -o no_aliases
# Don't write OSC 133 D when our precmd handler is invoked from zle. # Don't write OSC 133 D when our precmd handler is invoked from zle.
# Some plugins do that to update prompt on cd. # Some plugins do that to update prompt on cd.
@@ -126,11 +155,11 @@ _ksi_deferred_init() {
if (( _ksi_state == 1 )); then if (( _ksi_state == 1 )); then
# The last written OSC 133 C has not been closed with D yet. # The last written OSC 133 C has not been closed with D yet.
# Close it and supply status. # Close it and supply status.
builtin printf '\e]133;D;%s\a' $cmd_status builtin print -nu $_ksi_fd '\e]133;D;'$cmd_status'\a'
(( _ksi_state = 2 )) (( _ksi_state = 2 ))
elif (( _ksi_state == 2 )); then elif (( _ksi_state == 2 )); then
# There might be an unclosed OSC 133 C. Close that. # There might be an unclosed OSC 133 C. Close that.
builtin print -n '\e]133;D\a' builtin print -nu $_ksi_fd '\e]133;D\a'
fi fi
fi fi
@@ -152,6 +181,7 @@ _ksi_deferred_init() {
# - False positive (with prompt_percent): PS1="%(?.$mark1.)" # - False positive (with prompt_percent): PS1="%(?.$mark1.)"
# - False negative (with prompt_subst): PS1='$mark1' # - False negative (with prompt_subst): PS1='$mark1'
[[ $PS1 == *$mark1* ]] || PS1=${mark1}${PS1} [[ $PS1 == *$mark1* ]] || PS1=${mark1}${PS1}
# PS2 mark is needed when clearing the prompt on resize
[[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2} [[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2}
(( _ksi_state = 2 )) (( _ksi_state = 2 ))
else else
@@ -168,7 +198,7 @@ _ksi_deferred_init() {
# already have a mark, so the following reset-prompt will write # already have a mark, so the following reset-prompt will write
# it. If it doesn't, there is nothing we can do. # it. If it doesn't, there is nothing we can do.
if ! builtin zle; then if ! builtin zle; then
builtin print -rn -- $mark1[3,-3] builtin print -rnu $_ksi_fd -- $mark1[3,-3]
(( _ksi_state = 2 )) (( _ksi_state = 2 ))
fi fi
fi fi
@@ -176,13 +206,13 @@ _ksi_deferred_init() {
# Without prompt_percent we cannot patch prompt. Just print the # Without prompt_percent we cannot patch prompt. Just print the
# mark, except when we are invoked from zle. In the latter case we # mark, except when we are invoked from zle. In the latter case we
# cannot do anything. # cannot do anything.
builtin print -rn -- $mark1[3,-3] builtin print -rnu $_ksi_fd -- $mark1[3,-3]
(( _ksi_state = 2 )) (( _ksi_state = 2 ))
fi fi
} }
_ksi_preexec() { _ksi_preexec() {
builtin emulate -L zsh -o no_warn_create_global builtin emulate -L zsh -o no_warn_create_global -o no_aliases
# This can potentially break user prompt. Oh well. The robustness of # This can potentially break user prompt. Oh well. The robustness of
# this code can be improved in the case prompt_subst is set because # this code can be improved in the case prompt_subst is set because
@@ -198,12 +228,15 @@ _ksi_deferred_init() {
# its preexec hook before us, we'll incorrectly mark its output as # its preexec hook before us, we'll incorrectly mark its output as
# belonging to the command (as if the user typed it into zle) rather # belonging to the command (as if the user typed it into zle) rather
# than command output. # than command output.
builtin print -n '\e]133;C\a' builtin print -nu $_ksi_fd '\e]133;C\a'
(( _ksi_state = 1 )) (( _ksi_state = 1 ))
} }
functions[_ksi_zle_line_init]+=' # the following two lines are commented out as currently kitty doesn't use B prompt marking
builtin print -n "\\e]133;B\\a"' # and hooking zle widgets in ZSH is a total minefield, see https://github.com/kovidgoyal/kitty/issues/4428
# so we can at least tell users to use no-cursor and with that avoid hooking ZLE widgets at all
# functions[_ksi_zle_line_init]+='
# builtin print -nu "$_ksi_fd" "\\e]133;B\\a"'
fi fi
# Enable terminal title changes. # Enable terminal title changes.
@@ -214,10 +247,10 @@ _ksi_deferred_init() {
# We use (V) in preexec to convert control characters to something visible # We use (V) in preexec to convert control characters to something visible
# (LF becomes \n, etc.). This isn't necessary in precmd because (%) does it # (LF becomes \n, etc.). This isn't necessary in precmd because (%) does it
# for us. # for us.
functions[_ksi_precmd]+=' functions[_ksi_precmd]+="
builtin printf "\\e]2;%s\\a" "${(%):-%(4~|…/%3~|%~)}"' builtin print -rnu $_ksi_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
functions[_ksi_preexec]+=' functions[_ksi_preexec]+="
builtin printf "\\e]2;%s\\a" "${(V)1}"' builtin print -rnu $_ksi_fd \$'\\e]2;'\"\${(V)1}\"\$'\\a'"
fi fi
# Some zsh users manually run `source ~/.zshrc` in order to apply rc file # Some zsh users manually run `source ~/.zshrc` in order to apply rc file
@@ -231,16 +264,44 @@ _ksi_deferred_init() {
# current shell is `exec zsh`. This will remove our integration from the shell # current shell is `exec zsh`. This will remove our integration from the shell
# unless it's explicitly invoked from .zshrc. This is not an issue with # unless it's explicitly invoked from .zshrc. This is not an issue with
# `exec zsh` but rather with our implementation of automatic shell integration. # `exec zsh` but rather with our implementation of automatic shell integration.
builtin autoload -Uz add-zle-hook-widget
if (( $+functions[_ksi_zle_line_init] )); then # In the ideal world we would use add-zle-hook-widget to hook zle-line-init
add-zle-hook-widget line-init _ksi_zle_line_init # and similar widget. This breaks user configs though, so we have do this
fi # horrible thing instead.
if (( $+functions[_ksi_zle_line_finish] )); then builtin local hook func widget orig_widget flag
add-zle-hook-widget line-finish _ksi_zle_line_finish for hook in line-init line-finish keymap-select; do
fi func=_ksi_zle_${hook/-/_}
if (( $+functions[_ksi_zle_keymap_select] )); then (( $+functions[$func] )) || builtin continue
add-zle-hook-widget keymap-select _ksi_zle_keymap_select widget=zle-$hook
fi if [[ $widgets[$widget] == user:azhw:* &&
$+functions[add-zle-hook-widget] -eq 1 ]]; then
# If the widget is already hooked by add-zle-hook-widget at the top
# level, add our hook at the end. We MUST do it this way. We cannot
# just wrap the widget ourselves in this case because it would
# trigger bugs in add-zle-hook-widget.
add-zle-hook-widget $hook $func
else
if (( $+widgets[$widget] )); then
# There is a widget but it's not from add-zle-hook-widget. We
# can rename the original widget, install our own and invoke
# the original when we are called.
#
# Note: The leading dot is to work around bugs in
# zsh-syntax-highlighting.
orig_widget=._ksi_orig_$widget
builtin zle -A $widget $orig_widget
if [[ $widgets[$widget] == user:* ]]; then
# No -w here to preserve $WIDGET within the original widget.
flag=
else
flag=w
fi
functions[$func]+="
builtin zle $orig_widget -N$flag -- \"$@\""
fi
builtin zle -N $widget $func
fi
done
if (( $+functions[_ksi_preexec] )); then if (( $+functions[_ksi_preexec] )); then
builtin typeset -ag preexec_functions builtin typeset -ag preexec_functions