more typing work

This commit is contained in:
Kovid Goyal 2020-03-09 13:32:45 +05:30
parent 92827ae647
commit c817ba9eae
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
14 changed files with 163 additions and 81 deletions

View File

@ -7,13 +7,14 @@ import os
import sys import sys
from base64 import standard_b64encode from base64 import standard_b64encode
from collections import defaultdict, deque from collections import defaultdict, deque
from itertools import count
from contextlib import suppress from contextlib import suppress
from itertools import count
from typing import Any, DefaultDict, Deque, Dict, Tuple
from kitty.utils import fit_image from kitty.utils import fit_image
from .operations import cursor
from .handler import ImageManagerBase from .handler import ImageManagerBase
from .operations import cursor
try: try:
fsenc = sys.getfilesystemencoding() or 'utf-8' fsenc = sys.getfilesystemencoding() or 'utf-8'
@ -64,7 +65,7 @@ def run_imagemagick(path, cmd, keep_stdout=True):
def identify(path): def identify(path):
p = run_imagemagick(path, ['identify', '-format', '%m %w %h %A', '--', path]) p = run_imagemagick(path, ['identify', '-format', '%m %w %h %A', '--', path])
parts = tuple(filter(None, p.stdout.decode('utf-8').split())) parts: Tuple[str, ...] = tuple(filter(None, p.stdout.decode('utf-8').split()))
mode = 'rgb' if parts[3].lower() == 'false' else 'rgba' mode = 'rgb' if parts[3].lower() == 'false' else 'rgba'
return ImageData(parts[0].lower(), int(parts[1]), int(parts[2]), mode) return ImageData(parts[0].lower(), int(parts[1]), int(parts[2]), mode)
@ -106,7 +107,7 @@ def can_display_images():
ans = getattr(can_display_images, 'ans', None) ans = getattr(can_display_images, 'ans', None)
if ans is None: if ans is None:
ans = shutil.which('convert') is not None ans = shutil.which('convert') is not None
can_display_images.ans = ans setattr(can_display_images, 'ans', ans)
return ans return ans
@ -123,7 +124,7 @@ class ImageManager(ImageManagerBase):
self.image_id_to_image_data = {} self.image_id_to_image_data = {}
self.image_id_to_converted_data = {} self.image_id_to_converted_data = {}
self.transmission_status = {} self.transmission_status = {}
self.placements_in_flight = defaultdict(deque) self.placements_in_flight: DefaultDict[int, Deque[Dict[str, Any]]] = defaultdict(deque)
@property @property
def next_image_id(self): def next_image_id(self):

View File

