mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-05 23:51:29 +02:00
Migrate type checker to ty
Much faster than mypy. Matches usage of ruff from same developer.
This commit is contained in:
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -23,15 +23,15 @@ jobs:
|
||||
cc: [gcc, clang]
|
||||
include:
|
||||
- python: a
|
||||
pyver: "3.11"
|
||||
pyver: "3.12"
|
||||
sanitize: 0
|
||||
|
||||
- python: b
|
||||
pyver: "3.12"
|
||||
pyver: "3.13"
|
||||
sanitize: 1
|
||||
|
||||
- python: c
|
||||
pyver: "3.13"
|
||||
pyver: "3.14"
|
||||
sanitize: 1
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
${{ runner.os }}-golang-static-
|
||||
|
||||
- name: Install build-only deps
|
||||
run: python -m pip install -r docs/requirements.txt ruff mypy types-requests types-docutils types-Pygments
|
||||
run: python -m pip install -r docs/requirements.txt ruff ty types-requests types-docutils types-Pygments
|
||||
|
||||
- name: Run ruff
|
||||
run: ruff check .
|
||||
@@ -120,8 +120,8 @@ jobs:
|
||||
- name: Build kitty
|
||||
run: python setup.py build --debug
|
||||
|
||||
- name: Run mypy
|
||||
run: which python && python -m mypy --version && ./test.py mypy
|
||||
- name: Run type checking
|
||||
run: which python && ./test.py type-check
|
||||
|
||||
- name: Run go vet
|
||||
run: go version && go vet -tags testing ./...
|
||||
|
||||
@@ -1 +1 @@
|
||||
to_vm_excludes '/dependencies /build /dist /kitty/launcher/kitty* /.build-cache /.cache /.mypy_cache /.ruff_cache /tags __pycache__ /*_commands.json *.so *.pyd *.pyc *_generated.go'
|
||||
to_vm_excludes '/dependencies /build /dist /kitty/launcher/kitty* /.build-cache /.cache /.ruff_cache /tags __pycache__ /*_commands.json *.so *.pyd *.pyc *_generated.go'
|
||||
|
||||
@@ -17,7 +17,7 @@ from functools import lru_cache, partial
|
||||
from typing import Any, Callable, Dict, Iterable, Iterator, List, Tuple
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst.roles import set_classes
|
||||
from docutils.parsers.rst.roles import normalize_options
|
||||
from pygments.lexer import RegexLexer
|
||||
from pygments.lexer import bygroups as untyped_bygroups
|
||||
from pygments.token import Comment, Error, Keyword, Literal, Name, Number, String, Whitespace
|
||||
@@ -221,7 +221,7 @@ def commit_role(
|
||||
prb = inliner.problematic(rawtext, rawtext, msg)
|
||||
return [prb], [msg]
|
||||
url = f'https://github.com/kovidgoyal/kitty/commit/{commit_id}'
|
||||
set_classes(options)
|
||||
normalize_options(options)
|
||||
short_id = subprocess.check_output(
|
||||
f'git rev-list --max-count=1 --abbrev-commit {commit_id}'.split()).decode('utf-8').strip()
|
||||
node = nodes.reference(rawtext, f'commit: {short_id}', refuri=url, **options)
|
||||
@@ -498,7 +498,7 @@ def link_role(
|
||||
return [prb], [msg]
|
||||
text, url = m.group(1, 2)
|
||||
url = url.replace(' ', '')
|
||||
set_classes(options)
|
||||
normalize_options(options)
|
||||
node = nodes.reference(rawtext, text, refuri=url, **options)
|
||||
return [node], []
|
||||
|
||||
@@ -672,7 +672,7 @@ def monkeypatch_man_writer() -> None:
|
||||
th += "\n"
|
||||
sh_tmpl: str = (".SH Name\n"
|
||||
"%(ktitle)s \\- %(subtitle)s\n")
|
||||
return th + sh_tmpl % di # type: ignore
|
||||
return th + sh_tmpl % di
|
||||
|
||||
setattr(ManualPageTranslator, 'header', header)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import types
|
||||
|
||||
from kitty.conf.generate import write_output
|
||||
|
||||
@@ -45,7 +46,7 @@ def main(args: list[str]=sys.argv) -> None:
|
||||
all_colors = []
|
||||
special_colors = []
|
||||
for opt in definition.iter_all_options():
|
||||
if callable(opt.parser_func):
|
||||
if isinstance(opt.parser_func, types.FunctionType):
|
||||
match opt.parser_func.__name__:
|
||||
case 'to_color':
|
||||
all_colors.append(opt.name)
|
||||
|
||||
@@ -605,7 +605,7 @@ def load_ref_map() -> dict[str, dict[str, str]]:
|
||||
raw = f.read()
|
||||
raw = raw.split('{', 1)[1].split('}', 1)[0]
|
||||
data = json.loads(bytes(bytearray(json.loads(f'[{raw}]'))))
|
||||
return data # type: ignore
|
||||
return data
|
||||
|
||||
|
||||
def generate_constants() -> str:
|
||||
|
||||
@@ -51,8 +51,8 @@ class Broadcast(Handler):
|
||||
self.cmd.clear_to_end_of_screen()
|
||||
self.line_edit.write(self.write, screen_cols=self.screen_size.cols)
|
||||
|
||||
def on_resize(self, screen_size: ScreenSize) -> None:
|
||||
super().on_resize(screen_size)
|
||||
def on_resize(self, new_size: ScreenSize) -> None:
|
||||
super().on_resize(new_size)
|
||||
self.commit_line()
|
||||
|
||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||
|
||||
@@ -297,7 +297,7 @@ def linenum_handle_result(args: list[str], data: dict[str, Any], target_window_i
|
||||
def is_copy_action(s: str) -> bool:
|
||||
return s in ('-', '@', '*') or s.startswith('@')
|
||||
|
||||
programs = list(filter(is_copy_action, data['programs'] or ()))
|
||||
programs = list(filter(is_copy_action, data['programs'] or []))
|
||||
# keep for backward compatibility, previously option `--program` does not need to be specified to perform copy actions
|
||||
if is_copy_action(cmd[0]):
|
||||
programs.append(cmd.pop(0))
|
||||
@@ -412,7 +412,8 @@ def handle_result(args: list[str], data: dict[str, Any], target_window_id: int,
|
||||
w = boss.window_id_map.get(target_window_id)
|
||||
boss.call_remote_control(self_window=w, args=tuple(launch_args + ([m] if isinstance(m, str) else m)))
|
||||
else:
|
||||
boss.open_url(m, program, cwd=cwd)
|
||||
if isinstance(m, str):
|
||||
boss.open_url(m, program, cwd=cwd)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -102,7 +102,7 @@ class KittenUI:
|
||||
raise ValueError('Remote control is not enabled, remember to use allow_remote_control=True')
|
||||
prefix = [kitten_exe(), '@']
|
||||
r = -1
|
||||
pass_fds = list(kw.get('pass_fds') or ())
|
||||
pass_fds = list(kw.get('pass_fds') or [])
|
||||
try:
|
||||
if self.rc_fd > -1:
|
||||
pass_fds.append(self.rc_fd)
|
||||
@@ -200,12 +200,13 @@ class Handler:
|
||||
self.debug.fobj = self
|
||||
self.initialize()
|
||||
|
||||
def __exit__(self, etype: type, value: Exception, tb: TracebackType) -> None:
|
||||
def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, tb: TracebackType | None) -> bool | None:
|
||||
del self.debug.fobj
|
||||
with suppress(Exception):
|
||||
self.finalize()
|
||||
if self._image_manager is not None:
|
||||
self._image_manager.__exit__(etype, value, tb)
|
||||
self._image_manager.__exit__(exc_type, exc_val, tb)
|
||||
return None
|
||||
|
||||
def initialize(self) -> None:
|
||||
pass
|
||||
@@ -213,8 +214,8 @@ class Handler:
|
||||
def finalize(self) -> None:
|
||||
pass
|
||||
|
||||
def on_resize(self, screen_size: ScreenSize) -> None:
|
||||
self.screen_size = screen_size
|
||||
def on_resize(self, new_size: ScreenSize) -> None:
|
||||
self.screen_size = new_size
|
||||
|
||||
def quit_loop(self, return_code: int | None = None) -> None:
|
||||
self._tui_loop.quit(return_code)
|
||||
|
||||
@@ -10,7 +10,7 @@ from collections.abc import Callable, Iterator, Sequence
|
||||
from contextlib import suppress
|
||||
from enum import IntEnum
|
||||
from itertools import count
|
||||
from typing import Any, ClassVar, DefaultDict, Deque, Generic, Optional, TypeVar, Union, cast
|
||||
from typing import Any, ClassVar, DefaultDict, Deque, Generic, Optional, TypeVar, Union, cast, final
|
||||
|
||||
from kitty.conf.utils import positive_float, positive_int
|
||||
from kitty.fast_data_types import create_canvas
|
||||
@@ -343,6 +343,7 @@ class Alias(Generic[T]):
|
||||
self.name = Alias.currently_processing
|
||||
|
||||
|
||||
@final
|
||||
class GraphicsCommand:
|
||||
a = action = Alias('t')
|
||||
q = quiet = Alias(0)
|
||||
@@ -467,12 +468,13 @@ class ImageManager:
|
||||
gc.t = 'f'
|
||||
self.handler.cmd.gr_command(gc, standard_b64encode(f.name.encode(fsenc)))
|
||||
|
||||
def __exit__(self, *a: Any) -> None:
|
||||
def __exit__(self, *a: Any) -> bool | None:
|
||||
import shutil
|
||||
shutil.rmtree(self.tdir, ignore_errors=True)
|
||||
self.handler.cmd.clear_images_on_screen(delete_data=True)
|
||||
self.delete_all_sent_images()
|
||||
del self.handler
|
||||
return None
|
||||
|
||||
def delete_all_sent_images(self) -> None:
|
||||
gc = GraphicsCommand()
|
||||
|
||||
@@ -84,7 +84,7 @@ class PathCompleter:
|
||||
import readline
|
||||
|
||||
from .dircolors import Dircolors
|
||||
if 'libedit' in readline.__doc__:
|
||||
if 'libedit' in getattr(readline, '__doc__', '') or '':
|
||||
readline.parse_and_bind("bind -e")
|
||||
readline.parse_and_bind("bind '\t' rl_complete")
|
||||
else:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import inspect
|
||||
from typing import NamedTuple
|
||||
from typing import NamedTuple, cast
|
||||
|
||||
from .boss import Boss
|
||||
from .tabs import Tab
|
||||
@@ -50,7 +50,7 @@ def get_all_actions() -> dict[ActionGroup, list[Action]]:
|
||||
short_help = first
|
||||
long_help = '\n'.join(lines).strip()
|
||||
assert spec.group in groups
|
||||
return Action(getattr(x, '__name__'), spec.group, short_help, long_help)
|
||||
return Action(getattr(x, '__name__'), cast(ActionGroup, spec.group), short_help, long_help)
|
||||
|
||||
seen = set()
|
||||
for cls in (Window, Tab, Boss):
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import (
|
||||
Literal,
|
||||
Optional,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
@@ -899,7 +900,8 @@ class Boss:
|
||||
for x in payload:
|
||||
c.response_from_kitty(self, self_window, PayloadGetter(c, x if isinstance(x, dict) else {}))
|
||||
return None
|
||||
return c.response_from_kitty(self, self_window, PayloadGetter(c, payload if isinstance(payload, dict) else {}))
|
||||
pd = cast(dict[str, Any], payload if isinstance(payload, dict) else {})
|
||||
return c.response_from_kitty(self, self_window, PayloadGetter(c, pd))
|
||||
except Exception as e:
|
||||
if silent:
|
||||
log_error(f'Failed to run remote_control mapping: {aa} with error: {e}')
|
||||
@@ -3169,7 +3171,7 @@ class Boss:
|
||||
if theme_colors.has_applied_theme:
|
||||
theme_colors.refresh()
|
||||
if theme_colors.has_applied_theme: # in case the theme file was deleted
|
||||
assert theme_colors.applied_theme # to make mypy happy
|
||||
assert theme_colors.applied_theme # to make type check happy
|
||||
theme_colors.apply_theme(theme_colors.applied_theme, notify_on_bg_change=False)
|
||||
for w in self.all_windows:
|
||||
if w.screen.color_profile.default_bg != bg_colors_before.get(w.id):
|
||||
@@ -3302,7 +3304,8 @@ class Boss:
|
||||
except (Exception, SystemExit) as err:
|
||||
self.show_error('Failed to set colors', str(err))
|
||||
return
|
||||
c.response_from_kitty(self, self.window_for_dispatch or self.active_window, PayloadGetter(c, payload if isinstance(payload, dict) else {}))
|
||||
pd = cast(dict[str, Any], payload if isinstance(payload, dict) else {})
|
||||
c.response_from_kitty(self, self.window_for_dispatch or self.active_window, PayloadGetter(c, pd))
|
||||
|
||||
def _move_window_to(
|
||||
self,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
from collections.abc import Callable, Iterator, Sequence
|
||||
from re import Match
|
||||
from typing import Any, NoReturn, TypeVar, cast
|
||||
@@ -134,6 +135,7 @@ role_map: dict[str, Callable[[str], str]] = {}
|
||||
|
||||
|
||||
def role(func: Callable[[str], str]) -> Callable[[str], str]:
|
||||
assert isinstance(func, types.FunctionType)
|
||||
role_map[func.__name__] = func
|
||||
return func
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class Clipboard:
|
||||
|
||||
def set_mime(self, data: Mapping[str, DataType]) -> None:
|
||||
if self.enabled and isinstance(data, dict):
|
||||
self.data = data
|
||||
self.data = dict(data)
|
||||
set_clipboard_data_types(self.clipboard_type, tuple(self.data))
|
||||
|
||||
def get_text(self) -> str:
|
||||
@@ -147,7 +147,7 @@ class Clipboard:
|
||||
def __call__(self, mime: str) -> Callable[[], bytes]:
|
||||
data = self.data.get(mime, b'')
|
||||
if isinstance(data, str):
|
||||
data = data.encode('utf-8') # type: ignore
|
||||
data = data.encode('utf-8')
|
||||
if isinstance(data, bytes):
|
||||
def chunker() -> bytes:
|
||||
nonlocal data
|
||||
|
||||
@@ -5,7 +5,7 @@ import os
|
||||
from collections.abc import Iterable, Sequence
|
||||
from contextlib import suppress
|
||||
from enum import Enum
|
||||
from typing import Any, Literal, Optional, TypedDict
|
||||
from typing import Any, Literal, Optional, TypedDict, cast
|
||||
|
||||
from .config import parse_config
|
||||
from .constants import config_dir
|
||||
@@ -75,13 +75,14 @@ class ThemeColors:
|
||||
|
||||
def refresh(self) -> bool:
|
||||
found = False
|
||||
d: BackgroundImageOptions
|
||||
with suppress(FileNotFoundError):
|
||||
for x in os.scandir(config_dir):
|
||||
if x.name == ThemeFile.dark.value:
|
||||
mtime = x.stat().st_mtime_ns
|
||||
if mtime > self.dark_mtime:
|
||||
with open(x.path) as f:
|
||||
d: BackgroundImageOptions = {}
|
||||
d = {}
|
||||
self.dark_spec, self.dark_tbc = self.parse_colors(f, d)
|
||||
self.dark_background_image_options = d
|
||||
self.dark_mtime = mtime
|
||||
@@ -222,9 +223,10 @@ def parse_colors(
|
||||
conf = parse_config(spec)
|
||||
transparent_background_colors = conf.pop('transparent_background_colors', ())
|
||||
if background_image_options is not None:
|
||||
bio: dict[str, Any] = cast(dict[str, Any], background_image_options)
|
||||
for key in BackgroundImageOptions.__optional_keys__:
|
||||
if key in conf:
|
||||
background_image_options.__setitem__(key, conf[key])
|
||||
bio[key] = conf[key]
|
||||
colors.update(conf)
|
||||
for k in nullable_colors:
|
||||
q = colors.pop(k, False)
|
||||
|
||||
@@ -6,6 +6,7 @@ import inspect
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
import types
|
||||
from collections.abc import Callable, Iterator
|
||||
from typing import Any, get_type_hints
|
||||
|
||||
@@ -56,6 +57,7 @@ def generate_class(defn: Definition, loc: str) -> tuple[str, str]:
|
||||
def option_type_data(option: Option | MultiOption) -> tuple[Callable[[Any], Any], str]:
|
||||
func = option.parser_func
|
||||
if func.__module__ == 'builtins':
|
||||
assert isinstance(func, types.FunctionType)
|
||||
return func, func.__name__
|
||||
th = get_type_hints(func)
|
||||
rettype = th['return']
|
||||
@@ -86,6 +88,7 @@ def generate_class(defn: Definition, loc: str) -> tuple[str, str]:
|
||||
if isinstance(option, MultiOption):
|
||||
mval: dict[str, dict[str, Any]] = {'macos': {}, 'linux': {}, '': {}}
|
||||
func, typ = option_type_data(option)
|
||||
assert isinstance(func, types.FunctionType)
|
||||
for val in option:
|
||||
if val.add_to_default:
|
||||
gr = mval[val.only]
|
||||
@@ -114,6 +117,7 @@ def generate_class(defn: Definition, loc: str) -> tuple[str, str]:
|
||||
func = str
|
||||
elif defn.has_color_table and option.is_color_table_color:
|
||||
func, typ = option_type_data(option)
|
||||
assert isinstance(func, types.FunctionType)
|
||||
t(f' ans[{option.name!r}] = {func.__name__}(val)')
|
||||
tc_imports.add((func.__module__, func.__name__))
|
||||
cnum = int(option.name[5:])
|
||||
@@ -128,6 +132,7 @@ def generate_class(defn: Definition, loc: str) -> tuple[str, str]:
|
||||
params = dict(inspect.signature(func).parameters)
|
||||
except Exception:
|
||||
params = {}
|
||||
assert isinstance(func, types.FunctionType)
|
||||
if 'dict_with_parse_results' in params:
|
||||
t(f' {func.__name__}(val, ans)')
|
||||
else:
|
||||
@@ -163,6 +168,7 @@ def generate_class(defn: Definition, loc: str) -> tuple[str, str]:
|
||||
a(f' {option_name}: {typ} = ' '{}')
|
||||
|
||||
for parser, aliases in defn.deprecations.items():
|
||||
assert isinstance(parser, types.FunctionType)
|
||||
for alias in aliases:
|
||||
parser_function_declaration(alias)
|
||||
tc_imports.add((parser.__module__, parser.__name__))
|
||||
@@ -183,6 +189,7 @@ def generate_class(defn: Definition, loc: str) -> tuple[str, str]:
|
||||
for aname, action in defn.actions.items():
|
||||
option_names.add(aname)
|
||||
action_parsers[aname] = func = action.parser_func
|
||||
assert isinstance(func, types.FunctionType)
|
||||
th = get_type_hints(func)
|
||||
rettype = th['return']
|
||||
typ = option_type_as_str(rettype)
|
||||
@@ -487,6 +494,7 @@ def go_type_data(parser_func: ParserFuncType, ctype: str, is_multiple: bool = Fa
|
||||
_, rsep, fsep = ctype.split('_', 2)
|
||||
return 'map[string]string', f'config.ParseStrDict(val, `{rsep}`, `{fsep}`)'
|
||||
return f'*{ctype}', f'Parse{ctype}(val)'
|
||||
assert isinstance(parser_func, types.FunctionType)
|
||||
p = parser_func.__name__
|
||||
if p == 'int':
|
||||
return 'int64', 'strconv.ParseInt(val, 10, 64)'
|
||||
|
||||
@@ -558,7 +558,7 @@ def uniq(vals: Iterable[T]) -> list[T]:
|
||||
|
||||
def save_type_stub(text: str, fpath: str) -> None:
|
||||
fpath += 'i'
|
||||
preamble = '# Update this file by running: ./test.py mypy\n\n'
|
||||
preamble = '# Update this file by running: ./test.py type-check\n\n'
|
||||
try:
|
||||
with open(fpath) as fs:
|
||||
existing = fs.read()
|
||||
|
||||
@@ -82,7 +82,7 @@ def finalize_keys(opts: Options, accumulate_bad_lines: list[BadLine] | None = No
|
||||
defns: list[KeyDefinition] = []
|
||||
for d in opts.map:
|
||||
if d is None: # clear_all_shortcuts
|
||||
defns = [] # type: ignore
|
||||
defns = []
|
||||
else:
|
||||
try:
|
||||
defns.append(d.resolve_and_copy(opts.kitty_mod))
|
||||
@@ -122,7 +122,7 @@ def finalize_mouse_mappings(opts: Options, accumulate_bad_lines: list[BadLine] |
|
||||
defns: list[MouseMapping] = []
|
||||
for d in opts.mouse_map:
|
||||
if d is None: # clear_all_mouse_actions
|
||||
defns = [] # type: ignore
|
||||
defns = []
|
||||
else:
|
||||
try:
|
||||
defns.append(d.resolve_and_copy(opts.kitty_mod))
|
||||
|
||||
@@ -233,27 +233,13 @@ def running_in_kitty(set_val: bool | None = None) -> bool:
|
||||
|
||||
|
||||
def list_kitty_resources(package: str = 'kitty') -> Iterator[str]:
|
||||
try:
|
||||
if sys.version_info[:2] < (3, 10):
|
||||
raise ImportError("importlib.resources.files() doesn't work with frozen builds on python 3.9")
|
||||
from importlib.resources import files
|
||||
except ImportError:
|
||||
from importlib.resources import contents
|
||||
return iter(contents(package))
|
||||
else:
|
||||
return (path.name for path in files(package).iterdir())
|
||||
from importlib.resources import files
|
||||
return (path.name for path in files(package).iterdir())
|
||||
|
||||
|
||||
def read_kitty_resource(name: str, package_name: str = 'kitty') -> bytes:
|
||||
try:
|
||||
if sys.version_info[:2] < (3, 10):
|
||||
raise ImportError("importlib.resources.files() doesn't work with frozen builds on python 3.9")
|
||||
from importlib.resources import files
|
||||
except ImportError:
|
||||
from importlib.resources import read_binary
|
||||
return read_binary(package_name, name)
|
||||
else:
|
||||
return (files(package_name) / name).read_bytes()
|
||||
from importlib.resources import files
|
||||
return (files(package_name) / name).read_bytes()
|
||||
|
||||
|
||||
def website_url(doc_name: str = '', website: str = website_base_url) -> str:
|
||||
|
||||
@@ -11,7 +11,7 @@ from collections.abc import Callable, Iterator, Sequence
|
||||
from contextlib import suppress
|
||||
from functools import partial
|
||||
from pprint import pformat
|
||||
from typing import IO, TypeVar
|
||||
from typing import IO, TYPE_CHECKING, Protocol, cast
|
||||
|
||||
from kittens.tui.operations import colored, styled
|
||||
|
||||
@@ -24,12 +24,15 @@ from .options.types import Options as KittyOpts
|
||||
from .options.types import defaults, secret_options
|
||||
from .options.utils import KeyboardMode, KeyDefinition
|
||||
from .rgb import color_as_sharp, color_from_int
|
||||
from .types import MouseEvent, Shortcut, mod_to_names
|
||||
from .types import Shortcut, mod_to_names
|
||||
from .utils import shlex_split
|
||||
|
||||
AnyEvent = TypeVar('AnyEvent', MouseEvent, Shortcut)
|
||||
Print = Callable[..., None]
|
||||
ShortcutMap = dict[Shortcut, str]
|
||||
if TYPE_CHECKING:
|
||||
from .fast_data_types import FontFace
|
||||
else:
|
||||
FontFace = object
|
||||
|
||||
|
||||
def green(x: str) -> str:
|
||||
@@ -54,9 +57,13 @@ def print_mapping_changes(defns: dict[str, str], changes: set[str], text: str, p
|
||||
for k in sorted(changes):
|
||||
print_event(k, defns[k], print)
|
||||
|
||||
class AnyEvent(Protocol):
|
||||
def human_repr(self, kitty_mod: int = 0) -> str: ...
|
||||
|
||||
def compare_maps(
|
||||
final: dict[AnyEvent, str], final_kitty_mod: int, initial: dict[AnyEvent, str], initial_kitty_mod: int, print: Print, mode_name: str = ''
|
||||
|
||||
def compare_maps[T: AnyEvent](
|
||||
final: dict[T, str], final_kitty_mod: int, initial: dict[T, str],
|
||||
initial_kitty_mod: int, print: Print, mode_name: str = ''
|
||||
) -> None:
|
||||
ei = {k.human_repr(initial_kitty_mod): v for k, v in initial.items()}
|
||||
ef = {k.human_repr(final_kitty_mod): v for k, v in final.items()}
|
||||
@@ -308,7 +315,8 @@ def debug_config(opts: KittyOpts | None = None, global_shortcuts: dict[str, Sing
|
||||
p(green('Fonts:'))
|
||||
for k, font in current_fonts().items():
|
||||
if hasattr(font, 'identify_for_debug'):
|
||||
flines = font.identify_for_debug().splitlines()
|
||||
face: FontFace = cast(FontFace, font)
|
||||
flines = face.identify_for_debug().splitlines()
|
||||
p(yellow(f'{k.rjust(8)}:'), flines[0])
|
||||
for fl in flines[1:]:
|
||||
p(' ' * 9, fl)
|
||||
|
||||
@@ -1087,7 +1087,7 @@ class FileTransmission:
|
||||
q = self.active_receives.get(receive_id)
|
||||
if q is None:
|
||||
return
|
||||
ar = q # for mypy
|
||||
ar = q # for type check
|
||||
while ar.signature_pending_chunks:
|
||||
if self.write_ftc_to_child(ar.signature_pending_chunks[0], use_pending=False):
|
||||
ar.signature_pending_chunks.popleft()
|
||||
@@ -1244,11 +1244,11 @@ class TestFileTransmission(FileTransmission):
|
||||
self.test_responses.append(payload.asdict())
|
||||
return True
|
||||
|
||||
def start_receive(self, aid: str) -> None:
|
||||
self.handle_send_confirmation(self.allow, aid)
|
||||
def start_receive(self, ar_id: str) -> None:
|
||||
self.handle_send_confirmation(self.allow, ar_id)
|
||||
|
||||
def start_send(self, aid: str) -> None:
|
||||
self.handle_receive_confirmation(self.allow, aid)
|
||||
def start_send(self, asd_id: str) -> None:
|
||||
self.handle_receive_confirmation(self.allow, asd_id)
|
||||
|
||||
def callback_after(self, callback: Callable[[int | None], None], timeout: float = 0) -> int | None:
|
||||
callback(None)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from collections.abc import Sequence
|
||||
from enum import Enum, IntEnum, auto
|
||||
from typing import TYPE_CHECKING, Literal, NamedTuple, TypedDict, TypeVar, Union
|
||||
from typing import TYPE_CHECKING, Literal, NamedTuple, TypedDict, Union
|
||||
|
||||
from kitty.fast_data_types import ParsedFontFeature
|
||||
from kitty.types import run_once
|
||||
@@ -205,7 +205,6 @@ class FontSpec(NamedTuple):
|
||||
|
||||
|
||||
Descriptor = Union[FontConfigPattern, CoreTextFont]
|
||||
DescriptorVar = TypeVar('DescriptorVar', FontConfigPattern, CoreTextFont, Descriptor)
|
||||
|
||||
|
||||
class Score(NamedTuple):
|
||||
@@ -215,7 +214,7 @@ class Score(NamedTuple):
|
||||
width_score: int
|
||||
|
||||
|
||||
class Scorer:
|
||||
class Scorer[T]:
|
||||
|
||||
def __init__(self, bold: bool = False, italic: bool = False, monospaced: bool = True, prefer_variable: bool = False) -> None:
|
||||
self.bold = bold
|
||||
@@ -223,7 +222,7 @@ class Scorer:
|
||||
self.monospaced = monospaced
|
||||
self.prefer_variable = prefer_variable
|
||||
|
||||
def sorted_candidates(self, candidates: Sequence[DescriptorVar], dump: bool = False) -> list[DescriptorVar]:
|
||||
def sorted_candidates(self, candidates: Sequence[T], dump: bool = False) -> list[T]:
|
||||
raise NotImplementedError()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Literal, TypedDict, Union
|
||||
import copy
|
||||
from typing import TYPE_CHECKING, Any, Literal, TypedDict, Union, cast
|
||||
|
||||
from kitty.constants import is_macos
|
||||
from kitty.fast_data_types import ParsedFontFeature
|
||||
from kitty.fonts import Descriptor, DescriptorVar, DesignAxis, FontSpec, NamedStyle, Scorer, VariableAxis, VariableData, family_name_to_key
|
||||
from kitty.fonts import Descriptor, DesignAxis, FontSpec, NamedStyle, Scorer, VariableAxis, VariableData, family_name_to_key
|
||||
from kitty.options.types import Options
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from kitty.fast_data_types import CTFace
|
||||
from kitty.fast_data_types import CoreTextFont, CTFace, FontConfigPattern
|
||||
from kitty.fast_data_types import Face as FT_Face
|
||||
|
||||
FontCollectionMapType = Literal['family_map', 'ps_map', 'full_map', 'variable_map']
|
||||
@@ -139,9 +140,9 @@ def get_variable_data_for_face(d: Face) -> VariableData:
|
||||
return ans
|
||||
|
||||
|
||||
def find_best_match_in_candidates(
|
||||
candidates: list[DescriptorVar], scorer: Scorer, is_medium_face: bool, ignore_face: DescriptorVar | None = None
|
||||
) -> DescriptorVar | None:
|
||||
def find_best_match_in_candidates[T](
|
||||
candidates: list[T], scorer: Scorer[T], is_medium_face: bool, ignore_face: T | None = None
|
||||
) -> T | None:
|
||||
if candidates:
|
||||
for x in scorer.sorted_candidates(candidates):
|
||||
if ignore_face is None or x != ignore_face:
|
||||
@@ -154,8 +155,8 @@ def pprint(*a: Any, **kw: Any) -> None:
|
||||
pprint(*a, **kw)
|
||||
|
||||
|
||||
def find_medium_variant(font: DescriptorVar) -> DescriptorVar:
|
||||
font = font.copy()
|
||||
def find_medium_variant[T: (CoreTextFont | FontConfigPattern)](font: T) -> T:
|
||||
font = copy.copy(font)
|
||||
vd = get_variable_data_for_descriptor(font)
|
||||
for i, ns in enumerate(vd['named_styles']):
|
||||
if ns['name'] == 'Regular':
|
||||
@@ -452,7 +453,7 @@ def _get_named_style(axis_map: dict[str, float], vd: VariableData) -> NamedStyle
|
||||
|
||||
def get_named_style(face_or_descriptor: Face | Descriptor) -> NamedStyle | None:
|
||||
if isinstance(face_or_descriptor, dict):
|
||||
d: Descriptor = face_or_descriptor
|
||||
d = cast(Descriptor, face_or_descriptor)
|
||||
vd = get_variable_data_for_descriptor(d)
|
||||
if d['descriptor_type'] == 'fontconfig':
|
||||
ns = d.get('named_style', -1)
|
||||
@@ -466,7 +467,7 @@ def get_named_style(face_or_descriptor: Face | Descriptor) -> NamedStyle | None:
|
||||
else:
|
||||
axis_map = d.get('axis_map', {}).copy()
|
||||
else:
|
||||
face: Face = face_or_descriptor
|
||||
face = cast(Face, face_or_descriptor)
|
||||
vd = get_variable_data_for_face(face)
|
||||
q = face.get_variation()
|
||||
if q is None:
|
||||
@@ -479,7 +480,7 @@ def get_axis_map(face_or_descriptor: Face | Descriptor) -> dict[str, float]:
|
||||
base_axis_map = {}
|
||||
axis_map: dict[str, float] = {}
|
||||
if isinstance(face_or_descriptor, dict):
|
||||
d: Descriptor = face_or_descriptor
|
||||
d = cast(Descriptor, face_or_descriptor)
|
||||
vd = get_variable_data_for_descriptor(d)
|
||||
if d['descriptor_type'] == 'fontconfig':
|
||||
ns = d.get('named_style', -1)
|
||||
@@ -494,7 +495,7 @@ def get_axis_map(face_or_descriptor: Face | Descriptor) -> dict[str, float]:
|
||||
else:
|
||||
axis_map = d.get('axis_map', {}).copy()
|
||||
else:
|
||||
face: Face = face_or_descriptor
|
||||
face = cast(Face, face_or_descriptor)
|
||||
q = face.get_variation()
|
||||
if q is not None:
|
||||
axis_map = q
|
||||
|
||||
@@ -12,7 +12,7 @@ from kitty.fast_data_types import CTFace, coretext_all_fonts
|
||||
from kitty.typing_compat import CoreTextFont
|
||||
from kitty.utils import log_error
|
||||
|
||||
from . import Descriptor, DescriptorVar, ListedFont, Score, Scorer, VariableData, family_name_to_key
|
||||
from . import Descriptor, ListedFont, Score, Scorer, VariableData, family_name_to_key
|
||||
|
||||
attr_map = {(False, False): 'font_family',
|
||||
(True, False): 'bold_font',
|
||||
@@ -113,7 +113,7 @@ def weight_range_for_family(family: str) -> WeightRange:
|
||||
return WeightRange(mini, maxi, medium, bold)
|
||||
|
||||
|
||||
class CTScorer(Scorer):
|
||||
class CTScorer(Scorer[CoreTextFont]):
|
||||
weight_range: WeightRange | None = None
|
||||
|
||||
def score(self, candidate: Descriptor) -> Score:
|
||||
@@ -140,7 +140,7 @@ class CTScorer(Scorer):
|
||||
is_regular_width = not candidate['expanded'] and not candidate['condensed']
|
||||
return Score(variable_score, bold_score + italic_score, monospace_match, 0 if is_regular_width else 1)
|
||||
|
||||
def sorted_candidates(self, candidates: Sequence[DescriptorVar], dump: bool = False) -> list[DescriptorVar]:
|
||||
def sorted_candidates(self, candidates: Sequence[CoreTextFont], dump: bool = False) -> list[CoreTextFont]:
|
||||
self.weight_range = None
|
||||
families = {x['family'] for x in candidates}
|
||||
if len(families) == 1:
|
||||
|
||||
@@ -22,7 +22,7 @@ from kitty.fast_data_types import (
|
||||
from kitty.fast_data_types import fc_match as fc_match_impl
|
||||
from kitty.typing_compat import FontConfigPattern
|
||||
|
||||
from . import Descriptor, DescriptorVar, ListedFont, Score, Scorer, VariableData, family_name_to_key
|
||||
from . import Descriptor, ListedFont, Score, Scorer, VariableData, family_name_to_key
|
||||
|
||||
FontCollectionMapType = Literal['family_map', 'ps_map', 'full_map', 'variable_map']
|
||||
FontMap = dict[FontCollectionMapType, dict[str, list[FontConfigPattern]]]
|
||||
@@ -130,7 +130,7 @@ def weight_range_for_family(family: str) -> WeightRange:
|
||||
|
||||
|
||||
|
||||
class FCScorer(Scorer):
|
||||
class FCScorer(Scorer[FontConfigPattern]):
|
||||
|
||||
weight_range: WeightRange | None = None
|
||||
|
||||
@@ -148,7 +148,7 @@ class FCScorer(Scorer):
|
||||
width_score = abs(candidate['width'] - FC_WIDTH_NORMAL)
|
||||
return Score(variable_score, bold_score / 1000 + italic_score / 110, monospace_match, width_score)
|
||||
|
||||
def sorted_candidates(self, candidates: Sequence[DescriptorVar], dump: bool = False) -> list[DescriptorVar]:
|
||||
def sorted_candidates(self, candidates: Sequence[FontConfigPattern], dump: bool = False) -> list[FontConfigPattern]:
|
||||
self.weight_range = None
|
||||
families = {x['family'] for x in candidates}
|
||||
if len(families) == 1:
|
||||
|
||||
@@ -100,7 +100,7 @@ def guess_type(path: str, allow_filesystem_access: bool = False) -> str | None:
|
||||
mt = mt or is_special_file(path)
|
||||
if not mt:
|
||||
if is_dir:
|
||||
mt = 'inode/directory' # type: ignore
|
||||
mt = 'inode/directory'
|
||||
elif is_exe:
|
||||
mt = 'inode/executable'
|
||||
return mt
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections.abc import Callable, Generator, Iterable, Iterator, Sequence
|
||||
from enum import Enum
|
||||
from functools import partial
|
||||
from itertools import repeat
|
||||
from typing import Any, ClassVar, NamedTuple
|
||||
from typing import Any, ClassVar, NamedTuple, cast
|
||||
|
||||
from kitty.borders import BorderColor
|
||||
from kitty.fast_data_types import BOTTOM_EDGE, RIGHT_EDGE, Region, get_options, set_active_window, viewport_for_window
|
||||
@@ -92,7 +92,8 @@ def calculate_cells_map(
|
||||
number_of_windows: int, number_of_cells: int
|
||||
) -> list[int]:
|
||||
if isinstance(bias, dict):
|
||||
bias = convert_bias_map(bias, number_of_windows, number_of_cells)
|
||||
b: dict[int, float] = cast(dict[int, float], bias)
|
||||
bias = convert_bias_map(b, number_of_windows, number_of_cells)
|
||||
cells_per_window = number_of_cells // number_of_windows
|
||||
if bias is not None and number_of_windows > 1 and number_of_windows == len(bias) and cells_per_window > 5:
|
||||
cells_map = [int(b * number_of_cells) for b in bias]
|
||||
|
||||
@@ -93,10 +93,10 @@ class Grid(Layout):
|
||||
b[bias_idx] = increment
|
||||
return tuple(self.variable_layout(layout_func, num_windows, b)) == before_layout
|
||||
|
||||
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
def apply_bias(self, window_id: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
num_windows = all_windows.num_groups
|
||||
ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)
|
||||
row_num, col_num = self.position_for_window_idx(idx, num_windows, ncols, nrows, special_rows, special_col)
|
||||
row_num, col_num = self.position_for_window_idx(window_id, num_windows, ncols, nrows, special_rows, special_col)
|
||||
|
||||
if is_horizontal:
|
||||
b = self.biased_cols
|
||||
@@ -151,13 +151,13 @@ class Grid(Layout):
|
||||
pos += rows
|
||||
on_col_done(col_windows)
|
||||
|
||||
def do_layout(self, all_windows: WindowList) -> None:
|
||||
n = all_windows.num_groups
|
||||
def do_layout(self, windows: WindowList) -> None:
|
||||
n = windows.num_groups
|
||||
if n == 1:
|
||||
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
|
||||
self.layout_single_window_group(next(windows.iter_all_layoutable_groups()))
|
||||
return
|
||||
ncols, nrows, special_rows, special_col = calc_grid_size(n)
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||
groups = tuple(windows.iter_all_layoutable_groups())
|
||||
win_col_map: list[list[WindowGroup]] = []
|
||||
|
||||
def on_col_done(col_windows: list[int]) -> None:
|
||||
@@ -193,17 +193,17 @@ class Grid(Layout):
|
||||
n, nrows, ncols, special_rows, special_col, on_col_done):
|
||||
position_window_in_grid_cell(window_idx, xl, yl)
|
||||
|
||||
def minimal_borders(self, all_windows: WindowList) -> Iterator[BorderLine]:
|
||||
n = all_windows.num_groups
|
||||
def minimal_borders(self, windows: WindowList) -> Iterator[BorderLine]:
|
||||
n = windows.num_groups
|
||||
if not lgd.draw_minimal_borders or n < 2:
|
||||
return
|
||||
needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)
|
||||
needs_borders_map = windows.compute_needs_borders_map(lgd.draw_active_borders)
|
||||
ncols, nrows, special_rows, special_col = calc_grid_size(n)
|
||||
is_first_row: set[int] = set()
|
||||
is_last_row: set[int] = set()
|
||||
is_first_column: set[int] = set()
|
||||
is_last_column: set[int] = set()
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||
groups = tuple(windows.iter_all_layoutable_groups())
|
||||
bw = groups[0].effective_border()
|
||||
if not bw:
|
||||
return
|
||||
@@ -227,7 +227,7 @@ class Grid(Layout):
|
||||
all_groups_in_order.append(wg)
|
||||
layout_data_map[wg.id] = xl, yl
|
||||
is_last_column = {groups[x].id for x in prev_col_windows}
|
||||
active_group = all_windows.active_group
|
||||
active_group = windows.active_group
|
||||
|
||||
def ends(yl: LayoutData) -> tuple[int, int]:
|
||||
return yl.content_pos - yl.space_before, yl.content_pos + yl.content_size + yl.space_after
|
||||
@@ -258,17 +258,17 @@ class Grid(Layout):
|
||||
wid = wg.active_window_id
|
||||
yield from borders_for_window(wg.id, color, wid)
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:
|
||||
n = all_windows.num_groups
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
n = windows.num_groups
|
||||
if n < 4:
|
||||
return neighbors_for_tall_window(1, window, all_windows)
|
||||
return neighbors_for_tall_window(1, window, windows)
|
||||
|
||||
wg = all_windows.group_for_window(window)
|
||||
wg = windows.group_for_window(window)
|
||||
assert wg is not None
|
||||
ncols, nrows, special_rows, special_col = calc_grid_size(n)
|
||||
blank_row: list[int | None] = [None for i in range(ncols)]
|
||||
matrix = tuple(blank_row[:] for j in range(max(nrows, special_rows)))
|
||||
wi = all_windows.iter_all_layoutable_groups()
|
||||
wi = windows.iter_all_layoutable_groups()
|
||||
pos_map: dict[int, tuple[int, int]] = {}
|
||||
col_counts: list[int] = []
|
||||
for col in range(ncols):
|
||||
|
||||
@@ -576,7 +576,7 @@ class SplitsLayoutOpts(LayoutOpts):
|
||||
class Splits(Layout):
|
||||
name = 'splits'
|
||||
needs_all_windows = True
|
||||
layout_opts = SplitsLayoutOpts({})
|
||||
layout_opts: SplitsLayoutOpts = SplitsLayoutOpts({})
|
||||
no_minimal_window_borders = True
|
||||
drag_overlay_mode = DragOverlayMode.free
|
||||
|
||||
@@ -608,8 +608,8 @@ class Splits(Layout):
|
||||
if isinstance(q, Pair):
|
||||
self.pairs_root = q
|
||||
|
||||
def do_layout(self, all_windows: WindowList) -> None:
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||
def do_layout(self, windows: WindowList) -> None:
|
||||
groups = tuple(windows.iter_all_layoutable_groups())
|
||||
root = self.pairs_root
|
||||
all_present_group_ids = {g.id for g in groups}
|
||||
already_placed_group_ids = frozenset(root.all_window_ids())
|
||||
@@ -700,19 +700,19 @@ class Splits(Layout):
|
||||
return self.equalize_biases()
|
||||
return False
|
||||
|
||||
def minimal_borders(self, all_windows: WindowList) -> Iterator[BorderLine]:
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||
def minimal_borders(self, windows: WindowList) -> Iterator[BorderLine]:
|
||||
groups = tuple(windows.iter_all_layoutable_groups())
|
||||
window_count = len(groups)
|
||||
if not lgd.draw_minimal_borders or window_count < 2:
|
||||
return
|
||||
needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)
|
||||
ag = all_windows.active_group
|
||||
needs_borders_map = windows.compute_needs_borders_map(lgd.draw_active_borders)
|
||||
ag = windows.active_group
|
||||
active_group_id = -1 if ag is None else ag.id
|
||||
|
||||
border_color_map = {}
|
||||
for grp_id, needs_borders in needs_borders_map.items():
|
||||
if needs_borders:
|
||||
wid = g.active_window_id if (g := all_windows.group_for_id(grp_id)) else 0
|
||||
wid = g.active_window_id if (g := windows.group_for_id(grp_id)) else 0
|
||||
if wid:
|
||||
color = BorderColor.active if grp_id is active_group_id else BorderColor.bell
|
||||
border_color_map[wid] = color
|
||||
@@ -723,13 +723,13 @@ class Splits(Layout):
|
||||
for bb in which:
|
||||
yield bb._replace(color=border_color_map.get(abs(bb.window_id), BorderColor.inactive))
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:
|
||||
wg = all_windows.group_for_window(window)
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
wg = windows.group_for_window(window)
|
||||
assert wg is not None
|
||||
pair = self.pairs_root.pair_for_window(wg.id)
|
||||
ans: NeighborsMap = {}
|
||||
if pair is not None:
|
||||
pair.neighbors_for_window(wg.id, ans, self, all_windows)
|
||||
pair.neighbors_for_window(wg.id, ans, self, windows)
|
||||
return ans
|
||||
|
||||
def move_window(self, all_windows: WindowList, delta: int = 1) -> bool:
|
||||
@@ -886,9 +886,9 @@ class Splits(Layout):
|
||||
|
||||
return None
|
||||
|
||||
def drag_resize_window(self, all_windows: WindowList, pair_id: int, increment: float, is_horizontal: bool = True) -> bool:
|
||||
def drag_resize_window(self, all_windows: WindowList, window_id: int, increment: float, is_horizontal: bool = True) -> bool:
|
||||
for pair in self.pairs_root.self_and_descendants():
|
||||
if id(pair) == pair_id:
|
||||
if id(pair) == window_id:
|
||||
new_bias = max(0, min(pair.bias + increment, 1))
|
||||
if new_bias != pair.bias:
|
||||
pair.bias = new_bias
|
||||
|
||||
@@ -14,15 +14,15 @@ class Stack(Layout):
|
||||
needs_window_borders = False
|
||||
only_active_window_visible = True
|
||||
|
||||
def do_layout(self, all_windows: WindowList) -> None:
|
||||
active_group = all_windows.active_group
|
||||
for group in all_windows.iter_all_layoutable_groups():
|
||||
def do_layout(self, windows: WindowList) -> None:
|
||||
active_group = windows.active_group
|
||||
for group in windows.iter_all_layoutable_groups():
|
||||
self.layout_single_window_group(group, add_blank_rects=group is active_group)
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:
|
||||
wg = all_windows.group_for_window(window)
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
wg = windows.group_for_window(window)
|
||||
assert wg is not None
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||
groups = tuple(windows.iter_all_layoutable_groups())
|
||||
idx = groups.index(wg)
|
||||
before = [] if wg is groups[0] else [groups[idx-1].id]
|
||||
after = [] if wg is groups[-1] else [groups[idx+1].id]
|
||||
|
||||
@@ -138,7 +138,7 @@ class Tall(Layout):
|
||||
main_is_horizontal = True
|
||||
no_minimal_window_borders = True
|
||||
drag_overlay_mode = DragOverlayMode.axis_y
|
||||
layout_opts = TallLayoutOpts({})
|
||||
layout_opts: TallLayoutOpts = TallLayoutOpts({})
|
||||
main_axis_layout = Layout.xlayout
|
||||
perp_axis_layout = Layout.ylayout
|
||||
|
||||
@@ -167,12 +167,12 @@ class Tall(Layout):
|
||||
after_layout = tuple(self.variable_layout(all_windows, self.biased_map))
|
||||
return before_layout == after_layout
|
||||
|
||||
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
def apply_bias(self, window_id: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
num_windows = all_windows.num_groups
|
||||
if self.main_is_horizontal == is_horizontal:
|
||||
before_main_bias = self.main_bias
|
||||
ncols = self.num_full_size_windows + 1
|
||||
biased_col = idx if idx < self.num_full_size_windows else (ncols - 1)
|
||||
biased_col = window_id if window_id < self.num_full_size_windows else (ncols - 1)
|
||||
self.main_bias = [
|
||||
safe_increment_bias(self.main_bias[i], increment * (1 if i == biased_col else -1)) for i in range(ncols)
|
||||
]
|
||||
@@ -180,13 +180,13 @@ class Tall(Layout):
|
||||
return self.main_bias != before_main_bias
|
||||
|
||||
num_of_short_windows = num_windows - self.num_full_size_windows
|
||||
if idx < self.num_full_size_windows or num_of_short_windows < 2:
|
||||
if window_id < self.num_full_size_windows or num_of_short_windows < 2:
|
||||
return False
|
||||
idx -= self.num_full_size_windows
|
||||
window_id -= self.num_full_size_windows
|
||||
before_layout = tuple(self.variable_layout(all_windows, self.biased_map))
|
||||
before = self.biased_map.get(idx, 0.)
|
||||
before = self.biased_map.get(window_id, 0.)
|
||||
candidate = self.biased_map.copy()
|
||||
candidate[idx] = after = before + increment
|
||||
candidate[window_id] = after = before + increment
|
||||
if before_layout == tuple(self.variable_layout(all_windows, candidate)):
|
||||
return False
|
||||
self.biased_map = candidate
|
||||
@@ -251,12 +251,12 @@ class Tall(Layout):
|
||||
xl, yl = yl, xl
|
||||
yield wg, xl, yl, False
|
||||
|
||||
def do_layout(self, all_windows: WindowList) -> None:
|
||||
num = all_windows.num_groups
|
||||
def do_layout(self, windows: WindowList) -> None:
|
||||
num = windows.num_groups
|
||||
if num == 1:
|
||||
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
|
||||
self.layout_single_window_group(next(windows.iter_all_layoutable_groups()))
|
||||
return
|
||||
layouts = (self.simple_layout if num <= self.num_full_size_windows + 1 else self.full_layout)(all_windows)
|
||||
layouts = (self.simple_layout if num <= self.num_full_size_windows + 1 else self.full_layout)(windows)
|
||||
for wg, xl, yl, is_full_size in layouts:
|
||||
self.set_window_group_geometry(wg, xl, yl)
|
||||
|
||||
@@ -303,25 +303,25 @@ class Tall(Layout):
|
||||
return False
|
||||
return None
|
||||
|
||||
def minimal_borders(self, all_windows: WindowList) -> Iterator[BorderLine]:
|
||||
num = all_windows.num_groups
|
||||
def minimal_borders(self, windows: WindowList) -> Iterator[BorderLine]:
|
||||
num = windows.num_groups
|
||||
if num < 2 or not lgd.draw_minimal_borders:
|
||||
return
|
||||
try:
|
||||
bw = next(all_windows.iter_all_layoutable_groups()).effective_border()
|
||||
bw = next(windows.iter_all_layoutable_groups()).effective_border()
|
||||
except StopIteration:
|
||||
bw = 0
|
||||
if not bw:
|
||||
return
|
||||
if num <= self.num_full_size_windows + 1:
|
||||
layout = (x[:3] for x in self.simple_layout(all_windows))
|
||||
yield from borders(layout, self.main_is_horizontal, all_windows)
|
||||
layout = (x[:3] for x in self.simple_layout(windows))
|
||||
yield from borders(layout, self.main_is_horizontal, windows)
|
||||
return
|
||||
main_layouts: list[tuple[WindowGroup, LayoutData, LayoutData]] = []
|
||||
perp_borders: list[BorderLine] = []
|
||||
layouts = (self.simple_layout if num <= self.num_full_size_windows else self.full_layout)(all_windows)
|
||||
needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)
|
||||
active_group = all_windows.active_group
|
||||
layouts = (self.simple_layout if num <= self.num_full_size_windows else self.full_layout)(windows)
|
||||
needs_borders_map = windows.compute_needs_borders_map(lgd.draw_active_borders)
|
||||
active_group = windows.active_group
|
||||
mirrored = self.layout_opts.mirrored
|
||||
for wg, xl, yl, is_full_size in layouts:
|
||||
if is_full_size:
|
||||
@@ -356,7 +356,7 @@ class Tall(Layout):
|
||||
|
||||
mirrored = self.layout_opts.mirrored
|
||||
yield from borders(
|
||||
main_layouts, self.main_is_horizontal, all_windows,
|
||||
main_layouts, self.main_is_horizontal, windows,
|
||||
start_offset=int(not mirrored), end_offset=int(mirrored)
|
||||
)
|
||||
yield from perp_borders[1:-1]
|
||||
|
||||
@@ -80,7 +80,7 @@ class Vertical(Layout):
|
||||
self.biased_map: dict[int, float] = {}
|
||||
return True
|
||||
|
||||
def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
def apply_bias(self, window_id: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:
|
||||
if self.main_is_horizontal != is_horizontal:
|
||||
return False
|
||||
num_windows = all_windows.num_groups
|
||||
@@ -88,8 +88,8 @@ class Vertical(Layout):
|
||||
return False
|
||||
before_layout = list(self.variable_layout(all_windows, self.biased_map))
|
||||
candidate = self.biased_map.copy()
|
||||
before = candidate.get(idx, 0)
|
||||
candidate[idx] = before + increment
|
||||
before = candidate.get(window_id, 0)
|
||||
candidate[window_id] = before + increment
|
||||
if before_layout == list(self.variable_layout(all_windows, candidate)):
|
||||
return False
|
||||
self.biased_map = candidate
|
||||
@@ -109,24 +109,24 @@ class Vertical(Layout):
|
||||
xl, yl = yl, xl
|
||||
yield wg, xl, yl
|
||||
|
||||
def do_layout(self, all_windows: WindowList) -> None:
|
||||
window_count = all_windows.num_groups
|
||||
def do_layout(self, windows: WindowList) -> None:
|
||||
window_count = windows.num_groups
|
||||
if window_count == 1:
|
||||
self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))
|
||||
self.layout_single_window_group(next(windows.iter_all_layoutable_groups()))
|
||||
return
|
||||
for wg, xl, yl in self.generate_layout_data(all_windows):
|
||||
for wg, xl, yl in self.generate_layout_data(windows):
|
||||
self.set_window_group_geometry(wg, xl, yl)
|
||||
|
||||
def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:
|
||||
window_count = all_windows.num_groups
|
||||
def minimal_borders(self, windows: WindowList) -> Generator[BorderLine, None, None]:
|
||||
window_count = windows.num_groups
|
||||
if window_count < 2 or not lgd.draw_minimal_borders:
|
||||
return
|
||||
yield from borders(self.generate_layout_data(all_windows), self.main_is_horizontal, all_windows)
|
||||
yield from borders(self.generate_layout_data(windows), self.main_is_horizontal, windows)
|
||||
|
||||
def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:
|
||||
wg = all_windows.group_for_window(window)
|
||||
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:
|
||||
wg = windows.group_for_window(window)
|
||||
assert wg is not None
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups())
|
||||
groups = tuple(windows.iter_all_layoutable_groups())
|
||||
idx = groups.index(wg)
|
||||
lg = len(groups)
|
||||
if lg > 1:
|
||||
|
||||
@@ -606,7 +606,6 @@ def kitty_main(called_from_panel: bool = False) -> None:
|
||||
|
||||
|
||||
def main(called_from_panel: bool = False) -> None:
|
||||
global redirected_for_quick_access
|
||||
try:
|
||||
if is_macos and launched_by_launch_services and not called_from_panel:
|
||||
with suppress(OSError):
|
||||
|
||||
@@ -6,31 +6,17 @@
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
from collections.abc import Callable, Sequence
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
from multiprocessing import context, get_all_start_methods, get_context, spawn, util
|
||||
from typing import TYPE_CHECKING, Any, Union
|
||||
from typing import Any
|
||||
|
||||
from .constants import kitty_exe
|
||||
|
||||
orig_spawn_passfds = util.spawnv_passfds
|
||||
orig_executable = spawn.get_executable()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Buffer
|
||||
from typing import SupportsIndex, SupportsInt
|
||||
|
||||
if sys.version_info[:2] >= (3, 14):
|
||||
ArgsType = Sequence[Union[str, Buffer, SupportsInt, SupportsIndex]]
|
||||
else:
|
||||
from _typeshed import ReadableBuffer, SupportsTrunc
|
||||
ArgsType = Sequence[Union[str, ReadableBuffer, SupportsInt, SupportsIndex, SupportsTrunc]]
|
||||
else:
|
||||
ArgsType = Sequence[str]
|
||||
|
||||
|
||||
def spawnv_passfds(path: bytes, args: ArgsType, passfds: Sequence[int]) -> int:
|
||||
def spawnv_passfds(path: bytes, args: list[str], passfds: Sequence[int]) -> int:
|
||||
if '-c' in args:
|
||||
idx = args.index('-c')
|
||||
patched_args = [spawn.get_executable(), '+runpy'] + list(args)[idx + 1:]
|
||||
@@ -45,7 +31,7 @@ def spawnv_passfds(path: bytes, args: ArgsType, passfds: Sequence[int]) -> int:
|
||||
def monkey_patch_multiprocessing() -> None:
|
||||
# Use kitty to run the worker process used by multiprocessing
|
||||
spawn.set_executable(kitty_exe())
|
||||
util.spawnv_passfds = spawnv_passfds
|
||||
util.spawnv_passfds = spawnv_passfds # type: ignore
|
||||
|
||||
|
||||
def unmonkey_patch_multiprocessing() -> None:
|
||||
|
||||
@@ -456,8 +456,9 @@ class NotificationCommand:
|
||||
from .search_query_parser import search
|
||||
def get_matches(location: str, query: str, candidates: set['NotificationCommand']) -> set['NotificationCommand']:
|
||||
return {x for x in candidates if x.matches_rule_item(location, query)}
|
||||
universal_set: set['NotificationCommand'] = {self}
|
||||
try:
|
||||
return self in search(rule, ('title', 'body', 'app', 'type'), {self}, get_matches)
|
||||
return self in search(rule, ('title', 'body', 'app', 'type'), universal_set, get_matches)
|
||||
except Exception as e:
|
||||
self.log(f'Ignoring invalid filter_notification rule: {rule} with error: {e}')
|
||||
return False
|
||||
@@ -992,7 +993,7 @@ class NotificationManager:
|
||||
self.send_closed_response(channel_id, cmd.identifier, untracked=True)
|
||||
return desktop_notification_id
|
||||
|
||||
def expire_notification(self, desktop_notification_id: int, command_id: int, timer_id: int) -> None:
|
||||
def expire_notification(self, desktop_notification_id: int, command_id: int, timer_id: int | None) -> None:
|
||||
if n := self.in_progress_notification_commands.get(desktop_notification_id):
|
||||
if id(n) == command_id:
|
||||
self.desktop_integration.close_notification(desktop_notification_id)
|
||||
|
||||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
@@ -681,7 +681,7 @@ class Options:
|
||||
tab_separator: str = ' ┇'
|
||||
tab_switch_strategy: choices_for_tab_switch_strategy = 'previous'
|
||||
tab_title_max_length: int = 0
|
||||
tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{tab.last_focused_progress_percent}{title}'
|
||||
tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{secure_input_symbol}{fmt.fg.tab}{tab.last_focused_progress_percent}{title}'
|
||||
term: str = 'xterm-kitty'
|
||||
terminfo_type: choices_for_terminfo_type = 'path'
|
||||
text_composition_strategy: str = 'platform'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window
|
||||
|
||||
@@ -60,13 +60,14 @@ using this option means that you will not be notified of failures.
|
||||
return {'match': opts.match, 'amount': amount, 'self': True}
|
||||
|
||||
def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:
|
||||
amt = payload_get('amount')
|
||||
vamt: tuple[int | float | Literal['start', 'end'], str] = payload_get('amount')
|
||||
for window in self.windows_for_match_payload(boss, window, payload_get):
|
||||
if window:
|
||||
if amt[0] in ('start', 'end'):
|
||||
(window.scroll_home if amt[0] == 'start' else window.scroll_end)()
|
||||
if vamt[0] in ('start', 'end'):
|
||||
(window.scroll_home if vamt[0] == 'start' else window.scroll_end)()
|
||||
else:
|
||||
amt, unit = amt
|
||||
amt, unit = vamt
|
||||
assert isinstance(amt, (int, float))
|
||||
match unit:
|
||||
case 'u':
|
||||
window.screen.reverse_scroll(int(abs(amt)), True)
|
||||
@@ -78,6 +79,7 @@ using this option means that you will not be notified of failures.
|
||||
if not isinstance(amt, int) and not amt.is_integer():
|
||||
amt = round(window.screen.lines * amt)
|
||||
unit = 'line'
|
||||
assert isinstance(amt, int)
|
||||
direction = 'up' if amt < 0 else 'down'
|
||||
func = getattr(window, f'scroll_{unit}_{direction}')
|
||||
for i in range(int(abs(amt))):
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
from collections.abc import Iterable
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, LiteralString
|
||||
|
||||
from .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window
|
||||
|
||||
@@ -36,7 +36,7 @@ def patch_configured_edges(opts: 'Options', s: dict[str, float | None]) -> None:
|
||||
|
||||
|
||||
def parse_spacing_settings(args: Iterable[str]) -> dict[str, float | None]:
|
||||
mapper: dict[str, list[str]] = {}
|
||||
mapper: dict[str, list[LiteralString]] = {}
|
||||
for q in ('margin', 'padding'):
|
||||
mapper[q] = f'{q}-left {q}-top {q}-right {q}-bottom'.split()
|
||||
mapper[f'{q}-h'] = mapper[f'{q}-horizontal'] = f'{q}-left {q}-right'.split()
|
||||
|
||||
@@ -42,14 +42,15 @@ again. If you want to allow other programs to change it afterwards, use this opt
|
||||
return ans
|
||||
|
||||
def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:
|
||||
title = payload_get('title')
|
||||
title: str | None = payload_get('title')
|
||||
btitle: memoryview | None = None
|
||||
if payload_get('temporary') and title is not None:
|
||||
title = memoryview(title.encode('utf-8'))
|
||||
btitle = memoryview(title.encode('utf-8'))
|
||||
for window in self.windows_for_match_payload(boss, window, payload_get):
|
||||
if window:
|
||||
if payload_get('temporary'):
|
||||
window.override_title = None
|
||||
window.title_changed(title)
|
||||
window.title_changed(btitle)
|
||||
else:
|
||||
window.set_title(title)
|
||||
return None
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections.abc import Callable, Iterator, Sequence
|
||||
from enum import Enum
|
||||
from functools import lru_cache
|
||||
from gettext import gettext as _
|
||||
from typing import NamedTuple, TypeVar
|
||||
from typing import NamedTuple, TypeVar, cast
|
||||
|
||||
from .types import run_once
|
||||
|
||||
@@ -121,13 +121,13 @@ class Token(NamedTuple):
|
||||
|
||||
@run_once
|
||||
def lex_scanner() -> Callable[[str], tuple[list[Token], str]]:
|
||||
return getattr(re, 'Scanner')([ # type: ignore
|
||||
return cast(Callable[[str], tuple[list[Token], str]], getattr(re, 'Scanner')([
|
||||
(r'[()]', lambda x, t: Token(TokenType.OPCODE, t)),
|
||||
(r'@.+?:[^")\s]+', lambda x, t: Token(TokenType.WORD, str(t))),
|
||||
(r'[^"()\s]+', lambda x, t: Token(TokenType.WORD, str(t))),
|
||||
(r'".*?((?<!\\)")', lambda x, t: Token(TokenType.QUOTED_WORD, t[1:-1])),
|
||||
(r'\s+', None)
|
||||
], flags=re.DOTALL).scan
|
||||
], flags=re.DOTALL).scan)
|
||||
|
||||
|
||||
@run_once
|
||||
|
||||
@@ -123,9 +123,9 @@ class Session:
|
||||
def add_window(self, cmd: None | str | list[str], expand: Callable[[str], str] = lambda x: x) -> None:
|
||||
from .launch import parse_launch_args
|
||||
needs_expandvars = False
|
||||
if isinstance(cmd, str) and cmd:
|
||||
if isinstance(cmd, str):
|
||||
needs_expandvars = True
|
||||
cmd = list(shlex_split(cmd))
|
||||
cmd = list(shlex_split(cmd)) if cmd else []
|
||||
serialize_data: dict[str, Any] = {'id': 0, 'cmd_at_shell_startup': ()}
|
||||
if cmd and cmd[0].startswith(unserialize_launch_flag):
|
||||
serialize_data = json.loads(cmd[0][len(unserialize_launch_flag):])
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from collections.abc import Callable, Iterator, Mapping, Sequence
|
||||
from enum import Enum
|
||||
from functools import update_wrapper
|
||||
from typing import TYPE_CHECKING, Any, Generic, NamedTuple, TypedDict, TypeVar, Union
|
||||
from typing import TYPE_CHECKING, Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from kitty.fast_data_types import SingleKey
|
||||
@@ -213,11 +213,7 @@ def modmap() -> dict[str, int]:
|
||||
'caps_lock': GLFW_MOD_CAPS_LOCK, 'num_lock': GLFW_MOD_NUM_LOCK}
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Literal
|
||||
ActionGroup = Literal['cp', 'sc', 'win', 'tab', 'fs', 'mouse', 'mk', 'lay', 'misc', 'debug', 'session']
|
||||
else:
|
||||
ActionGroup = str
|
||||
ActionGroup = Literal['cp', 'sc', 'win', 'tab', 'fs', 'mouse', 'mk', 'lay', 'misc', 'debug', 'session']
|
||||
|
||||
|
||||
class ActionSpec(NamedTuple):
|
||||
|
||||
@@ -88,11 +88,6 @@ def filter_tests_by_module(suite: unittest.TestSuite, *names: str) -> unittest.T
|
||||
return filter_tests(suite, q)
|
||||
|
||||
|
||||
@lru_cache
|
||||
def python_for_type_check() -> str:
|
||||
return shutil.which('python') or shutil.which('python3') or 'python'
|
||||
|
||||
|
||||
def type_check() -> NoReturn:
|
||||
from kitty.cli_stub import generate_stub # type:ignore
|
||||
|
||||
@@ -100,8 +95,7 @@ def type_check() -> NoReturn:
|
||||
from kittens.tui.operations_stub import generate_stub # type: ignore
|
||||
|
||||
generate_stub()
|
||||
py = python_for_type_check()
|
||||
os.execlp(py, py, '-m', 'mypy', '--pretty')
|
||||
os.execlp('ty', 'ty', 'check')
|
||||
|
||||
|
||||
def run_cli(suite: unittest.TestSuite, verbosity: int = 4) -> bool:
|
||||
@@ -309,11 +303,9 @@ def env_for_python_tests(report_env: bool = False) -> Iterator[None]:
|
||||
path = os.pathsep.join(x for x in paths if not x.startswith(current_home))
|
||||
launcher_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty', 'launcher')
|
||||
path = f'{launcher_dir}{os.pathsep}{path}'
|
||||
python_for_type_check()
|
||||
print('Running under CI:', BaseTest.is_ci)
|
||||
if report_env:
|
||||
print('Using PATH in test environment:', path)
|
||||
print('Python:', python_for_type_check())
|
||||
from kitty.fast_data_types import has_avx2, has_sse4_2
|
||||
print(f'Intrinsics: {has_avx2=} {has_sse4_2=}')
|
||||
# we need fonts installed in the user home directory as well, so initialize
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[project]
|
||||
requires-python = ">=3.11"
|
||||
requires-python = ">=3.12"
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
[build-system]
|
||||
@@ -10,30 +10,15 @@ requires = [
|
||||
"packaging == 23.1",
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
files = 'kitty,kittens,glfw,*.py,docs/conf.py,gen'
|
||||
no_implicit_optional = true
|
||||
sqlite_cache = true
|
||||
cache_fine_grained = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
warn_unused_configs = true
|
||||
check_untyped_defs = true
|
||||
disallow_untyped_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_incomplete_defs = true
|
||||
strict = true
|
||||
strict_bytes = true
|
||||
no_implicit_reexport = true
|
||||
|
||||
[tool.pylsp-mypy]
|
||||
enabled = true
|
||||
dmypy = true
|
||||
exclude = ['kitty_tests/*']
|
||||
report_progress = true
|
||||
[tool.ty.src]
|
||||
include = [
|
||||
"kitty",
|
||||
"kittens",
|
||||
"glfw",
|
||||
"*.py",
|
||||
"docs/conf.py",
|
||||
"gen"
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 160
|
||||
|
||||
7
setup.py
7
setup.py
@@ -1369,8 +1369,11 @@ def read_bool_options(path: str = 'kitty/cli.py') -> Tuple[str, ...]:
|
||||
|
||||
|
||||
def build_launcher(args: Options, launcher_dir: str = '.', bundle_type: str = 'source') -> str:
|
||||
werror = '' if args.ignore_compiler_warnings else '-pedantic-errors -Werror'
|
||||
cflags = f'-Wall {werror} -fpie {c_std}'.strip().split()
|
||||
cflags = ['-Wall']
|
||||
if not args.ignore_compiler_warnings:
|
||||
cflags.extend(('-pedantic-errors', '-Werror'))
|
||||
if c_std:
|
||||
cflags.append(c_std)
|
||||
cppflags = [define(f'WRAPPED_KITTENS=" {wrapped_kittens()} "')]
|
||||
ldflags = shlex.split(os.environ.get('LDFLAGS', ''))
|
||||
xxhash = xxhash_flags()
|
||||
|
||||
Reference in New Issue
Block a user