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
from base64 import standard_b64encode
from collections import defaultdict, deque
from itertools import count
from contextlib import suppress
from itertools import count
from typing import Any, DefaultDict, Deque, Dict, Tuple
from kitty.utils import fit_image
from .operations import cursor
from .handler import ImageManagerBase
from .operations import cursor
try:
fsenc = sys.getfilesystemencoding() or 'utf-8'
@ -64,7 +65,7 @@ def run_imagemagick(path, cmd, keep_stdout=True):
def identify(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'
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)
if ans is None:
ans = shutil.which('convert') is not None
can_display_images.ans = ans
setattr(can_display_images, 'ans', ans)
return ans
@ -123,7 +124,7 @@ class ImageManager(ImageManagerBase):
self.image_id_to_image_data = {}
self.image_id_to_converted_data = {}
self.transmission_status = {}
self.placements_in_flight = defaultdict(deque)
self.placements_in_flight: DefaultDict[int, Deque[Dict[str, Any]]] = defaultdict(deque)
@property
def next_image_id(self):

View File

@ -31,15 +31,13 @@ if TYPE_CHECKING:
SequenceMap, KeyMap
def parse_shortcut(sc):
def parse_shortcut(sc: str) -> Tuple[int, bool, Optional[int]]:
parts = sc.split('+')
mods = 0
if len(parts) > 1:
mods = parse_mods(parts[:-1], sc)
if mods is None:
return None, None, None
key = parts[-1].upper()
key = getattr(defines, 'GLFW_KEY_' + key_name_aliases.get(key, key), None)
q = parts[-1].upper()
key: Optional[int] = getattr(defines, 'GLFW_KEY_' + key_name_aliases.get(q, q), None)
is_native = False
if key is None:
q = parts[-1]
@ -47,7 +45,7 @@ def parse_shortcut(sc):
with suppress(Exception):
key = int(q, 16)
else:
key = get_key_name_lookup()(q)
key = get_key_name_lookup()(q, False)
is_native = key is not None
return mods, is_native, key
@ -369,7 +367,7 @@ def parse_key(val, key_definitions):
return
is_sequence = sequence_sep in sc
if is_sequence:
trigger = None
trigger: Optional[Tuple[int, bool, int]] = None
restl: List[Tuple[int, bool, int]] = []
for part in sc.split(sequence_sep):
mods, is_native, key = parse_shortcut(part)
@ -484,8 +482,8 @@ def handle_symbol_map(key, val, ans):
class FontFeature(str):
def __new__(cls, name, parsed):
ans = str.__new__(cls, name)
def __new__(cls, name: str, parsed: bytes):
ans = str.__new__(cls, name) # type: ignore
ans.parsed = parsed
return ans
@ -531,7 +529,7 @@ def handle_clear_all_shortcuts(key, val, ans):
@deprecated_handler('x11_hide_window_decorations', 'macos_hide_titlebar')
def handle_deprecated_hide_window_decorations_aliases(key, val, ans):
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))
if to_bool(val):
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')
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):
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))
macos_show_window_title_in = ans.get('macos_show_window_title_in', 'all')
if to_bool(val):

View File

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

View File

@ -4,6 +4,7 @@ from typing import (
)
from kitty.boss import Boss
from kitty.fonts.render import FontObject
from kitty.options_stub import Options
# Constants {{{
@ -420,7 +421,20 @@ def fc_match(
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
@ -635,6 +649,14 @@ class ColorProfile:
def reset_color(self, num: int) -> None:
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(
spec: Dict[str, int], cursor_text_color: Optional[Union[bool, int]],
@ -866,11 +888,11 @@ def set_font_data(
Tuple[int, Union[bytearray, bytes]]],
prerender_func: Callable[
[int, int, int, int, int, float, float, float, float],
Tuple[int, ...]], descriptor_for_idx: Callable[[int], Tuple[dict, bool,
bool]],
Tuple[int, ...]],
descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]],
bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,
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
@ -902,6 +924,10 @@ class LineBuf:
class Cursor:
x: int
y: int
bg: int
fg: int
bold: bool
italic: bool
class Screen:
@ -959,6 +985,9 @@ class Screen:
def is_main_linebuf(self) -> bool:
pass
def erase_in_line(self, mode: int = 0, private: bool = False) -> None:
pass
def scroll(self, amt: int, upwards: bool) -> bool:
pass
@ -968,6 +997,9 @@ class Screen:
def clear_selection(self) -> None:
pass
def reset_mode(self, mode: int, private: bool = False) -> None:
pass
def refresh_sprite_positions(self) -> None:
pass

View File

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

View File