@ -31,15 +31,13 @@ if TYPE_CHECKING:
SequenceMap, KeyMap SequenceMap, KeyMap
def parse_shortcut(sc): def parse_shortcut(sc: str) -> Tuple[int, bool, Optional[int]]:
parts = sc.split('+') parts = sc.split('+')
mods = 0 mods = 0
if len(parts) > 1: if len(parts) > 1:
mods = parse_mods(parts[:-1], sc) mods = parse_mods(parts[:-1], sc)
if mods is None: q = parts[-1].upper()
return None, None, None key: Optional[int] = getattr(defines, 'GLFW_KEY_' + key_name_aliases.get(q, q), None)
key = parts[-1].upper()
key = getattr(defines, 'GLFW_KEY_' + key_name_aliases.get(key, key), None)
is_native = False is_native = False
if key is None: if key is None:
q = parts[-1] q = parts[-1]
@ -47,7 +45,7 @@ def parse_shortcut(sc):
with suppress(Exception): with suppress(Exception):
key = int(q, 16) key = int(q, 16)
else: else:
key = get_key_name_lookup()(q) key = get_key_name_lookup()(q, False)
is_native = key is not None is_native = key is not None
return mods, is_native, key return mods, is_native, key
@ -369,7 +367,7 @@ def parse_key(val, key_definitions):
return return
is_sequence = sequence_sep in sc is_sequence = sequence_sep in sc
if is_sequence: if is_sequence:
trigger = None trigger: Optional[Tuple[int, bool, int]] = None
restl: List[Tuple[int, bool, int]] = [] restl: List[Tuple[int, bool, int]] = []
for part in sc.split(sequence_sep): for part in sc.split(sequence_sep):
mods, is_native, key = parse_shortcut(part) mods, is_native, key = parse_shortcut(part)
@ -484,8 +482,8 @@ def handle_symbol_map(key, val, ans):
class FontFeature(str): class FontFeature(str):
def __new__(cls, name, parsed): def __new__(cls, name: str, parsed: bytes):
ans = str.__new__(cls, name) ans = str.__new__(cls, name) # type: ignore
ans.parsed = parsed ans.parsed = parsed
return ans return ans
@ -531,7 +529,7 @@ def handle_clear_all_shortcuts(key, val, ans):
@deprecated_handler('x11_hide_window_decorations', 'macos_hide_titlebar') @deprecated_handler('x11_hide_window_decorations', 'macos_hide_titlebar')
def handle_deprecated_hide_window_decorations_aliases(key, val, ans): def handle_deprecated_hide_window_decorations_aliases(key, val, ans):
if not hasattr(handle_deprecated_hide_window_decorations_aliases, key): if not hasattr(handle_deprecated_hide_window_decorations_aliases, key):
handle_deprecated_hide_window_decorations_aliases.key = True setattr(handle_deprecated_hide_window_decorations_aliases, 'key', True)
log_error('The option {} is deprecated. Use hide_window_decorations instead.'.format(key)) log_error('The option {} is deprecated. Use hide_window_decorations instead.'.format(key))
if to_bool(val): if to_bool(val):
if is_macos and key == 'macos_hide_titlebar' or (not is_macos and key == 'x11_hide_window_decorations'): if is_macos and key == 'macos_hide_titlebar' or (not is_macos and key == 'x11_hide_window_decorations'):
@ -541,7 +539,7 @@ def handle_deprecated_hide_window_decorations_aliases(key, val, ans):
@deprecated_handler('macos_show_window_title_in_menubar') @deprecated_handler('macos_show_window_title_in_menubar')
def handle_deprecated_macos_show_window_title_in_menubar_alias(key, val, ans): def handle_deprecated_macos_show_window_title_in_menubar_alias(key, val, ans):
if not hasattr(handle_deprecated_macos_show_window_title_in_menubar_alias, key): if not hasattr(handle_deprecated_macos_show_window_title_in_menubar_alias, key):
handle_deprecated_macos_show_window_title_in_menubar_alias.key = True setattr(handle_deprecated_macos_show_window_title_in_menubar_alias, 'key', True)
log_error('The option {} is deprecated. Use macos_show_window_title_in menubar instead.'.format(key)) log_error('The option {} is deprecated. Use macos_show_window_title_in menubar instead.'.format(key))
macos_show_window_title_in = ans.get('macos_show_window_title_in', 'all') macos_show_window_title_in = ans.get('macos_show_window_title_in', 'all')
if to_bool(val): if to_bool(val):

View File

@ -28,7 +28,7 @@ mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '⌘': 'SUPER',
'': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'} '': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'}
def parse_mods(parts, sc): def parse_mods(parts: Iterable[str], sc: str) -> int:
def map_mod(m): def map_mod(m):
return mod_map.get(m, m) return mod_map.get(m, m)

View File

