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:
import subprocess
import tty
import termios
from contextlib import suppress
from kittens.tui.operations import init_state, set_cursor_visible
ret = subprocess.Popen(args[1:]).wait()
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):
tty.setraw(sys.stdin.fileno())
sys.stdin.buffer.read(1)
fd = sys.stdin.fileno()
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)

View File

@@ -28,3 +28,9 @@ details > summary {
text-decoration-color: var(--color-link-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
-------------------------------------
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]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -129,11 +129,6 @@ different shells.
and sources the original :file:`.zshenv`. It then loads the shell integration code.
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
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
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
:envvar:`KITTY_SHELL_INTEGRATION` to the value of the :opt:`shell_integration`
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:
.. 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
.. code-block:: sh
@@ -228,6 +220,15 @@ Then in your shell's rc file, add the lines:
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
:opt:`shell_integration`, except if you want to disable shell integration
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()
name_map: Dict[int, str] = {}
word_search_map: DefaultDict[str, Set[int]] = defaultdict(set)
zwj = 0x200d
soft_hyphen = 0xad
flag_codepoints = frozenset(range(0x1F1E6, 0x1F1E6 + 26))
# 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))
property_maps: Dict[str, Set[int]] = defaultdict(set)
@@ -69,7 +68,6 @@ def parse_prop_list() -> None:
property_maps[name] |= chars
# see https://www.unicode.org/faq/unsup_char.html#3
marks |= property_maps['Other_Default_Ignorable_Code_Point']
marks.add(soft_hyphen)
def parse_ucd() -> None:
@@ -113,6 +111,12 @@ def parse_ucd() -> None:
marks.add(codepoint)
elif category.startswith('S'):
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:
for line in f:
@@ -382,9 +386,9 @@ def gen_ucd() -> None:
ascii_range='false'
)
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',
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'
)
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
if s < e <= len(text):
before = text[s]
if before in '({[<' and text[e-1] == closing_bracket_map[before]:
s += 1
e -= 1
if before in '({[<':
q = closing_bracket_map[before]
if text[e-1] == q:
s += 1
e -= 1
elif text[e:e+1] == q:
s += 1
return s, e
@@ -282,9 +286,12 @@ def quotes(text: str, s: int, e: int) -> Tuple[int, int]:
# Remove matching quotes
if s < e <= len(text):
before = text[s]
if before in '\'"' and text[e-1] == before:
s += 1
e -= 1
if before in '\'"':
if text[e-1] == before:
s += 1
e -= 1
elif text[e:e+1] == before:
s += 1
return s, e

View File

@@ -291,7 +291,7 @@ class MouseTracking(Enum):
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 ''
ans = (
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)
elif mouse_tracking is MouseTracking.full:
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

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
#pragma once
#include "data-types.h"

View File

@@ -22,7 +22,7 @@ class Version(NamedTuple):
appname: str = 'kitty'
kitty_face = '🐱'
version: Version = Version(0, 24, 0)
version: Version = Version(0, 24, 1)
str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat

View File