@ -131,6 +131,6 @@ def get_font_files(opts: Options) -> Dict[str, 'FontConfigPattern']:
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)
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
from functools import partial
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.constants import is_macos
@ -22,11 +22,30 @@ from kitty.options_stub import Options as OptionsStub
from kitty.utils import log_error
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:
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]:
@ -64,11 +83,11 @@ def create_symbol_map(opts: OptionsStub) -> Tuple[Tuple[int, int, int], ...]:
return sm
def descriptor_for_idx(idx):
def descriptor_for_idx(idx: int) -> Tuple[FontObject, bool, bool]:
return current_faces[idx]
def dump_faces(ftypes, indices):
def dump_faces(ftypes: List[str], indices: Dict[str, int]) -> None:
def face_str(f):
f = f[0]
if is_macos:
@ -87,7 +106,7 @@ def dump_faces(ftypes, indices):
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
opts = opts or defaults
sz = override_font_size or opts.font_size

View File

@ -4,6 +4,7 @@
import sys
from contextlib import suppress
from typing import Callable, Optional
from .constants import is_macos
@ -96,16 +97,18 @@ key_name_aliases = {
'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:
def get_key_name_lookup():
def get_key_name_lookup() -> LookupFunc:
return null_lookup
else:
def load_libxkb_lookup():
def load_libxkb_lookup() -> LookupFunc:
import ctypes
for suffix in ('.0', ''):
with suppress(Exception):
@ -113,7 +116,10 @@ else:
break
else:
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.argtypes = [ctypes.c_char_p, ctypes.c_int]
@ -125,13 +131,13 @@ else:
return xkb_lookup
def get_key_name_lookup():
ans = getattr(get_key_name_lookup, 'ans', None)
def get_key_name_lookup() -> LookupFunc:
ans: Optional[LookupFunc] = getattr(get_key_name_lookup, 'ans', None)
if ans is None:
try:
ans = load_libxkb_lookup()
except Exception as e:
print('Failed to load libxkbcommon.xkb_keysym_from_name with error:', e, file=sys.stderr)
ans = null_lookup
get_key_name_lookup.ans = ans
setattr(get_key_name_lookup, 'ans', ans)
return ans

View File

@ -5,14 +5,14 @@
import os
import re
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
pointer_to_uint = POINTER(c_uint)
def null_marker(*a):
return iter(())
MarkerFunc = Callable[[str, int, int, int], Generator[None, None, None]]
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))
if isinstance(expression, str):
pat = re.compile(expression, flags=flags)
else:
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)
colorv.value = color
for match in pat.finditer(text):
@ -41,7 +41,7 @@ def marker_from_regex(expression, color, flags=re.UNICODE):
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 = ''
color_map = {}
for i, (color, spec) in enumerate(regexes):
@ -51,24 +51,24 @@ def marker_from_multiple_regex(regexes, flags=re.UNICODE):
expr = expr[1:]
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)
for match in pat.finditer(text):
left.value = match.start()
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]
yield
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)
def marker_from_function(func):
def marker(text, left_address, right_address, color_address):
def marker_from_function(func: Callable[[str], Iterable[Tuple[int, int, int]]]) -> MarkerFunc:
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)
for (l, r, c) in func(text):
left.value = l
@ -79,13 +79,15 @@ def marker_from_function(func):
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':
assert not isinstance(spec, str)
if len(spec) == 1:
return marker_from_regex(spec[0][1], spec[0][0], flags=flags)
return marker_from_multiple_regex(spec, flags=flags)
if ftype == 'function':
import runpy
assert isinstance(spec, str)
path = spec
if not os.path.isabs(path):
path = os.path.join(config_dir, path)

View File

@ -16,7 +16,8 @@ def generate_stub():
text = as_type_stub(
all_options,
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=(
'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:
raise MatchError(match)
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')
kwargs = {}
if match:

View File

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

View File

@ -13,7 +13,9 @@ import sys
from contextlib import suppress
from functools import lru_cache
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 (
appname, is_macos, is_wayland, shell_path, supports_primary_selection
@ -61,20 +63,22 @@ def color_from_int(val):
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(';')
lp = len(parts)
if lp % 2 != 0:
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:
c = int(c)
c = int(c_)
if c < 0 or c > 255:
continue
if spec == '?':
yield c, None
else:
r, g, b = to_color(spec)
q = to_color(spec)
if q is not None:
r, g, b = q
yield c, r << 16 | g << 8 | b
except Exception:
continue

View File

@ -10,7 +10,7 @@ from collections import deque
from enum import IntEnum
from itertools import chain
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 .constants import ScreenGeometry, WindowGeometry, appname, wakeup
@ -33,6 +33,10 @@ from .utils import (
set_primary_selection
)
if TYPE_CHECKING:
from .tabs import Tab
Tab
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))
self.tab_id = tab.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.destroyed = False
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)