@ -4,6 +4,7 @@ from typing import (
) )
from kitty.boss import Boss from kitty.boss import Boss
from kitty.fonts.render import FontObject
from kitty.options_stub import Options from kitty.options_stub import Options
# Constants {{{ # Constants {{{
@ -420,7 +421,20 @@ def fc_match(
pass pass
def coretext_all_fonts() -> Tuple[Dict[str, Any], ...]: class CoreTextFont(TypedDict):
path: str
postscript_name: str
family: str
style: str
bold: bool
italic: bool
monospace: bool
weight: float
width: float
traits: int
def coretext_all_fonts() -> Tuple[CoreTextFont, ...]:
pass pass
@ -635,6 +649,14 @@ class ColorProfile:
def reset_color(self, num: int) -> None: def reset_color(self, num: int) -> None:
pass pass
def update_ansi_color_table(self, val: List[int]) -> None:
pass
def set_configured_colors(
self, fg: int, bg: int, cursor: int = 0, cursor_text: int = 0, cursor_text_uses_bg: int = 0, highlight_fg: int = 0, highlight_bg: int = 0
) -> None:
pass
def patch_color_profiles( def patch_color_profiles(
spec: Dict[str, int], cursor_text_color: Optional[Union[bool, int]], spec: Dict[str, int], cursor_text_color: Optional[Union[bool, int]],
@ -866,11 +888,11 @@ def set_font_data(
Tuple[int, Union[bytearray, bytes]]], Tuple[int, Union[bytearray, bytes]]],
prerender_func: Callable[ prerender_func: Callable[
[int, int, int, int, int, float, float, float, float], [int, int, int, int, int, float, float, float, float],
Tuple[int, ...]], descriptor_for_idx: Callable[[int], Tuple[dict, bool, Tuple[int, ...]],
bool]], descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]],
bold: int, italic: int, bold_italic: int, num_symbol_fonts: int, bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,
symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float, symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float,
font_feature_settings: Dict[str, Tuple[bytes, ...]] font_feature_settings: Dict[str, Tuple[str, ...]]
): ):
pass pass
@ -902,6 +924,10 @@ class LineBuf:
class Cursor: class Cursor:
x: int x: int
y: int y: int
bg: int
fg: int
bold: bool
italic: bool
class Screen: class Screen:
@ -959,6 +985,9 @@ class Screen:
def is_main_linebuf(self) -> bool: def is_main_linebuf(self) -> bool:
pass pass
def erase_in_line(self, mode: int = 0, private: bool = False) -> None:
pass
def scroll(self, amt: int, upwards: bool) -> bool: def scroll(self, amt: int, upwards: bool) -> bool:
pass pass
@ -968,6 +997,9 @@ class Screen:
def clear_selection(self) -> None: def clear_selection(self) -> None:
pass pass
def reset_mode(self, mode: int, private: bool = False) -> None:
pass
def refresh_sprite_positions(self) -> None: def refresh_sprite_positions(self) -> None:
pass pass

View File

