diff --git a/kitty/complete.py b/kitty/complete.py index 1f4439dd1..e6a5e64e4 100644 --- a/kitty/complete.py +++ b/kitty/complete.py @@ -6,11 +6,13 @@ import os import shlex import sys from functools import lru_cache -from typing import Callable, Dict, Tuple +from typing import Callable, Dict, Iterable, Optional, Tuple from kittens.runner import all_kitten_names, get_kitten_cli_docs -from .cli import options_for_completion, parse_option_spec +from .cli import ( + OptionDict, OptionSpecSeq, options_for_completion, parse_option_spec +) from .rc.base import all_command_names, command_for_name from .shell import options_for_cmd @@ -228,7 +230,7 @@ def complete_kitty_cli_arg(ans, opt, prefix): def complete_alias_map(ans, words, new_word, option_map, complete_args=None): expecting_arg = False - opt = None + opt: Optional[OptionDict] = None last_word = words[-1] if words else '' for w in words: if expecting_arg: @@ -261,7 +263,7 @@ def complete_alias_map(ans, words, new_word, option_map, complete_args=None): ans.match_groups['Options'] = {k: opt['help'] for k, opt in option_map.items() if k.startswith(prefix)} -def complete_cli(ans, words, new_word, seq, complete_args=lambda *a: None): +def complete_cli(ans, words, new_word, seq: OptionSpecSeq, complete_args=lambda *a: None): option_map = {} for opt in seq: if not isinstance(opt, str): @@ -292,7 +294,7 @@ def path_completion(prefix=''): src = os.path.expandvars(os.path.expanduser(base)) src_prefix = os.path.abspath(os.path.expandvars(os.path.expanduser(prefix))) if prefix else '' try: - items = os.scandir(src) + items: Iterable[os.DirEntry] = os.scandir(src) except FileNotFoundError: items = () for x in items: diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 579b8767f..071fa4b07 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1,5 +1,6 @@ from typing import ( - Any, AnyStr, Callable, Dict, List, NewType, Optional, Tuple, Union + Any, AnyStr, Callable, Dict, List, NewType, Optional, Tuple, TypedDict, + Union ) from kitty.boss import Boss @@ -390,7 +391,14 @@ def default_color_table() -> Tuple[int, ...]: pass -FontConfigPattern = Dict[str, Union[str, int, bool, float]] +class FontConfigPattern(TypedDict): + family: str + full_name: str + postscript_name: str + style: str + spacing: str + weight: int + slant: int def fc_list( diff --git a/kitty/fonts/fontconfig.py b/kitty/fonts/fontconfig.py index a18b1247f..15eb2533f 100644 --- a/kitty/fonts/fontconfig.py +++ b/kitty/fonts/fontconfig.py @@ -4,23 +4,32 @@ import re from functools import lru_cache -from typing import Generator +from typing import TYPE_CHECKING, Dict, Generator, List, Optional, Tuple, cast from kitty.fast_data_types import ( FC_DUAL, FC_MONO, FC_SLANT_ITALIC, FC_SLANT_ROMAN, FC_WEIGHT_BOLD, FC_WEIGHT_REGULAR, fc_list, fc_match as fc_match_impl ) +from kitty.options_stub import Options from . import ListedFont +if TYPE_CHECKING: + from kitty.fast_data_types import FontConfigPattern + FontConfigPattern + + 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['FontConfigPattern']]] + + +def create_font_map(all_fonts: Tuple['FontConfigPattern', ...]) -> FontMap: + ans: FontMap = {'family_map': {}, 'ps_map': {}, 'full_map': {}} for x in all_fonts: if 'path' not in x: continue @@ -34,7 +43,7 @@ def create_font_map(all_fonts): @lru_cache() -def all_fonts_map(monospaced=True): +def all_fonts_map(monospaced=True) -> FontMap: if monospaced: ans = fc_list(FC_DUAL) + fc_list(FC_MONO) else: @@ -55,16 +64,16 @@ def list_fonts() -> Generator[ListedFont, None, None]: yield {'family': f, 'full_name': fn, 'postscript_name': str(fd.get('postscript_name', '')), 'is_monospace': is_mono} -def family_name_to_key(family): +def family_name_to_key(family: str) -> str: return re.sub(r'\s+', ' ', family.lower()) @lru_cache() -def fc_match(family, bold, italic, spacing=FC_MONO): +def fc_match(family, bold, italic, spacing=FC_MONO) -> 'FontConfigPattern': return fc_match_impl(family, bold, italic, spacing) -def find_best_match(family, bold=False, italic=False, monospaced=True): +def find_best_match(family: str, bold=False, italic=False, monospaced=True) -> 'FontConfigPattern': q = family_name_to_key(family) font_map = all_fonts_map(monospaced) @@ -85,7 +94,7 @@ def find_best_match(family, bold=False, italic=False, monospaced=True): for spacing in (FC_MONO, FC_DUAL): possibility = fc_match(family, False, False, spacing) for key, map_key in (('postscript_name', 'ps_map'), ('full_name', 'full_map'), ('family', 'family_map')): - val = possibility.get(key) + val: Optional[str] = cast(Optional[str], possibility.get(key)) if val: candidates = font_map[map_key].get(family_name_to_key(val)) if candidates: @@ -103,14 +112,14 @@ def find_best_match(family, bold=False, italic=False, monospaced=True): return fc_match(family, bold, italic) -def resolve_family(f, main_family, bold, italic): +def resolve_family(f: str, main_family: str, bold: bool, italic: bool) -> str: if (bold or italic) and f == 'auto': f = main_family return f -def get_font_files(opts): - ans = {} +def get_font_files(opts: Options) -> Dict[str, 'FontConfigPattern']: + ans: Dict[str, 'FontConfigPattern'] = {} for (bold, italic), attr in attr_map.items(): rf = resolve_family(getattr(opts, attr), opts.font_family, bold, italic) font = find_best_match(rf, bold, italic) @@ -122,6 +131,6 @@ def get_font_files(opts): return ans -def font_for_family(family): +def font_for_family(family) -> 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 diff --git a/kitty/fonts/list.py b/kitty/fonts/list.py index 65be4e98d..34c336284 100644 --- a/kitty/fonts/list.py +++ b/kitty/fonts/list.py @@ -3,8 +3,11 @@ # License: GPL v3 Copyright: 2017, Kovid Goyal import sys +from typing import Dict, List, Sequence + from kitty.constants import is_macos -from typing import Sequence + +from . import ListedFont if is_macos: from .core_text import list_fonts @@ -12,8 +15,8 @@ else: from .fontconfig import list_fonts -def create_family_groups(monospaced=True): - g = {} +def create_family_groups(monospaced: bool = True) -> Dict[str, List[ListedFont]]: + g: Dict[str, List[ListedFont]] = {} for f in list_fonts(): if not monospaced or f['is_monospace']: g.setdefault(f['family'], []).append(f)