Use a stub rather than TYPE_CHECKING

This commit is contained in:
Kovid Goyal 2020-03-15 13:27:40 +05:30
parent 871ca4dda6
commit 382c31ddf2
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
27 changed files with 267 additions and 326 deletions

View File

@ -9,13 +9,13 @@ from typing import TYPE_CHECKING, List, Optional, Tuple
from kitty.cli import parse_args
from kitty.cli_stub import AskCLIOptions
from kitty.constants import cache_dir
from kitty.typing import BossType
from ..tui.handler import result_handler
from ..tui.operations import alternate_screen, styled
if TYPE_CHECKING:
import readline
import kitty
else:
readline = None
@ -127,7 +127,7 @@ def main(args: List[str]) -> Response:
@result_handler()
def handle_result(args: List[str], data: Response, target_window_id: int, boss: 'kitty.boss.Boss') -> None:
def handle_result(args: List[str], data: Response, target_window_id: int, boss: BossType) -> None:
if data['response'] is not None:
func, *args = data['items']
getattr(boss, func)(data['response'], *args)

View File

@ -10,8 +10,8 @@ from functools import lru_cache
from gettext import gettext as _
from itertools import repeat
from typing import (
TYPE_CHECKING, Any, Callable, Dict, Generator, Iterable, List, Optional,
Pattern, Sequence, Set, Tuple, Type, cast
Any, Callable, Dict, Generator, Iterable, List, Optional, Pattern,
Sequence, Set, Tuple, Type, cast
)
from kitty.cli import parse_args
@ -20,30 +20,26 @@ from kitty.fast_data_types import set_clipboard_string
from kitty.key_encoding import (
KeyEvent, backspace_key, enter_key, key_defs as K
)
from kitty.typing import BossType, KittyCommonOpts
from kitty.utils import ScreenSize, screen_size_function
from ..tui.handler import Handler, result_handler
from ..tui.loop import Loop
from ..tui.operations import faint, styled
if TYPE_CHECKING:
from kitty.config import KittyCommonOpts
from kitty.boss import Boss
@lru_cache()
def kitty_common_opts() -> 'KittyCommonOpts':
def kitty_common_opts() -> KittyCommonOpts:
import json
v = os.environ.get('KITTY_COMMON_OPTS')
if v:
return cast('KittyCommonOpts', json.loads(v))
return cast(KittyCommonOpts, json.loads(v))
from kitty.config import common_opts_as_dict
return common_opts_as_dict()
DEFAULT_HINT_ALPHABET = string.digits + string.ascii_lowercase
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
screen_size = screen_size_function()
ESCAPE = K['ESCAPE']
@ -346,7 +342,7 @@ def parse_input(text: str) -> str:
try:
cols = int(os.environ['OVERLAID_WINDOW_COLS'])
except KeyError:
cols = screen_size().cols
cols = screen_size_function()().cols
return convert_text(text, cols)
@ -560,7 +556,7 @@ def main(args: List[str]) -> Optional[Dict[str, Any]]:
return run(opts, text, items)
def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: 'Boss', extra_cli_args: Sequence[str], *a: Any) -> None:
def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: BossType, extra_cli_args: Sequence[str], *a: Any) -> None:
for m, g in zip(data['match'], data['groupdicts']):
if m:
path, line = g['path'], g['line']
@ -589,7 +585,7 @@ def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_i
@result_handler(type_of_input='screen')
def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: 'Boss') -> None:
def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: BossType) -> None:
if data['customize_processing']:
m = load_custom_processor(data['customize_processing'])
if 'handle_result' in m:

View File