@ -3,21 +3,32 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import re import re
from typing import Generator from typing import (
TYPE_CHECKING, Dict, Generator, Iterable, List, Optional, Sequence, Tuple
)
from kitty.fast_data_types import coretext_all_fonts from kitty.fast_data_types import coretext_all_fonts
from kitty.options_stub import Options
from kitty.utils import log_error from kitty.utils import log_error
from . import ListedFont from . import ListedFont
if TYPE_CHECKING:
from kitty.fast_data_types import CoreTextFont
CoreTextFont
attr_map = {(False, False): 'font_family', attr_map = {(False, False): 'font_family',
(True, False): 'bold_font', (True, False): 'bold_font',
(False, True): 'italic_font', (False, True): 'italic_font',
(True, True): 'bold_italic_font'} (True, True): 'bold_italic_font'}
def create_font_map(all_fonts): FontMap = Dict[str, Dict[str, List['CoreTextFont']]]
ans = {'family_map': {}, 'ps_map': {}, 'full_map': {}}
def create_font_map(all_fonts: Iterable['CoreTextFont']) -> FontMap:
ans: FontMap = {'family_map': {}, 'ps_map': {}, 'full_map': {}}
for x in all_fonts: for x in all_fonts:
f = (x['family'] or '').lower() f = (x['family'] or '').lower()
s = (x['style'] or '').lower() s = (x['style'] or '').lower()
@ -28,10 +39,11 @@ def create_font_map(all_fonts):
return ans return ans
def all_fonts_map(): def all_fonts_map() -> FontMap:
ans = getattr(all_fonts_map, 'ans', None) ans: Optional[FontMap] = getattr(all_fonts_map, 'ans', None)
if ans is None: if ans is None:
ans = all_fonts_map.ans = create_font_map(coretext_all_fonts()) ans = create_font_map(coretext_all_fonts())
setattr(all_fonts_map, 'ans', ans)
return ans return ans
@ -44,7 +56,15 @@ def list_fonts() -> Generator[ListedFont, None, None]:
yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': is_mono} yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': is_mono}
def find_best_match(family, bold=False, italic=False): def bi_match(fonts: Sequence['CoreTextFont'], bold: bool, italic: bool) -> 'CoreTextFont':
for b, i in ((bold, italic), (False, False)):
for q in fonts:
if q['bold'] == b and q['italic'] == i:
return q
return fonts[0]
def find_best_match(family: str, bold: bool = False, italic: bool = False) -> 'CoreTextFont':
q = re.sub(r'\s+', ' ', family.lower()) q = re.sub(r'\s+', ' ', family.lower())
font_map = all_fonts_map() font_map = all_fonts_map()
@ -66,16 +86,10 @@ def find_best_match(family, bold=False, italic=False):
# fallback to Menlo # fallback to Menlo
if q not in font_map['family_map']: if q not in font_map['family_map']:
log_error('The font {} was not found, falling back to Menlo'.format(family)) log_error('The font {} was not found, falling back to Menlo'.format(family))
family = 'Menlo' return bi_match(font_map['family_map']['menlo'], bold, italic)
return {
'monospace': True,
'bold': bold,
'italic': italic,
'family': family
}
def resolve_family(f, main_family, bold=False, italic=False): def resolve_family(f: str, main_family: str, bold: bool = False, italic: bool = False) -> str:
if (bold or italic) and f == 'auto': if (bold or italic) and f == 'auto':
f = main_family f = main_family
if f.lower() == 'monospace': if f.lower() == 'monospace':
@ -83,8 +97,8 @@ def resolve_family(f, main_family, bold=False, italic=False):
return f return f
def get_font_files(opts): def get_font_files(opts: Options) -> Dict[str, 'CoreTextFont']:
ans = {} ans: Dict[str, 'CoreTextFont'] = {}
for (bold, italic), attr in attr_map.items(): for (bold, italic), attr in attr_map.items():
face = find_best_match(resolve_family(getattr(opts, attr), opts.font_family, bold, italic), bold, italic) face = find_best_match(resolve_family(getattr(opts, attr), opts.font_family, bold, italic), bold, italic)
key = {(False, False): 'medium', key = {(False, False): 'medium',
@ -93,10 +107,10 @@ def get_font_files(opts):
(True, True): 'bi'}[(bold, italic)] (True, True): 'bi'}[(bold, italic)]
ans[key] = face ans[key] = face
if key == 'medium': if key == 'medium':
get_font_files.medium_family = face['family'] setattr(get_font_files, 'medium_family', face['family'])
return ans return ans
def font_for_family(family): def font_for_family(family: str) -> Tuple['CoreTextFont', bool, bool]:
ans = find_best_match(resolve_family(family, get_font_files.medium_family)) ans = find_best_match(resolve_family(family, getattr(get_font_files, 'medium_family')))
return ans, ans['bold'], ans['italic'] return ans, ans['bold'], ans['italic']

View File

