more typing work
This commit is contained in:
parent
92827ae647
commit
c817ba9eae
@ -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):
|
||||||
|
|||||||
@ -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):
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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']
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user