@ -13,13 +13,13 @@ from functools import lru_cache
from math import ceil
from tempfile import NamedTemporaryFile
from typing import (
TYPE_CHECKING, Dict, Generator, List, NamedTuple, Optional, Pattern, Tuple,
Union
Dict, Generator, List, NamedTuple, Optional, Pattern, Tuple, Union
)
from kitty.cli import parse_args
from kitty.cli_stub import IcatCLIOptions
from kitty.constants import appname
from kitty.typing import GRT_f, GRT_t
from kitty.utils import (
TTYIO, ScreenSize, ScreenSizeGetter, fit_image, screen_size_function
)
@ -30,9 +30,6 @@ from ..tui.images import (
)
from ..tui.operations import clear_images_on_screen
if TYPE_CHECKING:
from ..tui.images import GRT_f, GRT_t # noqa
OPTIONS = '''\
--align
type=choices

View File

@ -3,35 +3,29 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from types import TracebackType
from typing import (
TYPE_CHECKING, Any, Callable, ContextManager, Dict, Optional, Sequence, Type,
Union
Any, Callable, ContextManager, Dict, Optional, Sequence, Type, Union
)
if TYPE_CHECKING:
from kitty.utils import ScreenSize
from .loop import TermManager, Loop, Debug, MouseEvent
from .images import ImageManager
from kitty.conf.utils import KittensKeyAction
from kitty.boss import Boss
from kitty.key_encoding import KeyEvent
from types import TracebackType
ScreenSize, TermManager, Loop, Debug, KeyEvent, MouseEvent, TracebackType, Boss, ImageManager
import asyncio
from kitty.typing import (
AbstractEventLoop, BossType, Debug, ImageManagerType, KeyEventType,
KittensKeyActionType, LoopType, MouseEvent, ScreenSize, TermManagerType
)
class Handler:
image_manager_class: Optional[Type['ImageManager']] = None
image_manager_class: Optional[Type[ImageManagerType]] = None
def _initialize(
self,
screen_size: 'ScreenSize',
term_manager: 'TermManager',
screen_size: ScreenSize,
term_manager: TermManagerType,
schedule_write: Callable[[bytes], None],
tui_loop: 'Loop',
debug: 'Debug',
image_manager: Optional['ImageManager'] = None
tui_loop: LoopType,
debug: Debug,
image_manager: Optional[ImageManagerType] = None
) -> None:
from .operations import commander
self.screen_size = screen_size
@ -43,15 +37,15 @@ class Handler:
self._image_manager = image_manager
@property
def image_manager(self) -> 'ImageManager':
def image_manager(self) -> ImageManagerType:
assert self._image_manager is not None
return self._image_manager
@property
def asyncio_loop(self) -> 'asyncio.AbstractEventLoop':
def asyncio_loop(self) -> AbstractEventLoop:
return self._tui_loop.asycio_loop
def add_shortcut(self, action: 'KittensKeyAction', key: str, mods: Optional[int] = None, is_text: Optional[bool] = False) -> None:
def add_shortcut(self, action: KittensKeyActionType, key: str, mods: Optional[int] = None, is_text: Optional[bool] = False) -> None:
if not hasattr(self, '_text_shortcuts'):
self._text_shortcuts, self._key_shortcuts = {}, {}
if is_text:
@ -59,7 +53,7 @@ class Handler:
else:
self._key_shortcuts[(key, mods or 0)] = action
def shortcut_action(self, key_event_or_text: Union[str, 'KeyEvent']) -> Optional['KittensKeyAction']:
def shortcut_action(self, key_event_or_text: Union[str, KeyEventType]) -> Optional[KittensKeyActionType]:
if isinstance(key_event_or_text, str):
return self._text_shortcuts.get(key_event_or_text)
return self._key_shortcuts.get((key_event_or_text.key, key_event_or_text.mods))
@ -70,7 +64,7 @@ class Handler:
self.debug.fobj = self
self.initialize()
def __exit__(self, etype: type, value: Exception, tb: 'TracebackType') -> None:
def __exit__(self, etype: type, value: Exception, tb: TracebackType) -> None:
del self.debug.fobj
self.finalize()
if self._image_manager is not None:
@ -82,7 +76,7 @@ class Handler:
def finalize(self) -> None:
pass
def on_resize(self, screen_size: 'ScreenSize') -> None:
def on_resize(self, screen_size: ScreenSize) -> None:
self.screen_size = screen_size
def quit_loop(self, return_code: Optional[int] = None) -> None:
@ -94,7 +88,7 @@ class Handler:
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
pass
def on_key(self, key_event: 'KeyEvent') -> None:
def on_key(self, key_event: KeyEventType) -> None:
pass
def on_mouse(self, mouse_event: 'MouseEvent') -> None:
@ -127,7 +121,7 @@ class Handler:
data = sep.join(map(str, args)) + end
self.write(data)
def suspend(self) -> ContextManager['TermManager']:
def suspend(self) -> ContextManager[TermManagerType]:
return self._term_manager.suspend()
@ -141,7 +135,7 @@ class HandleResult:
self.no_ui = no_ui
self.type_of_input = type_of_input
def __call__(self, args: Sequence[str], data: Any, target_window_id: int, boss: 'Boss') -> Any:
def __call__(self, args: Sequence[str], data: Any, target_window_id: int, boss: BossType) -> Any:
return self.impl(args, data, target_window_id, boss)

View File

@ -10,10 +10,13 @@ from collections import defaultdict, deque
from contextlib import suppress
from itertools import count
from typing import (
TYPE_CHECKING, Any, DefaultDict, Deque, Dict, List, Optional, Sequence,
Tuple, Union
Any, DefaultDict, Deque, Dict, List, Optional, Sequence, Tuple, Union
)
from kitty.typing import (
CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t, HandlerType,
TypedDict
)
from kitty.utils import ScreenSize, fit_image
from .operations import cursor
@ -25,28 +28,11 @@ except Exception:
fsenc = 'utf-8'
try:
from typing import TypedDict, Literal
GRT_a = Literal['t', 'T', 'q', 'p', 'd']
GRT_f = Literal[24, 32, 100]
GRT_t = Literal['d', 'f', 't', 's']
GRT_o = Literal['z']
GRT_m = Literal[0, 1]
GRT_d = Literal['a', 'A', 'c', 'C', 'i', 'I', 'p', 'P', 'q', 'Q', 'x', 'X', 'y', 'Y', 'z', 'Z']
except ImportError:
TypedDict = dict
if TYPE_CHECKING:
import subprocess
from .handler import Handler
class ImageData:
def __init__(self, fmt: str, width: int, height: int, mode: str):
self.width, self.height, self.fmt, self.mode = width, height, fmt, mode
self.transmit_fmt: 'GRT_f' = (24 if self.mode == 'rgb' else 32)
self.transmit_fmt: GRT_f = (24 if self.mode == 'rgb' else 32)
class OpenFailed(ValueError):
@ -71,7 +57,7 @@ class NoImageMagick(Exception):
pass
def run_imagemagick(path: str, cmd: Sequence[str], keep_stdout: bool = True) -> 'subprocess.CompletedProcess[bytes]':
def run_imagemagick(path: str, cmd: Sequence[str], keep_stdout: bool = True) -> CompletedProcess:
import subprocess
try:
p = subprocess.run(cmd, stdout=subprocess.PIPE if keep_stdout else subprocess.DEVNULL, stderr=subprocess.PIPE)
@ -140,16 +126,16 @@ SentImageKey = Tuple[int, int, int]
class GraphicsCommand:
a: 'GRT_a' = 't' # action
f: 'GRT_f' = 32 # image data format
t: 'GRT_t' = 'd' # transmission medium
a: GRT_a = 't' # action
f: GRT_f = 32 # image data format
t: GRT_t = 'd' # transmission medium
s: int = 0 # sent image width
v: int = 0 # sent image height
S: int = 0 # size of data to read from file
O: int = 0 # offset of data to read from file
i: int = 0 # image id
o: Optional['GRT_o'] = None # type of compression
m: 'GRT_m' = 0 # 0 or 1 whether there is more chunked data
o: Optional[GRT_o] = None # type of compression
m: GRT_m = 0 # 0 or 1 whether there is more chunked data
x: int = 0 # left edge of image area to display
y: int = 0 # top edge of image area to display
w: int = 0 # image width to display
@ -159,7 +145,7 @@ class GraphicsCommand:
c: int = 0 # number of cols to display image over
r: int = 0 # number of rows to display image over
z: int = 0 # z-index
d: 'GRT_d' = 'a' # what to delete
d: GRT_d = 'a' # what to delete
def serialize(self, payload: bytes = b'') -> bytes:
items = []
@ -193,7 +179,7 @@ class Placement(TypedDict):
class ImageManager:
def __init__(self, handler: 'Handler'):
def __init__(self, handler: HandlerType):
self.image_id_counter = count()
self.handler = handler
self.filesystem_ok: Optional[bool] = None

View File

@ -12,9 +12,7 @@ import signal
import sys
from contextlib import contextmanager
from functools import partial
from typing import (
TYPE_CHECKING, Any, Callable, Dict, Generator, List, NamedTuple, Optional
)
from typing import Any, Callable, Dict, Generator, List, NamedTuple, Optional
from kitty.constants import is_macos
from kitty.fast_data_types import (
@ -24,20 +22,12 @@ from kitty.key_encoding import (
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, backspace_key, decode_key_event,
enter_key, key_defs as K
)
from kitty.utils import screen_size_function, write_all
from kitty.typing import ImageManagerType, KeyEventType, Protocol
from kitty.utils import ScreenSizeGetter, screen_size_function, write_all
from .handler import Handler
from .operations import init_state, reset_state
if TYPE_CHECKING:
from kitty.key_encoding import KeyEvent
from .images import ImageManager
KeyEvent, ImageManager
from typing import Protocol
else:
Protocol = object
C, D = K['C'], K['D']
@ -156,7 +146,7 @@ class UnhandledException(Handler):
self.write('\r\n')
self.write('Press the Enter key to quit')
def on_key(self, key_event: 'KeyEvent') -> None:
def on_key(self, key_event: KeyEventType) -> None:
if key_event is enter_key:
self.quit_loop(1)
@ -355,7 +345,7 @@ class Loop:
self.return_code = return_code
self.asycio_loop.stop()
def loop_impl(self, handler: Handler, term_manager: TermManager, image_manager: Optional['ImageManager'] = None) -> Optional[str]:
def loop_impl(self, handler: Handler, term_manager: TermManager, image_manager: Optional[ImageManagerType] = None) -> Optional[str]:
self.write_buf = []
tty_fd = term_manager.tty_fd
tb = None
@ -399,7 +389,7 @@ class Loop:
signal_manager = SignalManager(self.asycio_loop, _on_sigwinch, handler.on_interrupt, handler.on_term)
with TermManager() as term_manager, signal_manager:
self._get_screen_size = screen_size_function(term_manager.tty_fd)
self._get_screen_size: ScreenSizeGetter = screen_size_function(term_manager.tty_fd)
image_manager = None
if handler.image_manager_class is not None:
image_manager = handler.image_manager_class(handler)

View File

@ -6,20 +6,15 @@ import sys
from contextlib import contextmanager
from functools import wraps
from typing import (
IO, TYPE_CHECKING, Any, Callable, Dict, Generator, Optional, Tuple,
TypeVar, Union
IO, Any, Callable, Dict, Generator, Optional, Tuple, TypeVar, Union
)
from kitty.rgb import Color, color_as_sharp, to_color
from kitty.typing import GraphicsCommandType, HandlerType, ScreenSize
from .operations_stub import CMD
if TYPE_CHECKING:
from kitty.utils import ScreenSize
from .images import GraphicsCommand
from .handler import Handler
ScreenSize, GraphicsCommand, Handler
GraphicsCommandType, ScreenSize # needed for stub generation
S7C1T = '\033 F'
SAVE_CURSOR = '\0337'
RESTORE_CURSOR = '\0338'
@ -232,7 +227,7 @@ def serialize_gr_command(cmd: Dict[str, Union[int, str]], payload: Optional[byte
@cmd
def gr_command(cmd: Union[Dict, 'GraphicsCommand'], payload: Optional[bytes] = None) -> str:
def gr_command(cmd: Union[Dict, 'GraphicsCommandType'], payload: Optional[bytes] = None) -> str:
if isinstance(cmd, dict):
raw = serialize_gr_command(cmd, payload)
else:
@ -349,14 +344,14 @@ def request_from_clipboard(use_primary: bool = False) -> str:
# Boilerplate to make operations availble via Handler.cmd {{{
def writer(handler: 'Handler', func: Callable) -> Callable:
def writer(handler: HandlerType, func: Callable) -> Callable:
@wraps(func)
def f(*a: Any, **kw: Any) -> None:
handler.write(func(*a, **kw))
return f
def commander(handler: 'Handler') -> CMD:
def commander(handler: HandlerType) -> CMD:
ans = CMD()
for name, func in all_cmds.items():
setattr(ans, name, writer(handler, func))
@ -374,8 +369,7 @@ def func_sig(func: Callable) -> Generator[str, None, None]:
def as_type_stub() -> str:
ans = [
'from typing import * # noqa',
'from kitty.utils import ScreenSize',
'from kittens.tui.images import GraphicsCommand',
'from kitty.typing import GraphicsCommandType, ScreenSize',
'from kitty.rgb import Color',
'import kitty.rgb',
]

View File

@ -10,7 +10,7 @@ from contextlib import suppress
from functools import lru_cache
from gettext import gettext as _
from typing import (
TYPE_CHECKING, Any, Dict, FrozenSet, Generator, Iterable, List, Optional, Sequence, Tuple,
Any, Dict, FrozenSet, Generator, Iterable, List, Optional, Sequence, Tuple,
Union
)
@ -22,6 +22,7 @@ from kitty.fast_data_types import is_emoji_presentation_base, wcswidth
from kitty.key_encoding import (
CTRL, RELEASE, SHIFT, KeyEvent, enter_key, key_defs as K
)
from kitty.typing import BossType
from kitty.utils import ScreenSize, get_editor
from ..tui.handler import Handler, result_handler
@ -32,11 +33,6 @@ from ..tui.operations import (
sgr, styled
)
if TYPE_CHECKING:
from kitty.boss import Boss
Boss
HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES'
UP = K['UP']
DOWN = K['DOWN']
@ -579,7 +575,7 @@ def main(args: List[str]) -> Optional[str]:
@result_handler()
def handle_result(args: List[str], current_char: str, target_window_id: int, boss: 'Boss') -> None:
def handle_result(args: List[str], current_char: str, target_window_id: int, boss: BossType) -> None:
w = boss.window_id_map.get(target_window_id)
if w is not None:
w.paste(current_char)

View File

@ -2,8 +2,9 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from enum import IntFlag
from itertools import chain
from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple
from typing import List, Optional, Sequence, Tuple
from .constants import WindowGeometry
from .fast_data_types import (
@ -12,17 +13,7 @@ from .fast_data_types import (
)
from .options_stub import Options
from .utils import load_shaders
try:
from enum import IntFlag
except ImportError:
from enum import IntEnum as IntFlag # type: ignore
if TYPE_CHECKING:
from .window import Window
from .layout import Layout
Window, Layout
from .typing import WindowType, LayoutType
class BorderColor(IntFlag):
@ -63,9 +54,9 @@ class Borders:
def __call__(
self,
windows: List['Window'],
active_window: Optional['Window'],
current_layout: 'Layout',
windows: List[WindowType],
active_window: Optional[WindowType],
current_layout: LayoutType,
extra_blank_rects: Sequence[Tuple[int, int, int, int]],
padding_width: int,
border_width: int,

View File

@ -10,8 +10,7 @@ from contextlib import suppress
from functools import partial
from gettext import gettext as _
from typing import (
TYPE_CHECKING, Any, Callable, Dict, Generator, Iterable, List, Optional,
Tuple, Union
Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union
)
from weakref import WeakValueDictionary
@ -44,6 +43,7 @@ from .session import Session, create_sessions
from .tabs import (
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
)
from .typing import PopenType, TypedDict
from .utils import (
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
log_error, open_url, parse_address_spec, remove_socket_file, safe_print,
@ -51,13 +51,6 @@ from .utils import (
)
from .window import MatchPatternType, Window
if TYPE_CHECKING:
from typing import TypedDict
from .rc.base import RemoteCommand # noqa
from subprocess import Popen # noqa
else:
TypedDict = dict
class OSWindowDict(TypedDict):
id: int
@ -149,7 +142,7 @@ class Boss:
):
set_layout_options(opts)
self.clipboard_buffers: Dict[str, str] = {}
self.update_check_process: Optional['Popen'] = None
self.update_check_process: Optional[PopenType] = None
self.window_id_map: WeakValueDictionary[int, Window] = WeakValueDictionary()
self.startup_colors = {k: opts[k] for k in opts if isinstance(opts[k], Color)}
self.startup_cursor_text_color = opts.cursor_text_color
@ -1209,7 +1202,7 @@ class Boss:
with suppress(FileNotFoundError):
os.remove(path)
def set_update_check_process(self, process: Optional['Popen'] = None) -> None:
def set_update_check_process(self, process: Optional[PopenType] = None) -> None:
if self.update_check_process is not None:
with suppress(Exception):
if self.update_check_process.poll() is None:

View File

@ -4,15 +4,13 @@
import re
from typing import TYPE_CHECKING, List, Generator, Any, Type
from typing import List, Generator, Any, Type
if TYPE_CHECKING:
from kitty.cli_stub import HintsCLIOptions
from kittens.hints.main import Mark as MarkClass
HintsCLIOptions, MarkClass
from .cli_stub import HintsCLIOptions
from .typing import MarkType
def mark(text: str, args: 'HintsCLIOptions', Mark: Type['MarkClass'], extra_cli_args: List[str], *a: Any) -> Generator['MarkClass', None, None]:
def mark(text: str, args: HintsCLIOptions, Mark: Type[MarkType], extra_cli_args: List[str], *a: Any) -> Generator[MarkType, None, None]:
for idx, m in enumerate(re.finditer(args.regex, text)):
start, end = m.span()
mark_text = text[start:end].replace('\n', '').replace('\0', '')

View File

@ -7,32 +7,27 @@ import re
import sys
from collections import deque
from typing import (
TYPE_CHECKING, Any, Callable, Dict, FrozenSet, Iterable, Iterator, List,
Match, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, cast
Any, Callable, Dict, FrozenSet, Iterable, Iterator, List, Match, Optional,
Sequence, Set, Tuple, Type, TypeVar, Union, cast
)
from .cli_stub import CLIOptions
from .conf.utils import resolve_config
from .config import KeyAction
from .constants import appname, defconf, is_macos, is_wayland, str_version
from .options_stub import Options as OptionsStub
from .typing import BadLineType, KeySpec, SequenceMap, TypedDict
try:
from typing import TypedDict
class OptionDict(TypedDict):
dest: str
aliases: FrozenSet[str]
help: str
choices: FrozenSet[str]
type: str
default: Optional[str]
condition: bool
except ImportError:
OptionDict = Dict[str, Any] # type: ignore
class OptionDict(TypedDict):
dest: str
aliases: FrozenSet[str]
help: str
choices: FrozenSet[str]
type: str
default: Optional[str]
condition: bool
if TYPE_CHECKING:
from .config import KeyAction, KeySpec, SequenceMap # noqa
from .conf.utils import BadLine # noqa
CONFIG_HELP = '''\
Specify a path to the configuration file(s) to use. All configuration files are
@ -733,10 +728,10 @@ def parse_args(
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
ShortcutMap = Dict[Tuple['KeySpec', ...], 'KeyAction']
ShortcutMap = Dict[Tuple[KeySpec, ...], KeyAction]
def print_shortcut(key_sequence: Iterable['KeySpec'], action: 'KeyAction') -> None:
def print_shortcut(key_sequence: Iterable[KeySpec], action: KeyAction) -> None:
if not getattr(print_shortcut, 'maps', None):
from kitty.keys import defines
v = vars(defines)
@ -764,7 +759,7 @@ def print_shortcut(key_sequence: Iterable['KeySpec'], action: 'KeyAction') -> No
print('\t', ' > '.join(keys), action)
def print_shortcut_changes(defns: ShortcutMap, text: str, changes: Set[Tuple['KeySpec', ...]]) -> None:
def print_shortcut_changes(defns: ShortcutMap, text: str, changes: Set[Tuple[KeySpec, ...]]) -> None:
if changes:
print(title(text))
@ -781,8 +776,8 @@ def compare_keymaps(final: ShortcutMap, initial: ShortcutMap) -> None:
print_shortcut_changes(final, 'Changed shortcuts:', changed)
def flatten_sequence_map(m: 'SequenceMap') -> ShortcutMap:
ans: Dict[Tuple['KeySpec', ...], 'KeyAction'] = {}
def flatten_sequence_map(m: SequenceMap) -> ShortcutMap:
ans: Dict[Tuple[KeySpec, ...], KeyAction] = {}
for key_spec, rest_map in m.items():
for r, action in rest_map.items():
ans[(key_spec,) + (r)] = action
@ -811,7 +806,7 @@ def compare_opts(opts: OptionsStub) -> None:
compare_keymaps(final, initial)
def create_opts(args: CLIOptions, debug_config: bool = False, accumulate_bad_lines: Optional[List['BadLine']] = None) -> OptionsStub:
def create_opts(args: CLIOptions, debug_config: bool = False, accumulate_bad_lines: Optional[List[BadLineType]] = None) -> OptionsStub:
from .config import load_config
config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config))
if debug_config:

View File

@ -23,14 +23,9 @@ from .config_data import all_options, parse_mods, type_convert
from .constants import cache_dir, defconf, is_macos
from .key_names import get_key_name_lookup, key_name_aliases
from .options_stub import Options as OptionsStub
from .typing import TypedDict
from .utils import log_error
try:
from typing import TypedDict
except ImportError:
TypedDict = dict
KeySpec = Tuple[int, bool, int]
KeyMap = Dict[KeySpec, 'KeyAction']
KeySequence = Tuple[KeySpec, ...]

View File

@ -8,10 +8,9 @@ import pwd
import sys
from contextlib import suppress
from functools import lru_cache
from typing import TYPE_CHECKING, NamedTuple, Optional, Set
from typing import NamedTuple, Optional, Set
if TYPE_CHECKING:
from .options_stub import Options # noqa
from .options_stub import Options
class Version(NamedTuple):
@ -20,11 +19,11 @@ class Version(NamedTuple):
patch: int
appname = 'kitty'
version = Version(0, 16, 0)
str_version = '.'.join(map(str, version))
appname: str = 'kitty'
version: Version = Version(0, 16, 0)
str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos = 'darwin' in _plat
is_macos: bool = 'darwin' in _plat
base = os.path.dirname(os.path.abspath(__file__))
@ -179,7 +178,7 @@ def detect_if_wayland_ok() -> bool:
return ans == b'YES'
def is_wayland(opts: Optional['Options'] = None) -> bool:
def is_wayland(opts: Optional[Options] = None) -> bool:
if is_macos:
return False
if opts is None:

View File

@ -3,31 +3,25 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import re
from typing import (
TYPE_CHECKING, Dict, Generator, Iterable, List, Optional, Tuple
)
from typing import Dict, Generator, Iterable, List, Optional, Tuple
from kitty.fast_data_types import coretext_all_fonts
from kitty.options_stub import Options
from kitty.typing import CoreTextFont
from kitty.utils import log_error
from . import ListedFont
if TYPE_CHECKING:
from kitty.fast_data_types import CoreTextFont as C
CoreTextFont = C
attr_map = {(False, False): 'font_family',
(True, False): 'bold_font',
(False, True): 'italic_font',
(True, True): 'bold_italic_font'}
FontMap = Dict[str, Dict[str, List['CoreTextFont']]]
FontMap = Dict[str, Dict[str, List[CoreTextFont]]]
def create_font_map(all_fonts: Iterable['CoreTextFont']) -> FontMap:
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()
@ -56,11 +50,11 @@ 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: str, bold: bool = False, italic: bool = False) -> 'CoreTextFont':
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()
def score(candidate: 'CoreTextFont') -> Tuple[int, int]:
def score(candidate: CoreTextFont) -> Tuple[int, int]:
style_match = 1 if candidate['bold'] == bold and candidate[
'italic'
] == italic else 0
@ -90,8 +84,8 @@ def resolve_family(f: str, main_family: str, bold: bool = False, italic: bool =
return f
def get_font_files(opts: Options) -> Dict[str, 'CoreTextFont']:
ans: Dict[str, 'CoreTextFont'] = {}
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',
@ -104,6 +98,6 @@ def get_font_files(opts: Options) -> Dict[str, 'CoreTextFont']:
return ans
def font_for_family(family: str) -> Tuple['CoreTextFont', bool, bool]:
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

@ -4,31 +4,27 @@
import re
from functools import lru_cache
from typing import TYPE_CHECKING, Dict, Generator, List, Optional, Tuple, cast
from typing import 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 kitty.typing import FontConfigPattern
from . import ListedFont
if TYPE_CHECKING:
from kitty.fast_data_types import FontConfigPattern as F
FontConfigPattern = F
attr_map = {(False, False): 'font_family',
(True, False): 'bold_font',
(False, True): 'italic_font',
(True, True): 'bold_italic_font'}
FontMap = Dict[str, Dict[str, List['FontConfigPattern']]]
FontMap = Dict[str, Dict[str, List[FontConfigPattern]]]
def create_font_map(all_fonts: Tuple['FontConfigPattern', ...]) -> FontMap:
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:
@ -69,15 +65,15 @@ def family_name_to_key(family: str) -> str:
@lru_cache()
def fc_match(family: str, bold: bool, italic: bool, spacing: int = FC_MONO) -> 'FontConfigPattern':
def fc_match(family: str, bold: bool, italic: bool, spacing: int = FC_MONO) -> FontConfigPattern:
return fc_match_impl(family, bold, italic, spacing)
def find_best_match(family: str, bold: bool = False, italic: bool = False, monospaced: bool = True) -> 'FontConfigPattern':
def find_best_match(family: str, bold: bool = False, italic: bool = False, monospaced: bool = True) -> FontConfigPattern:
q = family_name_to_key(family)
font_map = all_fonts_map(monospaced)
def score(candidate: 'FontConfigPattern') -> Tuple[int, int]:
def score(candidate: FontConfigPattern) -> Tuple[int, int]:
bold_score = abs((FC_WEIGHT_BOLD if bold else FC_WEIGHT_REGULAR) - candidate.get('weight', 0))
italic_score = abs((FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN) - candidate.get('slant', 0))
monospace_match = 0 if candidate.get('spacing') == 'MONO' else 1
@ -118,8 +114,8 @@ def resolve_family(f: str, main_family: str, bold: bool, italic: bool) -> str:
return f
def get_font_files(opts: Options) -> Dict[str, 'FontConfigPattern']:
ans: Dict[str, 'FontConfigPattern'] = {}
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)
@ -131,6 +127,6 @@ def get_font_files(opts: Options) -> Dict[str, 'FontConfigPattern']:
return ans
def font_for_family(family: str) -> 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,9 +6,7 @@ import ctypes
import sys
from functools import partial
from math import ceil, cos, floor, pi
from typing import (
TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union, cast
)
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
from kitty.config import defaults
from kitty.constants import is_macos
@ -21,18 +19,15 @@ from kitty.fonts.box_drawing import (
BufType, render_box_char, render_missing_glyph
)
from kitty.options_stub import Options as OptionsStub
from kitty.typing import CoreTextFont, FontConfigPattern
from kitty.utils import log_error
if is_macos:
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 # noqa
else:
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 # noqa
FontObject = Union['CoreTextFont', 'FontConfigPattern']
FontObject = Union[CoreTextFont, FontConfigPattern]
current_faces: List[Tuple[FontObject, bool, bool]] = []