@ -131,6 +131,6 @@ def get_font_files(opts: Options) -> Dict[str, 'FontConfigPattern']:
return ans return ans
def font_for_family(family) -> Tuple['FontConfigPattern', bool, bool]: def font_for_family(family: str) -> Tuple['FontConfigPattern', bool, bool]:
ans = find_best_match(family, monospaced=False) ans = find_best_match(family, monospaced=False)
return ans, ans.get('weight', 0) >= FC_WEIGHT_BOLD, ans.get('slant', FC_SLANT_ROMAN) != FC_SLANT_ROMAN return ans, ans.get('weight', 0) >= FC_WEIGHT_BOLD, ans.get('slant', FC_SLANT_ROMAN) != FC_SLANT_ROMAN

View File

@ -6,7 +6,7 @@ import ctypes
import sys import sys
from functools import partial from functools import partial
from math import ceil, cos, floor, pi from math import ceil, cos, floor, pi
from typing import Any, Dict, List, Optional, Tuple, cast from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, cast
from kitty.config import defaults from kitty.config import defaults
from kitty.constants import is_macos from kitty.constants import is_macos
@ -22,11 +22,30 @@ from kitty.options_stub import Options as OptionsStub
from kitty.utils import log_error from kitty.utils import log_error
if is_macos: if is_macos:
from .core_text import get_font_files, font_for_family from .core_text import get_font_files as get_font_files_coretext, font_for_family as font_for_family_macos
if TYPE_CHECKING:
from .core_text import CoreTextFont
CoreTextFont
else: else:
from .fontconfig import get_font_files, font_for_family from .fontconfig import get_font_files as get_font_files_fontconfig, font_for_family as font_for_family_fontconfig
if TYPE_CHECKING:
from .fontconfig import FontConfigPattern
FontConfigPattern
current_faces: List[Tuple[Any, bool, bool]] = [] FontObject = Union['CoreTextFont', 'FontConfigPattern']
current_faces: List[Tuple[FontObject, bool, bool]] = []
def get_font_files(opts: OptionsStub) -> Dict[str, Any]:
if is_macos:
return get_font_files_coretext(opts)
return get_font_files_fontconfig(opts)
def font_for_family(family: str) -> Tuple[FontObject, bool, bool]:
if is_macos:
return font_for_family_macos(family)
return font_for_family_fontconfig(family)
def coalesce_symbol_maps(maps: Dict[Tuple[int, int], str]) -> Dict[Tuple[int, int], str]: def coalesce_symbol_maps(maps: Dict[Tuple[int, int], str]) -> Dict[Tuple[int, int], str]:
@ -64,11 +83,11 @@ def create_symbol_map(opts: OptionsStub) -> Tuple[Tuple[int, int, int], ...]:
return sm return sm
def descriptor_for_idx(idx): def descriptor_for_idx(idx: int) -> Tuple[FontObject, bool, bool]:
return current_faces[idx] return current_faces[idx]
def dump_faces(ftypes, indices): def dump_faces(ftypes: List[str], indices: Dict[str, int]) -> None:
def face_str(f): def face_str(f):
f = f[0] f = f[0]
if is_macos: if is_macos:
@ -87,7 +106,7 @@ def dump_faces(ftypes, indices):
log_error(face_str(face)) log_error(face_str(face))
def set_font_family(opts=None, override_font_size=None, debug_font_matching=False): def set_font_family(opts: Optional[OptionsStub] = None, override_font_size=None, debug_font_matching=False):
global current_faces global current_faces
opts = opts or defaults opts = opts or defaults
sz = override_font_size or opts.font_size sz = override_font_size or opts.font_size

View File