@@ -7,11 +7,10 @@ import socket
import sys
import termios
import time
from contextlib import suppress
from functools import partial
from pprint import pformat
from typing import (
IO, Callable, Dict, Generator, Iterable, Iterator, Optional, Set, Tuple
)
from typing import IO, Callable, Dict, Iterable, Iterator, Optional, Set, Tuple
from kittens.tui.operations import colored, styled
@@ -19,7 +18,7 @@ from .cli import version
from .constants import (
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.utils import MouseMap
from .rgb import color_as_sharp
@@ -41,7 +40,7 @@ def title(x: str) -> str:
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 (
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
@@ -162,16 +161,22 @@ class IssueData:
def __init__(self) -> None:
self.uname = os.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()
self.formatted_time = self.d = time.strftime('%a %b %d %Y', _time)
self.formatted_date = self.t = time.strftime('%H:%M:%S', _time)
try:
self.tty_name = format_tty_name(os.ttyname(sys.stdin.fileno()))
self.tty_name = format_tty_name(os.ctermid())
except OSError:
self.tty_name = '(none)'
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)
try:
self.num_users = num_users()
@@ -218,8 +223,18 @@ def debug_config(opts: KittyOpts) -> str:
import subprocess
p(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip())
if os.path.exists('/etc/issue'):
with open('/etc/issue', encoding='utf-8', errors='replace') as f:
p(end=''.join(IssueData().parse_issue_file(f)))
try:
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'):
with open('/etc/lsb-release', encoding='utf-8', errors='replace') as f:
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
#pragma once
#include "data-types.h"
@@ -276,7 +276,7 @@ is_emoji(char_type code) {
return true;
case 0x1f6d5 ... 0x1f6d7:
return true;
case 0x1f6e0 ... 0x1f6e5:
case 0x1f6dd ... 0x1f6e5:
return true;
case 0x1f6e9:
return true;
@@ -288,29 +288,31 @@ is_emoji(char_type code) {
return true;
case 0x1f7e0 ... 0x1f7eb:
return true;
case 0x1f7f0:
return true;
case 0x1f90c ... 0x1f93a:
return true;
case 0x1f93c ... 0x1f945:
return true;
case 0x1f947 ... 0x1f978:
return true;
case 0x1f97a ... 0x1f9cb:
return true;
case 0x1f9cd ... 0x1f9ff:
case 0x1f947 ... 0x1f9ff:
return true;
case 0x1fa70 ... 0x1fa74:
return true;
case 0x1fa78 ... 0x1fa7a:
case 0x1fa78 ... 0x1fa7c:
return true;
case 0x1fa80 ... 0x1fa86:
return true;
case 0x1fa90 ... 0x1faa8:
case 0x1fa90 ... 0x1faac:
return true;
case 0x1fab0 ... 0x1fab6:
case 0x1fab0 ... 0x1faba:
return true;
case 0x1fac0 ... 0x1fac2:
case 0x1fac0 ... 0x1fac5:
return true;
case 0x1fad0 ... 0x1fad6:
case 0x1fad0 ... 0x1fad9:
return true;
case 0x1fae0 ... 0x1fae7:
return true;
case 0x1faf0 ... 0x1faf6:
return true;
default: return false;
}

View File

@@ -387,7 +387,8 @@ has_cell_text(Font *self, CPUCell *cell) {
char_type combining_chars[arraysz(cell->cc_idx)];
unsigned num_cc = 0;
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 == 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 (!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;
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
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
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
: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
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`
The tab bar is hidden. If you use this, you might want to create a
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 "state.h"
// START_KNOWN_MARKS
static const combining_type VS15 = 1325, VS16 = 1326;
static const combining_type VS15 = 1362, VS16 = 1363;
// END_KNOWN_MARKS
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
#pragma once
#include "data-types.h"
@@ -14,7 +14,7 @@ wcwidth_std(int32_t code) {
return 2;
// }}}
// Marks (6189 codepoints) {{{
// Marks (6350 codepoints) {{{
case 0x0:
return 0;
case 0xad:
@@ -33,13 +33,17 @@ wcwidth_std(int32_t code) {
return 0;
case 0x5c7:
return 0;
case 0x600 ... 0x605:
return 0;
case 0x610 ... 0x61a:
return 0;
case 0x61c:
return 0;
case 0x64b ... 0x65f:
return 0;
case 0x670:
return 0;
case 0x6d6 ... 0x6dc:
case 0x6d6 ... 0x6dd:
return 0;
case 0x6df ... 0x6e4:
return 0;
@@ -47,6 +51,8 @@ wcwidth_std(int32_t code) {
return 0;
case 0x6ea ... 0x6ed:
return 0;
case 0x70f:
return 0;
case 0x711:
return 0;
case 0x730 ... 0x74a:
@@ -67,11 +73,11 @@ wcwidth_std(int32_t code) {
return 0;
case 0x859 ... 0x85b:
return 0;
case 0x890 ... 0x891:
return 0;
case 0x898 ... 0x89f:
return 0;
case 0x8ca ... 0x8e1:
return 0;
case 0x8e3 ... 0x903:
case 0x8ca ... 0x903:
return 0;
case 0x93a ... 0x93c:
return 0;
@@ -271,9 +277,7 @@ wcwidth_std(int32_t code) {
return 0;
case 0x17dd:
return 0;
case 0x180b ... 0x180d:
return 0;
case 0x180f:
case 0x180b ... 0x180f:
return 0;
case 0x1885 ... 0x1886:
return 0;
@@ -319,9 +323,11 @@ wcwidth_std(int32_t code) {
return 0;
case 0x1dc0 ... 0x1dff:
return 0;
case 0x200d:
case 0x200b ... 0x200f:
return 0;
case 0x2065:
case 0x202a ... 0x202e:
return 0;
case 0x2060 ... 0x206f:
return 0;
case 0x20d0 ... 0x20f0:
return 0;
@@ -405,9 +411,11 @@ wcwidth_std(int32_t code) {
return 0;
case 0xfe20 ... 0xfe2f:
return 0;
case 0xfeff:
return 0;
case 0xffa0:
return 0;
case 0xfff0 ... 0xfff8:
case 0xfff0 ... 0xfffb:
return 0;
case 0x101fd:
return 0;
@@ -447,8 +455,12 @@ wcwidth_std(int32_t code) {
return 0;
case 0x110b0 ... 0x110ba:
return 0;
case 0x110bd:
return 0;
case 0x110c2:
return 0;
case 0x110cd:
return 0;
case 0x11100 ... 0x11102:
return 0;
case 0x11127 ... 0x11134:
@@ -563,6 +575,8 @@ wcwidth_std(int32_t code) {
return 0;
case 0x11ef3 ... 0x11ef6:
return 0;
case 0x13430 ... 0x13438:
return 0;
case 0x16af0 ... 0x16af4:
return 0;
case 0x16b30 ... 0x16b36:
@@ -579,15 +593,15 @@ wcwidth_std(int32_t code) {
return 0;
case 0x1bc9d ... 0x1bc9e:
return 0;
case 0x1bca0 ... 0x1bca3:
return 0;
case 0x1cf00 ... 0x1cf2d:
return 0;
case 0x1cf30 ... 0x1cf46:
return 0;
case 0x1d165 ... 0x1d169:
return 0;
case 0x1d16d ... 0x1d172:
return 0;
case 0x1d17b ... 0x1d182:
case 0x1d16d ... 0x1d182:
return 0;
case 0x1d185 ... 0x1d18b:
return 0;
@@ -629,63 +643,17 @@ wcwidth_std(int32_t code) {
return 0;
case 0x1f3fb ... 0x1f3ff:
return 0;
case 0xe0000:
return 0;
case 0xe0002 ... 0xe001f:
return 0;
case 0xe0080 ... 0xe0fff:
case 0xe0000 ... 0xe0fff:
return 0;
// }}}
// Non-printing characters (2273 codepoints) {{{
// Non-printing characters (2112 codepoints) {{{
case 0x1 ... 0x1f:
return -1;
case 0x7f ... 0x9f:
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:
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) {{{
@@ -3203,7 +3171,7 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x1f6d5 ... 0x1f6d7:
return true;
case 0x1f6e0 ... 0x1f6e5:
case 0x1f6dd ... 0x1f6e5:
return true;
case 0x1f6e9:
return true;
@@ -3215,29 +3183,31 @@ is_emoji_presentation_base(uint32_t code) {
return true;
case 0x1f7e0 ... 0x1f7eb:
return true;
case 0x1f7f0:
return true;
case 0x1f90c ... 0x1f93a:
return true;
case 0x1f93c ... 0x1f945:
return true;
case 0x1f947 ... 0x1f978:
return true;
case 0x1f97a ... 0x1f9cb:
return true;
case 0x1f9cd ... 0x1f9ff:
case 0x1f947 ... 0x1f9ff:
return true;
case 0x1fa70 ... 0x1fa74:
return true;
case 0x1fa78 ... 0x1fa7a:
case 0x1fa78 ... 0x1fa7c:
return true;
case 0x1fa80 ... 0x1fa86:
return true;
case 0x1fa90 ... 0x1faa8:
case 0x1fa90 ... 0x1faac:
return true;
case 0x1fab0 ... 0x1fab6:
case 0x1fab0 ... 0x1faba:
return true;
case 0x1fac0 ... 0x1fac2:
case 0x1fac0 ... 0x1fac5:
return true;
case 0x1fad0 ... 0x1fad6:
case 0x1fad0 ... 0x1fad9:
return true;
case 0x1fae0 ... 0x1fae7:
return true;
case 0x1faf0 ... 0x1faf6:
return true;
default: return false;
}

View File

@@ -127,6 +127,12 @@ class TestScreen(BaseTest):
s.draw(q)
self.ae(q, str(s.line(0)))
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):
s = self.create_screen()

View File

@@ -19,8 +19,8 @@ from contextlib import suppress
from functools import lru_cache, partial
from pathlib import Path
from typing import (
Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Set, Tuple,
Union
Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, Sequence,
Set, Tuple, Union
)
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:
print(src, file=srcf)
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] +
[f'-l{x}' for x in libraries] + list(ldflags),
stdout=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
@@ -1161,10 +1161,13 @@ def package(args: Options, bundle_type: str) -> None:
if for_freeze:
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:
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)
if nraw == raw:
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:
oraw = raw = f.read()
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:
f.seek(0), f.truncate(), f.write(raw)

View File

@@ -5,7 +5,7 @@ _ksi_main() {
if [[ -z "$KITTY_SHELL_INTEGRATION" ]]; then return; fi
declare -A _ksi_prompt=( [cursor]='y' [title]='y' [mark]='y' [complete]='y' )
set -f
for i in ${KITTY_SHELL_INTEGRATION}; do
for i in ${KITTY_SHELL_INTEGRATION[@]}; do
set +f
if [[ "$i" == "no-cursor" ]]; then _ksi_prompt[cursor]='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.
# 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
[[ -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.
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.
_ksi_debug_print() {
builtin local data saved
saved="$IFS"
IFS=" "
data=$(command base64 <<< "$*")
IFS="$saved"
builtin printf '\eP@kitty-print|%s\e\\' "${data//$'\n'}"
builtin local data
data=$(command base64 <<<"${(j: :)@}") || builtin return
# Removing all spaces rather than just \n allows this code to
# work on broken systems where base64 outputs \r\n.
builtin print -nu "$_ksi_fd" '\eP@kitty-print|'"${data//[[:space:]]}"'\e\\'
}
# 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
# parts of integration. For example, if a zshrc theme prints OSC 133
# 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
precmd_functions+=(_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.
builtin local -a opt
@@ -69,7 +97,6 @@ _ksi_deferred_init() {
builtin local comp_dir=$self_dir/completions
# Enable completions for `kitty` command.
_ksi_debug_print "$self_dir"
if (( ! opt[(Ie)no-complete] )) && [[ -r $comp_dir/_kitty ]]; then
if (( $+functions[compdef] )); then
# 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.
_ksi_zle_line_init _ksi_zle_line_finish _ksi_zle_keymap_select() {
case ${KEYMAP-} in
vicmd|visual) builtin print -n '\e[1 q';; # blinking block cursor
*) builtin print -n '\e[5 q';; # blinking bar cursor
# Blinking block cursor.
vicmd|visual) builtin print -nu "$_ksi_fd" '\e[1 q';;
# Blinking bar cursor.
*) builtin print -nu "$_ksi_fd" '\e[5 q';;
esac
}
fi
@@ -109,7 +138,7 @@ _ksi_deferred_init() {
if (( ! opt[(Ie)no-prompt-mark] )); then
_ksi_precmd() {
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.
# Some plugins do that to update prompt on cd.
@@ -126,11 +155,11 @@ _ksi_deferred_init() {
if (( _ksi_state == 1 )); then
# The last written OSC 133 C has not been closed with D yet.
# 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 ))
elif (( _ksi_state == 2 )); then
# 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
@@ -152,6 +181,7 @@ _ksi_deferred_init() {
# - False positive (with prompt_percent): PS1="%(?.$mark1.)"
# - False negative (with prompt_subst): PS1='$mark1'
[[ $PS1 == *$mark1* ]] || PS1=${mark1}${PS1}
# PS2 mark is needed when clearing the prompt on resize
[[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2}
(( _ksi_state = 2 ))
else
@@ -168,7 +198,7 @@ _ksi_deferred_init() {
# already have a mark, so the following reset-prompt will write
# it. If it doesn't, there is nothing we can do.
if ! builtin zle; then
builtin print -rn -- $mark1[3,-3]
builtin print -rnu $_ksi_fd -- $mark1[3,-3]
(( _ksi_state = 2 ))
fi
fi
@@ -176,13 +206,13 @@ _ksi_deferred_init() {
# Without prompt_percent we cannot patch prompt. Just print the
# mark, except when we are invoked from zle. In the latter case we
# cannot do anything.
builtin print -rn -- $mark1[3,-3]
builtin print -rnu $_ksi_fd -- $mark1[3,-3]
(( _ksi_state = 2 ))
fi
}
_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 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
# belonging to the command (as if the user typed it into zle) rather
# than command output.
builtin print -n '\e]133;C\a'
builtin print -nu $_ksi_fd '\e]133;C\a'
(( _ksi_state = 1 ))
}
functions[_ksi_zle_line_init]+='
builtin print -n "\\e]133;B\\a"'
# the following two lines are commented out as currently kitty doesn't use B prompt marking
# 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
# Enable terminal title changes.
@@ -214,10 +247,10 @@ _ksi_deferred_init() {
# 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
# for us.
functions[_ksi_precmd]+='
builtin printf "\\e]2;%s\\a" "${(%):-%(4~|…/%3~|%~)}"'
functions[_ksi_preexec]+='
builtin printf "\\e]2;%s\\a" "${(V)1}"'
functions[_ksi_precmd]+="
builtin print -rnu $_ksi_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
functions[_ksi_preexec]+="
builtin print -rnu $_ksi_fd \$'\\e]2;'\"\${(V)1}\"\$'\\a'"
fi
# 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
# unless it's explicitly invoked from .zshrc. This is not an issue with
# `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
add-zle-hook-widget line-init _ksi_zle_line_init
fi
if (( $+functions[_ksi_zle_line_finish] )); then
add-zle-hook-widget line-finish _ksi_zle_line_finish
fi
if (( $+functions[_ksi_zle_keymap_select] )); then
add-zle-hook-widget keymap-select _ksi_zle_keymap_select
fi
# In the ideal world we would use add-zle-hook-widget to hook zle-line-init
# and similar widget. This breaks user configs though, so we have do this
# horrible thing instead.
builtin local hook func widget orig_widget flag
for hook in line-init line-finish keymap-select; do
func=_ksi_zle_${hook/-/_}
(( $+functions[$func] )) || builtin continue
widget=zle-$hook
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
builtin typeset -ag preexec_functions