8
kitty/key_encoding.py generated
View File

@ -3,7 +3,7 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import string
from typing import NamedTuple, Optional
from typing import Dict, NamedTuple, Optional
from . import fast_data_types as defines
from .key_names import key_name_aliases
@ -457,14 +457,16 @@ class KeyEvent(NamedTuple):
key: str
PRESS, REPEAT, RELEASE = 1, 2, 4
PRESS: int = 1
REPEAT: int = 2
RELEASE: int = 4
SHIFT, ALT, CTRL, SUPER = 1, 2, 4, 8
type_map = {'p': PRESS, 't': REPEAT, 'r': RELEASE}
rtype_map = {v: k for k, v in type_map.items()}
mod_map = {c: i for i, c in enumerate('ABCDEFGHIJKLMNOP')}
rmod_map = {v: k for k, v in mod_map.items()}
key_rmap = {}
key_defs = {}
key_defs: Dict[str, str] = {}
config_key_map = {}
config_mod_map = {
'SHIFT': SHIFT,

View File

@ -3,20 +3,15 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import string
from typing import (
TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional, Tuple, Union
)
from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union
from . import fast_data_types as defines
from .config import KeyAction, KeyMap, KeySpec, SequenceMap, SubSequenceMap
from .key_encoding import KEY_MAP
from .terminfo import key_as_bytes, modify_key_bytes
from .typing import ScreenType, WindowType
from .utils import base64_encode
if TYPE_CHECKING:
from .fast_data_types import Screen # noqa
from .window import Window # noqa
def modify_complex_key(name: Union[str, bytes], amt: int) -> bytes:
q = name if isinstance(name, bytes) else key_as_bytes(name)
@ -149,7 +144,7 @@ rmkx_key_map.update({
cursor_key_mode_map = {True: smkx_key_map, False: rmkx_key_map}
def keyboard_mode_name(screen: 'Screen') -> str:
def keyboard_mode_name(screen: ScreenType) -> str:
if screen.extended_keyboard:
return 'kitty'
return 'application' if screen.cursor_key_mode else 'normal'
@ -270,7 +265,7 @@ def key_to_bytes(key: int, smkx: bool, extended: bool, mods: int, action: int) -
return bytes(data)
def interpret_key_event(key: int, native_key: int, mods: int, window: 'Window', action: int) -> bytes:
def interpret_key_event(key: int, native_key: int, mods: int, window: WindowType, action: int) -> bytes:
screen = window.screen
if (
action == defines.GLFW_PRESS or

View File

@ -5,7 +5,7 @@
from functools import lru_cache, partial
from itertools import islice, repeat
from typing import (
TYPE_CHECKING, Callable, Collection, Deque, Dict, FrozenSet, Generator,
Callable, Collection, Deque, Dict, FrozenSet, Generator,
Iterable, List, NamedTuple, Optional, Sequence, Tuple, Union, cast
)
@ -14,20 +14,7 @@ from .fast_data_types import (
Region, set_active_window, swap_windows, viewport_for_window
)
from .options_stub import Options
try:
from typing import TypedDict, Literal
EdgeLiteral = Literal['left', 'top', 'right', 'bottom']
except ImportError:
TypedDict = dict
EdgeLiteral = str # type: ignore
if TYPE_CHECKING:
from .window import Window
Window
else:
Window = object
from .typing import WindowType, EdgeLiteral, TypedDict
class Borders(NamedTuple):
@ -53,7 +40,7 @@ draw_active_borders = True
align_top_left = False
LayoutDimension = Generator[Tuple[int, int], None, None]
DecorationPairs = Sequence[Tuple[int, int]]
WindowList = Union[List[Window], Deque[Window]]
WindowList = Union[List[WindowType], Deque[WindowType]]
class InternalNeighborsMap(TypedDict):
@ -70,7 +57,7 @@ class NeighborsMap(TypedDict):
bottom: Tuple[int, ...]
def idx_for_id(win_id: int, windows: Iterable[Window]) -> Optional[int]:
def idx_for_id(win_id: int, windows: Iterable[WindowType]) -> Optional[int]:
for i, w in enumerate(windows):
if w.id == win_id:
return i
@ -134,7 +121,7 @@ class Rect(NamedTuple):
bottom: int
def process_overlaid_windows(all_windows: WindowList) -> Tuple[FrozenSet[Window], WindowList]:
def process_overlaid_windows(all_windows: WindowList) -> Tuple[FrozenSet[WindowType], WindowList]:
id_map = {w.id: w for w in all_windows}
overlaid_windows = frozenset(w for w in all_windows if w.overlay_window_id is not None and w.overlay_window_id in id_map)
windows = [w for w in all_windows if w not in overlaid_windows]
@ -151,25 +138,25 @@ def layout_single_window(xdecoration_pairs: DecorationPairs, ydecoration_pairs:
return window_geometry(xstart, xnum, ystart, ynum)
def left_blank_rect(w: Window, rects: List[Rect]) -> None:
def left_blank_rect(w: WindowType, rects: List[Rect]) -> None:
lt = w.geometry.left
if lt > central.left:
rects.append(Rect(central.left, central.top, lt, central.bottom + 1))
def right_blank_rect(w: Window, rects: List[Rect]) -> None:
def right_blank_rect(w: WindowType, rects: List[Rect]) -> None:
r = w.geometry.right
if r < central.right:
rects.append(Rect(r, central.top, central.right + 1, central.bottom + 1))
def top_blank_rect(w: Window, rects: List[Rect]) -> None:
def top_blank_rect(w: WindowType, rects: List[Rect]) -> None:
t = w.geometry.top
if t > central.top:
rects.append(Rect(central.left, central.top, central.right + 1, t))
def bottom_blank_rect(w: Window, rects: List[Rect]) -> None:
def bottom_blank_rect(w: WindowType, rects: List[Rect]) -> None:
b = w.geometry.bottom
# Need to use <= here as otherwise a single pixel row at the bottom of the
# window is sometimes not covered. See https://github.com/kovidgoyal/kitty/issues/506
@ -177,7 +164,7 @@ def bottom_blank_rect(w: Window, rects: List[Rect]) -> None:
rects.append(Rect(central.left, b, central.right + 1, central.bottom + 1))
def blank_rects_for_window(w: Window) -> List[Rect]:
def blank_rects_for_window(w: WindowType) -> List[Rect]:
ans: List[Rect] = []
left_blank_rect(w, ans)
top_blank_rect(w, ans)
@ -288,7 +275,7 @@ class Layout: # {{{
data[k] = v
return type(self.layout_opts)(data)
def nth_window(self, all_windows: WindowList, num: int) -> Window:
def nth_window(self, all_windows: WindowList, num: int) -> WindowType:
windows = process_overlaid_windows(all_windows)[1]
w = windows[min(num, len(windows) - 1)]
return w
@ -370,7 +357,7 @@ class Layout: # {{{
def swap_windows_in_layout(self, all_windows: WindowList, a: int, b: int) -> None:
all_windows[a], all_windows[b] = all_windows[b], all_windows[a]
def add_window(self, all_windows: WindowList, window: Window, current_active_window_idx: int, location: Optional[str] = None) -> int:
def add_window(self, all_windows: WindowList, window: WindowType, current_active_window_idx: int, location: Optional[str] = None) -> int:
active_window_idx = None
if window.overlay_for is not None:
i = idx_for_id(window.overlay_for, all_windows)
@ -390,7 +377,7 @@ class Layout: # {{{
self.set_active_window_in_os_window(active_window_idx)
return active_window_idx
def do_add_window(self, all_windows: WindowList, window: Window, current_active_window_idx: Optional[int], location: Optional[str]) -> int:
def do_add_window(self, all_windows: WindowList, window: WindowType, current_active_window_idx: Optional[int], location: Optional[str]) -> int:
active_window_idx = None
if location is not None:
if location in ('after', 'vsplit', 'hsplit') and current_active_window_idx is not None and len(all_windows) > 1:
@ -409,7 +396,7 @@ class Layout: # {{{
all_windows.append(window)
return active_window_idx
def remove_window(self, all_windows: WindowList, window: Window, current_active_window_idx: int, swapped: bool = False) -> int:
def remove_window(self, all_windows: WindowList, window: WindowType, current_active_window_idx: int, swapped: bool = False) -> int:
try:
active_window = all_windows[current_active_window_idx]
except Exception:
@ -443,7 +430,7 @@ class Layout: # {{{
self(all_windows, active_window_idx)
return self.set_active_window(all_windows, active_window_idx)
def update_visibility(self, all_windows: WindowList, active_window: Window, overlaid_windows: Optional[FrozenSet[Window]] = None) -> None:
def update_visibility(self, all_windows: WindowList, active_window: WindowType, overlaid_windows: Optional[FrozenSet[WindowType]] = None) -> None:
if overlaid_windows is None:
overlaid_windows = process_overlaid_windows(all_windows)[0]
for i, w in enumerate(all_windows):
@ -492,7 +479,7 @@ class Layout: # {{{
return cast(int, idx_for_id(active_window.id, all_windows))
# Utils {{{
def layout_single_window(self, w: Window) -> None:
def layout_single_window(self, w: WindowType) -> None:
mw = self.margin_width if self.single_window_margin_width < 0 else self.single_window_margin_width
decoration_pairs = ((self.padding_width + mw, self.padding_width + mw),)
wg = layout_single_window(decoration_pairs, decoration_pairs)
@ -521,19 +508,19 @@ class Layout: # {{{
height = central.height
return layout_dimension(top, height, cell_height, decoration_pairs, bias=bias, left_align=align_top_left)
def simple_blank_rects(self, first_window: Window, last_window: Window) -> None:
def simple_blank_rects(self, first_window: WindowType, last_window: WindowType) -> None:
br = self.blank_rects
left_blank_rect(first_window, br)
top_blank_rect(first_window, br)
right_blank_rect(last_window, br)
def between_blank_rect(self, left_window: Window, right_window: Window, vertical: bool = True) -> None:
def between_blank_rect(self, left_window: WindowType, right_window: WindowType, vertical: bool = True) -> None:
if vertical:
self.blank_rects.append(Rect(left_window.geometry.right, central.top, right_window.geometry.left, central.bottom + 1))
else:
self.blank_rects.append(Rect(central.left, left_window.geometry.top, central.right + 1, right_window.geometry.bottom))
def bottom_blank_rect(self, window: Window) -> None:
def bottom_blank_rect(self, window: WindowType) -> None:
self.blank_rects.append(Rect(window.geometry.left, window.geometry.bottom, window.geometry.right, central.bottom + 1))
# }}}
@ -543,24 +530,24 @@ class Layout: # {{{
def do_layout_all_windows(self, windows: WindowList, active_window_idx: int, all_windows: WindowList) -> None:
raise NotImplementedError()
def neighbors_for_window(self, window: Window, windows: WindowList) -> InternalNeighborsMap:
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
return {'left': [], 'right': [], 'top': [], 'bottom': []}
def compute_needs_borders_map(self, windows: WindowList, active_window: Optional[Window]) -> Dict[int, bool]:
def compute_needs_borders_map(self, windows: WindowList, active_window: Optional[WindowType]) -> Dict[int, bool]:
return {w.id: ((w is active_window and draw_active_borders) or w.needs_attention) for w in windows}
def resolve_borders(self, windows: WindowList, active_window: Optional[Window]) -> Generator[Borders, None, None]:
def resolve_borders(self, windows: WindowList, active_window: Optional[WindowType]) -> Generator[Borders, None, None]:
if draw_minimal_borders:
needs_borders_map = self.compute_needs_borders_map(windows, active_window)
yield from self.minimal_borders(windows, active_window, needs_borders_map)
else:
yield from Layout.minimal_borders(self, windows, active_window, {})
def window_independent_borders(self, windows: WindowList, active_window: Optional[Window] = None) -> Generator[Tuple[int, int, int, int], None, None]:
def window_independent_borders(self, windows: WindowList, active_window: Optional[WindowType] = None) -> Generator[Tuple[int, int, int, int], None, None]:
return
yield (0, 0, 0, 0) # type: ignore
def minimal_borders(self, windows: WindowList, active_window: Optional[Window], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
for w in windows:
if (w is active_window and draw_active_borders) or w.needs_attention:
yield all_borders
@ -592,7 +579,7 @@ class Stack(Layout): # {{{
# Tall {{{
def neighbors_for_tall_window(num_full_size_windows: int, window: Window, windows: WindowList) -> InternalNeighborsMap:
def neighbors_for_tall_window(num_full_size_windows: int, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
idx = windows.index(window)
prev = None if idx == 0 else windows[idx-1]
nxt = None if idx == len(windows) - 1 else windows[idx+1]
@ -713,10 +700,10 @@ class Tall(Layout):
# left, top and right blank rects
self.simple_blank_rects(windows[0], windows[-1])
def neighbors_for_window(self, window: Window, windows: WindowList) -> InternalNeighborsMap:
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
return neighbors_for_tall_window(self.num_full_size_windows, window, windows)
def minimal_borders(self, windows: WindowList, active_window: Optional[Window], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
last_i = len(windows) - 1
for i, w in enumerate(windows):
if needs_borders_map[w.id]:
@ -778,7 +765,7 @@ class Fat(Tall): # {{{
# left, top and right blank rects
self.simple_blank_rects(windows[0], windows[-1])
def neighbors_for_window(self, window: Window, windows: WindowList) -> InternalNeighborsMap:
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
idx = windows.index(window)
prev = None if idx == 0 else windows[idx-1]
nxt = None if idx == len(windows) - 1 else windows[idx+1]
@ -925,7 +912,7 @@ class Grid(Layout):
for i in range(ncols - 1):
self.between_blank_rect(win_col_map[i][0], win_col_map[i + 1][0])
def minimal_borders(self, windows: WindowList, active_window: Optional[Window], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
n = len(windows)
ncols, nrows, special_rows, special_col = calc_grid_size(n)
blank_row: List[Optional[int]] = [None for i in range(ncols)]
@ -962,7 +949,7 @@ class Grid(Layout):
bottom_neighbor_id is not None and not needs_borders_map[bottom_neighbor_id]
)
def neighbors_for_window(self, window: Window, windows: WindowList) -> InternalNeighborsMap:
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
n = len(windows)
if n < 4:
return neighbors_for_tall_window(1, window, windows)
@ -1051,7 +1038,7 @@ class Vertical(Layout): # {{{
# left, top and right blank rects
self.simple_blank_rects(windows[0], windows[-1])
def minimal_borders(self, windows: WindowList, active_window: Optional[Window], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
def minimal_borders(self, windows: WindowList, active_window: Optional[WindowType], needs_borders_map: Dict[int, bool]) -> Generator[Borders, None, None]:
last_i = len(windows) - 1
for i, w in enumerate(windows):
if needs_borders_map[w.id]:
@ -1065,7 +1052,7 @@ class Vertical(Layout): # {{{
else:
yield self.only_between_border
def neighbors_for_window(self, window: Window, windows: WindowList) -> InternalNeighborsMap:
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
idx = windows.index(window)
before = [] if window is windows[0] else [windows[idx-1].id]
after = [] if window is windows[-1] else [windows[idx+1].id]
@ -1220,7 +1207,7 @@ class Pair:
tuple(map(pair.balanced_add, q))
return pair
def apply_window_geometry(self, window_id: int, window_geometry: WindowGeometry, id_window_map: Dict[int, Window], id_idx_map: Dict[int, int]) -> None:
def apply_window_geometry(self, window_id: int, window_geometry: WindowGeometry, id_window_map: Dict[int, WindowType], id_idx_map: Dict[int, int]) -> None:
w = id_window_map[window_id]
w.set_geometry(id_idx_map[window_id], window_geometry)
if w.overlay_window_id is not None:
@ -1228,7 +1215,7 @@ class Pair:
if q is not None:
q.set_geometry(id_idx_map[q.id], window_geometry)
def blank_rects_for_window(self, layout_object: Layout, window: Window, left: int, top: int, width: int, height: int) -> None:
def blank_rects_for_window(self, layout_object: Layout, window: WindowType, left: int, top: int, width: int, height: int) -> None:
right = left + width - 1
bottom = top + height - 1
g = window.geometry
@ -1249,7 +1236,7 @@ class Pair:
def layout_pair(
self,
left: int, top: int, width: int, height: int,
id_window_map: Dict[int, Window],
id_window_map: Dict[int, WindowType],
id_idx_map: Dict[int, int],
layout_object: Layout
) -> None:
@ -1436,7 +1423,7 @@ class Splits(Layout):
def do_add_window(
self,
all_windows: WindowList,
window: Window,
window: WindowType,
current_active_window_idx: Optional[int],
location: Optional[str]
) -> int:
@ -1490,14 +1477,14 @@ class Splits(Layout):
pair.bias = 0.5
return True
def window_independent_borders(self, windows: WindowList, active_window: Optional[Window] = None) -> Generator[Tuple[int, int, int, int], None, None]:
def window_independent_borders(self, windows: WindowList, active_window: Optional[WindowType] = None) -> Generator[Tuple[int, int, int, int], None, None]:
if not draw_minimal_borders:
return
for pair in self.pairs_root.self_and_descendants():
if pair.between_border is not None:
yield pair.between_border
def neighbors_for_window(self, window: Window, windows: WindowList) -> InternalNeighborsMap:
def neighbors_for_window(self, window: WindowType, windows: WindowList) -> InternalNeighborsMap:
window_id = window.overlay_for or window.id
pair = self.pairs_root.pair_for_window(window_id)
ans: InternalNeighborsMap = {'left': [], 'right': [], 'top': [], 'bottom': []}

View File

@ -10,7 +10,7 @@ import types
from contextlib import suppress
from functools import partial
from typing import (
TYPE_CHECKING, Any, Dict, Generator, Iterable, List, Optional, Tuple,
Any, Dict, Generator, Iterable, List, Optional, Tuple,
Union, cast
)
@ -22,14 +22,11 @@ from .rc.base import (
PayloadGetter, all_command_names, command_for_name,
no_response as no_response_sentinel, parse_subcommand_cli
)
from .typing import BossType, WindowType
from .utils import TTYIO, parse_address_spec
if TYPE_CHECKING:
from .boss import Boss # noqa
from .window import Window # noqa
def handle_cmd(boss: 'Boss', window: Optional['Window'], serialized_cmd: str) -> Optional[Dict[str, Any]]:
def handle_cmd(boss: BossType, window: Optional[WindowType], serialized_cmd: str) -> Optional[Dict[str, Any]]:
cmd = json.loads(serialized_cmd)
v = cmd['version']
no_response = cmd.get('no_response', False)

View File

@ -4,20 +4,16 @@
import shlex
import sys
from typing import (
TYPE_CHECKING, Generator, List, NamedTuple, Optional, Tuple, Union
)
from typing import Generator, List, NamedTuple, Optional, Tuple, Union
from .cli_stub import CLIOptions
from .config_data import to_layout_names
from .constants import kitty_exe
from .layout import all_layouts
from .options_stub import Options
from .typing import SpecialWindowInstance
from .utils import log_error, resolved_shell
if TYPE_CHECKING:
from .tabs import SpecialWindowInstance # noqa
from .cli_stub import CLIOptions # noqa
class WindowSizeOpts(NamedTuple):
@ -135,7 +131,7 @@ def parse_session(raw: str, opts: Options, default_title: Optional[str] = None)
def create_sessions(
opts: Options,
args: Optional['CLIOptions'] = None,
args: Optional[CLIOptions] = None,
special_window: Optional['SpecialWindowInstance'] = None,
cwd_from: Optional[int] = None,
respect_cwd: bool = False,

View File

@ -7,7 +7,7 @@ from collections import deque
from contextlib import suppress
from functools import partial
from typing import (
TYPE_CHECKING, Deque, Dict, Generator, Iterator, List, NamedTuple,
Deque, Dict, Generator, Iterator, List, NamedTuple,
Optional, Pattern, Sequence, Tuple, cast
)
@ -27,13 +27,7 @@ from .options_stub import Options
from .tab_bar import TabBar, TabBarData
from .utils import log_error, resolved_shell
from .window import Window, WindowDict
if TYPE_CHECKING:
from .session import Session, Tab as SessionTab
SessionTab, Session
from typing import TypedDict
else:
TypedDict = dict
from .typing import TypedDict, SessionTab, SessionType
class TabDict(TypedDict):
@ -555,7 +549,7 @@ class Tab: # {{{
class TabManager: # {{{
def __init__(self, os_window_id: int, opts: Options, args: CLIOptions, startup_session: Optional['Session'] = None):
def __init__(self, os_window_id: int, opts: Options, args: CLIOptions, startup_session: Optional[SessionType] = None):
self.os_window_id = os_window_id
self.last_active_tab_id = None
self.opts, self.args = opts, args

21
kitty/typing.py Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from typing import Tuple
BossType = ChildType = TabType = WindowType = ScreenType = None
BadLineType = KeySpec = SequenceMap = KeyActionType = None
AddressFamily = PopenType = Socket = StartupCtx = None
SessionTab = SessionType = LayoutType = SpecialWindowInstance = None
MarkType = RemoteCommandType = CoreTextFont = FontConfigPattern = None
KeyEventType = ImageManagerType = KittyCommonOpts = HandlerType = None
GRT_t = GRT_a = GRT_d = GRT_f = GRT_m = GRT_o = None
ScreenSize = KittensKeyActionType = MouseEvent = AbstractEventLoop = None
TermManagerType = LoopType = Debug = GraphicsCommandType = None
CompletedProcess = Tuple
TypedDict = dict
EdgeLiteral = str
Protocol = object

51
kitty/typing.pyi Normal file
View File

@ -0,0 +1,51 @@
from asyncio import AbstractEventLoop as AbstractEventLoop # noqa
from socket import AddressFamily as AddressFamily, socket as Socket # noqa
from typing import ( # noqa
Literal, Protocol as Protocol, TypedDict as TypedDict
)
from kittens.hints.main import Mark as MarkType # noqa
from kittens.tui.handler import Handler as HandlerType # noqa
from kittens.tui.images import ( # noqa
GraphicsCommand as GraphicsCommandType, ImageManager as ImageManagerType
)
from kittens.tui.loop import ( # noqa
Debug as Debug, Loop as LoopType, MouseEvent as MouseEvent,
TermManager as TermManagerType
)
from kitty.conf.utils import KittensKeyAction as KittensKeyActionType # noqa
from .boss import Boss as BossType # noqa
from .child import Child as ChildType # noqa
from .conf.utils import BadLine as BadLineType # noqa
from .fast_data_types import ( # noqa
CoreTextFont as CoreTextFont, FontConfigPattern as FontConfigPattern,
Screen as ScreenType, StartupCtx as StartupCtx
)
from .key_encoding import KeyEvent as KeyEventType # noqa
from .layout import Layout as LayoutType # noqa
from .rc.base import RemoteCommand as RemoteCommandType # noqa
from .session import Session as SessionType, Tab as SessionTab # noqa
from .tabs import ( # noqa
SpecialWindowInstance as SpecialWindowInstance, Tab as TabType
)
from .utils import ScreenSize as ScreenSize # noqa
from .window import Window as WindowType # noqa
from subprocess import ( # noqa; noqa
CompletedProcess as CompletedProcess, Popen as PopenType
)
from .config import ( # noqa; noqa
KeyAction as KeyActionType, KeyMap as KeyMap, KeySpec as KeySpec,
KittyCommonOpts as KittyCommonOpts, SequenceMap as SequenceMap
)
EdgeLiteral = Literal['left', 'top', 'right', 'bottom']
GRT_a = Literal['t', 'T', 'q', 'p', 'd']
GRT_f = Literal[24, 32, 100]
GRT_t = Literal['d', 'f', 't', 's']
GRT_o = Literal['z']
GRT_m = Literal[0, 1]
GRT_d = Literal['a', 'A', 'c', 'C', 'i', 'I', 'p', 'P', 'q', 'Q', 'x', 'X', 'y', 'Y', 'z', 'Z']

View File

@ -14,8 +14,8 @@ from contextlib import suppress
from functools import lru_cache
from time import monotonic
from typing import (
TYPE_CHECKING, Any, Callable, Dict, Generator, Iterable, List,
NamedTuple, Optional, Tuple, Union, cast
Any, Callable, Dict, Generator, Iterable, List, NamedTuple, Optional,
Tuple, Union, cast
)
from .constants import (
@ -23,11 +23,7 @@ from .constants import (
)
from .options_stub import Options
from .rgb import Color, to_color
if TYPE_CHECKING:
from subprocess import Popen # noqa
from .fast_data_types import StartupCtx # noqa
from socket import socket as Socket, AddressFamily # noqa
from .typing import AddressFamily, PopenType, Socket, StartupCtx
BASE = os.path.dirname(os.path.abspath(__file__))
@ -182,7 +178,7 @@ def command_for_open(program: Union[str, List[str]] = 'default') -> List[str]:
return cmd
def open_cmd(cmd: Union[Iterable[str], List[str]], arg: Union[None, Iterable[str], str] = None, cwd: Optional[str] = None) -> 'Popen':
def open_cmd(cmd: Union[Iterable[str], List[str]], arg: Union[None, Iterable[str], str] = None, cwd: Optional[str] = None) -> PopenType:
import subprocess
if arg is not None:
cmd = list(cmd)
@ -193,7 +189,7 @@ def open_cmd(cmd: Union[Iterable[str], List[str]], arg: Union[None, Iterable[str
return subprocess.Popen(tuple(cmd), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cwd or None)
def open_url(url: str, program: Union[str, List[str]] = 'default', cwd: Optional[str] = None) -> 'Popen':
def open_url(url: str, program: Union[str, List[str]] = 'default', cwd: Optional[str] = None) -> PopenType:
return open_cmd(command_for_open(program), url, cwd=cwd)
@ -367,7 +363,7 @@ class SingleInstance:
single_instance = SingleInstance()
def parse_address_spec(spec: str) -> Tuple['AddressFamily', Union[Tuple[str, int], str], Optional[str]]:
def parse_address_spec(spec: str) -> Tuple[AddressFamily, Union[Tuple[str, int], str], Optional[str]]:
import socket
protocol, rest = spec.split(':', 1)
socket_path = None

View File

@ -10,8 +10,8 @@ from collections import deque
from enum import IntEnum
from itertools import chain
from typing import (
TYPE_CHECKING, Any, Callable, Deque, Dict, List, Optional, Pattern,
Sequence, Tuple, Union
Any, Callable, Deque, Dict, List, Optional, Pattern, Sequence, Tuple,
Union
)
from .child import ProcessDesc
@ -32,20 +32,13 @@ from .keys import defines, extended_key_event, keyboard_mode_name
from .options_stub import Options
from .rgb import to_color
from .terminfo import get_capabilities
from .typing import ChildType, TabType, TypedDict
from .utils import (
color_as_int, get_primary_selection, load_shaders, open_cmd, open_url,
parse_color_set, read_shell_environment, sanitize_title,
set_primary_selection
)
if TYPE_CHECKING:
from .tabs import Tab
from .child import Child
from typing import TypedDict
Tab, Child
else:
TypedDict = dict
MatchPatternType = Union[Pattern[str], Tuple[Pattern[str], Optional[Pattern[str]]]]
@ -183,8 +176,8 @@ class Window:
def __init__(
self,
tab: 'Tab',
child: 'Child',
tab: TabType,
child: ChildType,
opts: Options,
args: CLIOptions,
override_title: Optional[str] = None,
@ -207,7 +200,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: Callable[[], Optional['Tab']] = weakref.ref(tab)
self.tabref: Callable[[], Optional[TabType]] = weakref.ref(tab)
self.clipboard_control_buffers = {'p': '', 'c': ''}
self.destroyed = False
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
@ -221,7 +214,7 @@ class Window:
else:
setup_colors(self.screen, opts)
def change_tab(self, tab: 'Tab') -> None:
def change_tab(self, tab: TabType) -> None:
self.tab_id = tab.id
self.os_window_id = tab.os_window_id
self.tabref = weakref.ref(tab)