@ -4,6 +4,7 @@
import sys import sys
from contextlib import suppress from contextlib import suppress
from typing import Callable, Optional
from .constants import is_macos from .constants import is_macos
@ -96,16 +97,18 @@ key_name_aliases = {
'ARROWLEFT': 'LEFT' 'ARROWLEFT': 'LEFT'
} }
LookupFunc = Callable[[str, bool], Optional[int]]
def null_lookup(name, case_sensitive=False):
pass def null_lookup(name: str, case_sensitive: bool = False) -> Optional[int]:
return None
if is_macos: if is_macos:
def get_key_name_lookup(): def get_key_name_lookup() -> LookupFunc:
return null_lookup return null_lookup
else: else:
def load_libxkb_lookup(): def load_libxkb_lookup() -> LookupFunc:
import ctypes import ctypes
for suffix in ('.0', ''): for suffix in ('.0', ''):
with suppress(Exception): with suppress(Exception):
@ -113,7 +116,10 @@ else:
break break
else: else:
from ctypes.util import find_library from ctypes.util import find_library
lib = ctypes.CDLL(find_library('xkbcommon')) lname = find_library('xkbcommon')
if lname is None:
raise RuntimeError('Failed to find libxkbcommon')
lib = ctypes.CDLL(lname)
f = lib.xkb_keysym_from_name f = lib.xkb_keysym_from_name
f.argtypes = [ctypes.c_char_p, ctypes.c_int] f.argtypes = [ctypes.c_char_p, ctypes.c_int]
@ -125,13 +131,13 @@ else:
return xkb_lookup return xkb_lookup
def get_key_name_lookup(): def get_key_name_lookup() -> LookupFunc:
ans = getattr(get_key_name_lookup, 'ans', None) ans: Optional[LookupFunc] = getattr(get_key_name_lookup, 'ans', None)
if ans is None: if ans is None:
try: try:
ans = load_libxkb_lookup() ans = load_libxkb_lookup()
except Exception as e: except Exception as e:
print('Failed to load libxkbcommon.xkb_keysym_from_name with error:', e, file=sys.stderr) print('Failed to load libxkbcommon.xkb_keysym_from_name with error:', e, file=sys.stderr)
ans = null_lookup ans = null_lookup
get_key_name_lookup.ans = ans setattr(get_key_name_lookup, 'ans', ans)
return ans return ans

View File

@ -5,14 +5,14 @@
import os import os
import re import re
from ctypes import POINTER, c_uint, c_void_p, cast from ctypes import POINTER, c_uint, c_void_p, cast
from typing import Callable, Generator, Iterable, Pattern, Tuple, Union, Sequence
from .constants import config_dir from .constants import config_dir
pointer_to_uint = POINTER(c_uint) pointer_to_uint = POINTER(c_uint)
def null_marker(*a): MarkerFunc = Callable[[str, int, int, int], Generator[None, None, None]]
return iter(())
def get_output_variables(left_address, right_address, color_address): def get_output_variables(left_address, right_address, color_address):
@ -23,14 +23,14 @@ def get_output_variables(left_address, right_address, color_address):
) )
def marker_from_regex(expression, color, flags=re.UNICODE): def marker_from_regex(expression: Union[str, Pattern], color: int, flags: int = re.UNICODE) -> MarkerFunc:
color = max(1, min(color, 3)) color = max(1, min(color, 3))
if isinstance(expression, str): if isinstance(expression, str):
pat = re.compile(expression, flags=flags) pat = re.compile(expression, flags=flags)
else: else:
pat = expression pat = expression
def marker(text, left_address, right_address, color_address): def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
left, right, colorv = get_output_variables(left_address, right_address, color_address) left, right, colorv = get_output_variables(left_address, right_address, color_address)
colorv.value = color colorv.value = color
for match in pat.finditer(text): for match in pat.finditer(text):
@ -41,7 +41,7 @@ def marker_from_regex(expression, color, flags=re.UNICODE):
return marker return marker
def marker_from_multiple_regex(regexes, flags=re.UNICODE): def marker_from_multiple_regex(regexes: Iterable[Tuple[int, str]], flags: int = re.UNICODE) -> MarkerFunc:
expr = '' expr = ''
color_map = {} color_map = {}
for i, (color, spec) in enumerate(regexes): for i, (color, spec) in enumerate(regexes):
@ -51,24 +51,24 @@ def marker_from_multiple_regex(regexes, flags=re.UNICODE):
expr = expr[1:] expr = expr[1:]
pat = re.compile(expr, flags=flags) pat = re.compile(expr, flags=flags)
def marker(text, left_address, right_address, color_address): def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
left, right, color = get_output_variables(left_address, right_address, color_address) left, right, color = get_output_variables(left_address, right_address, color_address)
for match in pat.finditer(text): for match in pat.finditer(text):
left.value = match.start() left.value = match.start()
right.value = match.end() - 1 right.value = match.end() - 1
grp = next(k for k, v in match.groupdict().items() if v is not None) grp = next(k for k, v in match.groupdict().items())
color.value = color_map[grp] color.value = color_map[grp]
yield yield
return marker return marker
def marker_from_text(expression, color): def marker_from_text(expression: str, color: int) -> MarkerFunc:
return marker_from_regex(re.escape(expression), color) return marker_from_regex(re.escape(expression), color)
def marker_from_function(func): def marker_from_function(func: Callable[[str], Iterable[Tuple[int, int, int]]]) -> MarkerFunc:
def marker(text, left_address, right_address, color_address): def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
left, right, colorv = get_output_variables(left_address, right_address, color_address) left, right, colorv = get_output_variables(left_address, right_address, color_address)
for (l, r, c) in func(text): for (l, r, c) in func(text):
left.value = l left.value = l
@ -79,13 +79,15 @@ def marker_from_function(func):
return marker return marker
def marker_from_spec(ftype, spec, flags): def marker_from_spec(ftype: str, spec: Union[str, Sequence[Tuple[int, str]]], flags: int) -> MarkerFunc:
if ftype == 'regex': if ftype == 'regex':
assert not isinstance(spec, str)
if len(spec) == 1: if len(spec) == 1:
return marker_from_regex(spec[0][1], spec[0][0], flags=flags) return marker_from_regex(spec[0][1], spec[0][0], flags=flags)
return marker_from_multiple_regex(spec, flags=flags) return marker_from_multiple_regex(spec, flags=flags)
if ftype == 'function': if ftype == 'function':
import runpy import runpy
assert isinstance(spec, str)
path = spec path = spec
if not os.path.isabs(path): if not os.path.isabs(path):
path = os.path.join(config_dir, path) path = os.path.join(config_dir, path)

View File

@ -16,7 +16,8 @@ def generate_stub():
text = as_type_stub( text = as_type_stub(
all_options, all_options,
special_types={ special_types={
'symbol_map': 'typing.Dict[typing.Tuple[int, int], str]' 'symbol_map': 'typing.Dict[typing.Tuple[int, int], str]',
'font_features': 'typing.Dict[str, typing.Tuple[str, ...]]'
}, },
preamble_lines=( preamble_lines=(
'from kitty.config import KeyAction', 'from kitty.config import KeyAction',

View File

@ -43,7 +43,8 @@ If specified detach the tab this command is run in, rather than the active tab.
if not tabs: if not tabs:
raise MatchError(match) raise MatchError(match)
else: else:
tabs = tuple(window.tabref() if payload_get('self') and window and window.tabref() else boss.active_tab) tab = window.tabref()
tabs = tuple(tab if payload_get('self') and window and tab else boss.active_tab)
match = payload_get('target_tab') match = payload_get('target_tab')
kwargs = {} kwargs = {}
if match: if match:

View File

@ -3,7 +3,7 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from collections import namedtuple from collections import namedtuple
from typing import Set from typing import Set, Tuple, Union
from .config import build_ansi_color_table from .config import build_ansi_color_table
from .constants import WindowGeometry from .constants import WindowGeometry
@ -165,7 +165,7 @@ class TabBar:
color_as_int(opts.inactive_tab_foreground), color_as_int(opts.inactive_tab_foreground),
color_as_int(opts.inactive_tab_background) color_as_int(opts.inactive_tab_background)
) )
self.blank_rects = () self.blank_rects: Union[Tuple, Tuple[Rect, Rect]] = ()
sep = opts.tab_separator sep = opts.tab_separator
self.trailing_spaces = self.leading_spaces = 0 self.trailing_spaces = self.leading_spaces = 0
while sep and sep[0] == ' ': while sep and sep[0] == ' ':
@ -218,7 +218,7 @@ class TabBar:
return return
self.cell_width = cell_width self.cell_width = cell_width
s = self.screen s = self.screen
viewport_width = tab_bar.width - 2 * self.margin_width viewport_width = int(tab_bar.width - 2 * self.margin_width)
ncells = viewport_width // cell_width ncells = viewport_width // cell_width
s.resize(1, ncells) s.resize(1, ncells)
s.reset_mode(DECAWM) s.reset_mode(DECAWM)

View File

@ -13,7 +13,9 @@ import sys
from contextlib import suppress from contextlib import suppress
from functools import lru_cache from functools import lru_cache
from time import monotonic from time import monotonic
from typing import Any, Dict, List, NamedTuple, Optional, cast from typing import (
Any, Dict, Generator, List, NamedTuple, Optional, Tuple, cast
)
from .constants import ( from .constants import (
appname, is_macos, is_wayland, shell_path, supports_primary_selection appname, is_macos, is_wayland, shell_path, supports_primary_selection
@ -61,21 +63,23 @@ def color_from_int(val):
return Color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF) return Color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)
def parse_color_set(raw): def parse_color_set(raw: str) -> Generator[Tuple[int, Optional[int]], None, None]:
parts = raw.split(';') parts = raw.split(';')
lp = len(parts) lp = len(parts)
if lp % 2 != 0: if lp % 2 != 0:
return return
for c, spec in [parts[i:i + 2] for i in range(0, len(parts), 2)]: for c_, spec in [parts[i:i + 2] for i in range(0, len(parts), 2)]:
try: try:
c = int(c) c = int(c_)
if c < 0 or c > 255: if c < 0 or c > 255:
continue continue
if spec == '?': if spec == '?':
yield c, None yield c, None
else: else:
r, g, b = to_color(spec) q = to_color(spec)
yield c, r << 16 | g << 8 | b if q is not None:
r, g, b = q
yield c, r << 16 | g << 8 | b
except Exception: except Exception:
continue continue

View File

@ -10,7 +10,7 @@ from collections import deque
from enum import IntEnum from enum import IntEnum
from itertools import chain from itertools import chain
from re import Pattern from re import Pattern
from typing import Deque, Dict, List, Optional, Tuple, Union from typing import TYPE_CHECKING, Callable, Deque, Dict, List, Optional, Tuple, Union
from .config import build_ansi_color_table from .config import build_ansi_color_table
from .constants import ScreenGeometry, WindowGeometry, appname, wakeup from .constants import ScreenGeometry, WindowGeometry, appname, wakeup
@ -33,6 +33,10 @@ from .utils import (
set_primary_selection set_primary_selection
) )
if TYPE_CHECKING:
from .tabs import Tab
Tab
MatchPatternType = Union[Pattern, Tuple[Pattern, Optional[Pattern]]] MatchPatternType = Union[Pattern, Tuple[Pattern, Optional[Pattern]]]
@ -164,7 +168,7 @@ class Window:
raise Exception('No tab with id: {} in OS Window: {} was found, or the window counter wrapped'.format(tab.id, tab.os_window_id)) raise Exception('No tab with id: {} in OS Window: {} was found, or the window counter wrapped'.format(tab.id, tab.os_window_id))
self.tab_id = tab.id self.tab_id = tab.id
self.os_window_id = tab.os_window_id self.os_window_id = tab.os_window_id
self.tabref = weakref.ref(tab) self.tabref: Callable[[], Optional['Tab']] = weakref.ref(tab)
self.clipboard_control_buffers = {'p': '', 'c': ''} self.clipboard_control_buffers = {'p': '', 'c': ''}
self.destroyed = False self.destroyed = False
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0) self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)