More typing work
This commit is contained in:
parent
bfbb3c7068
commit
ce94a9b2df
@ -7,7 +7,7 @@ import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List, Tuple
|
||||
from typing import List, Set, Tuple
|
||||
|
||||
SHELL_SCRIPT = '''\
|
||||
#!/bin/sh
|
||||
@ -43,10 +43,12 @@ exec -a "-$shell_name" "$0"
|
||||
'''
|
||||
|
||||
|
||||
def get_ssh_cli():
|
||||
def get_ssh_cli() -> Tuple[Set[str], Set[str]]:
|
||||
other_ssh_args: List[str] = []
|
||||
boolean_ssh_args: List[str] = []
|
||||
raw = subprocess.Popen(['ssh'], stderr=subprocess.PIPE).stderr.read().decode('utf-8')
|
||||
stderr = subprocess.Popen(['ssh'], stderr=subprocess.PIPE).stderr
|
||||
assert stderr is not None
|
||||
raw = stderr.read().decode('utf-8')
|
||||
for m in re.finditer(r'\[(.+?)\]', raw):
|
||||
q = m.group(1)
|
||||
if len(q) < 2 or q[0] != '-':
|
||||
|
||||
@ -3,19 +3,36 @@
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from typing import TYPE_CHECKING, Callable, Optional, Type
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Callable, ContextManager, Dict, Optional, Sequence, Type,
|
||||
Union
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from kitty.utils import ScreenSize
|
||||
ScreenSize
|
||||
from .loop import TermManager, Loop, Debug, MouseEvent
|
||||
from .images import ImageManager
|
||||
from kitty.config import KeyAction
|
||||
from kitty.boss import Boss
|
||||
from kitty.key_encoding import KeyEvent
|
||||
from types import TracebackType
|
||||
ScreenSize, TermManager, Loop, Debug, KeyAction, KeyEvent, MouseEvent, TracebackType, Boss, ImageManager
|
||||
import asyncio
|
||||
|
||||
|
||||
class Handler:
|
||||
|
||||
image_manager_class: Optional[Type['ImageManagerBase']] = None
|
||||
image_manager_class: Optional[Type['ImageManager']] = None
|
||||
|
||||
def _initialize(self, screen_size: 'ScreenSize', term_manager, schedule_write, tui_loop, debug, image_manager=None):
|
||||
def _initialize(
|
||||
self,
|
||||
screen_size: 'ScreenSize',
|
||||
term_manager: 'TermManager',
|
||||
schedule_write: Callable[[bytes], None],
|
||||
tui_loop: 'Loop',
|
||||
debug: 'Debug',
|
||||
image_manager: Optional['ImageManager'] = None
|
||||
) -> None:
|
||||
from .operations import commander
|
||||
self.screen_size = screen_size
|
||||
self._term_manager = term_manager
|
||||
@ -23,13 +40,18 @@ class Handler:
|
||||
self._schedule_write = schedule_write
|
||||
self.debug = debug
|
||||
self.cmd = commander(self)
|
||||
self.image_manager = image_manager
|
||||
self._image_manager = image_manager
|
||||
|
||||
@property
|
||||
def asyncio_loop(self):
|
||||
def image_manager(self) -> 'ImageManager':
|
||||
assert self._image_manager is not None
|
||||
return self._image_manager
|
||||
|
||||
@property
|
||||
def asyncio_loop(self) -> 'asyncio.AbstractEventLoop':
|
||||
return self._tui_loop.asycio_loop
|
||||
|
||||
def add_shortcut(self, action, key, mods=None, is_text=False):
|
||||
def add_shortcut(self, action: 'KeyAction', 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:
|
||||
@ -37,104 +59,95 @@ class Handler:
|
||||
else:
|
||||
self._key_shortcuts[(key, mods or 0)] = action
|
||||
|
||||
def shortcut_action(self, key_event_or_text):
|
||||
def shortcut_action(self, key_event_or_text: Union[str, 'KeyEvent']) -> Optional['KeyAction']:
|
||||
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))
|
||||
|
||||
def __enter__(self):
|
||||
if self.image_manager is not None:
|
||||
self.image_manager.__enter__()
|
||||
def __enter__(self) -> None:
|
||||
if self._image_manager is not None:
|
||||
self._image_manager.__enter__()
|
||||
self.debug.fobj = self
|
||||
self.initialize()
|
||||
|
||||
def __exit__(self, etype, value, tb):
|
||||
def __exit__(self, etype: type, value: Exception, tb: 'TracebackType') -> None:
|
||||
del self.debug.fobj
|
||||
self.finalize()
|
||||
if self.image_manager is not None:
|
||||
self.image_manager.__exit__(etype, value, tb)
|
||||
if self._image_manager is not None:
|
||||
self._image_manager.__exit__(etype, value, tb)
|
||||
|
||||
def initialize(self):
|
||||
def initialize(self) -> None:
|
||||
pass
|
||||
|
||||
def finalize(self):
|
||||
def finalize(self) -> None:
|
||||
pass
|
||||
|
||||
def on_resize(self, screen_size):
|
||||
def on_resize(self, screen_size: 'ScreenSize') -> None:
|
||||
self.screen_size = screen_size
|
||||
|
||||
def quit_loop(self, return_code=None):
|
||||
def quit_loop(self, return_code: Optional[int] = None) -> None:
|
||||
self._tui_loop.quit(return_code)
|
||||
|
||||
def on_term(self):
|
||||
def on_term(self) -> None:
|
||||
self._tui_loop.quit(1)
|
||||
|
||||
def on_text(self, text, in_bracketed_paste=False):
|
||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||
pass
|
||||
|
||||
def on_key(self, key_event):
|
||||
def on_key(self, key_event: 'KeyEvent') -> None:
|
||||
pass
|
||||
|
||||
def on_mouse(self, mouse_event):
|
||||
def on_mouse(self, mouse_event: 'MouseEvent') -> None:
|
||||
pass
|
||||
|
||||
def on_interrupt(self):
|
||||
def on_interrupt(self) -> None:
|
||||
pass
|
||||
|
||||
def on_eot(self):
|
||||
def on_eot(self) -> None:
|
||||
pass
|
||||
|
||||
def on_kitty_cmd_response(self, response):
|
||||
def on_kitty_cmd_response(self, response: Dict) -> None:
|
||||
pass
|
||||
|
||||
def on_clipboard_response(self, text, from_primary=False):
|
||||
def on_clipboard_response(self, text: str, from_primary: bool = False) -> None:
|
||||
pass
|
||||
|
||||
def on_capability_response(self, name, val):
|
||||
def on_capability_response(self, name: str, val: str) -> None:
|
||||
pass
|
||||
|
||||
def write(self, data):
|
||||
def write(self, data: Union[bytes, str]) -> None:
|
||||
if isinstance(data, str):
|
||||
data = data.encode('utf-8')
|
||||
self._schedule_write(data)
|
||||
|
||||
def print(self, *args, sep=' ', end='\r\n'):
|
||||
def flush(self) -> None:
|
||||
pass
|
||||
|
||||
def print(self, *args: object, sep: str = ' ', end: str = '\r\n') -> None:
|
||||
data = sep.join(map(str, args)) + end
|
||||
self.write(data)
|
||||
|
||||
def suspend(self):
|
||||
def suspend(self) -> ContextManager['TermManager']:
|
||||
return self._term_manager.suspend()
|
||||
|
||||
|
||||
class ImageManagerBase:
|
||||
|
||||
def __init__(self, handler: Handler):
|
||||
pass
|
||||
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
def __exit__(self, etype, value, tb):
|
||||
pass
|
||||
|
||||
|
||||
class HandleResult:
|
||||
|
||||
type_of_input: Optional[str] = None
|
||||
no_ui: bool = False
|
||||
|
||||
def __init__(self, impl, type_of_input: Optional[str], no_ui: bool):
|
||||
def __init__(self, impl: Callable, type_of_input: Optional[str], no_ui: bool):
|
||||
self.impl = impl
|
||||
self.no_ui = no_ui
|
||||
self.type_of_input = type_of_input
|
||||
|
||||
def __call__(self, args, data, target_window_id, boss):
|
||||
def __call__(self, args: Sequence[str], data: Dict, target_window_id: int, boss: 'Boss') -> Any:
|
||||
return self.impl(args, data, target_window_id, boss)
|
||||
|
||||
|
||||
def result_handler(type_of_input: Optional[str] = None, no_ui=False) -> Callable[[Callable], HandleResult]:
|
||||
def result_handler(type_of_input: Optional[str] = None, no_ui: bool = False) -> Callable[[Callable], HandleResult]:
|
||||
|
||||
def wrapper(impl):
|
||||
def wrapper(impl: Callable) -> HandleResult:
|
||||
return HandleResult(impl, type_of_input, no_ui)
|
||||
|
||||
return wrapper
|
||||
|
||||
@ -16,7 +16,6 @@ from typing import (
|
||||
|
||||
from kitty.utils import ScreenSize, fit_image
|
||||
|
||||
from .handler import ImageManagerBase
|
||||
from .operations import cursor
|
||||
|
||||
try:
|
||||
@ -192,7 +191,7 @@ class Placement(TypedDict):
|
||||
y: int
|
||||
|
||||
|
||||
class ImageManager(ImageManagerBase):
|
||||
class ImageManager:
|
||||
|
||||
def __init__(self, handler: 'Handler'):
|
||||
self.image_id_counter = count()
|
||||
|
||||
@ -2,8 +2,10 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from typing import Callable, Tuple
|
||||
|
||||
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
||||
from kitty.key_encoding import RELEASE, key_defs as K
|
||||
from kitty.key_encoding import RELEASE, KeyEvent, key_defs as K
|
||||
|
||||
HOME = K['HOME']
|
||||
END = K['END']
|
||||
@ -15,21 +17,21 @@ RIGHT = K['RIGHT']
|
||||
|
||||
class LineEdit:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.clear()
|
||||
|
||||
def clear(self):
|
||||
def clear(self) -> None:
|
||||
self.current_input = ''
|
||||
self.cursor_pos = 0
|
||||
self.pending_bell = False
|
||||
|
||||
def split_at_cursor(self, delta=0):
|
||||
def split_at_cursor(self, delta: int = 0) -> Tuple[str, str]:
|
||||
pos = max(0, self.cursor_pos + delta)
|
||||
x = truncate_point_for_length(self.current_input, pos) if pos else 0
|
||||
before, after = self.current_input[:x], self.current_input[x:]
|
||||
return before, after
|
||||
|
||||
def write(self, write, prompt=''):
|
||||
def write(self, write: Callable[[str], None], prompt: str = '') -> None:
|
||||
if self.pending_bell:
|
||||
write('\a')
|
||||
self.pending_bell = False
|
||||
@ -37,7 +39,7 @@ class LineEdit:
|
||||
write(self.current_input)
|
||||
write('\r\x1b[{}C'.format(self.cursor_pos + wcswidth(prompt)))
|
||||
|
||||
def add_text(self, text):
|
||||
def add_text(self, text: str) -> None:
|
||||
if self.current_input:
|
||||
x = truncate_point_for_length(self.current_input, self.cursor_pos) if self.cursor_pos else 0
|
||||
self.current_input = self.current_input[:x] + text + self.current_input[x:]
|
||||
@ -45,10 +47,10 @@ class LineEdit:
|
||||
self.current_input = text
|
||||
self.cursor_pos += wcswidth(text)
|
||||
|
||||
def on_text(self, text, in_bracketed_paste):
|
||||
def on_text(self, text: str, in_bracketed_paste: bool) -> None:
|
||||
self.add_text(text)
|
||||
|
||||
def backspace(self, num=1):
|
||||
def backspace(self, num: int = 1) -> bool:
|
||||
before, after = self.split_at_cursor()
|
||||
nbefore = before[:-num]
|
||||
if nbefore != before:
|
||||
@ -58,7 +60,7 @@ class LineEdit:
|
||||
self.pending_bell = True
|
||||
return False
|
||||
|
||||
def delete(self, num=1):
|
||||
def delete(self, num: int = 1) -> bool:
|
||||
before, after = self.split_at_cursor()
|
||||
nafter = after[num:]
|
||||
if nafter != after:
|
||||
@ -68,7 +70,7 @@ class LineEdit:
|
||||
self.pending_bell = True
|
||||
return False
|
||||
|
||||
def _left(self):
|
||||
def _left(self) -> None:
|
||||
if not self.current_input:
|
||||
self.cursor_pos = 0
|
||||
return
|
||||
@ -76,7 +78,7 @@ class LineEdit:
|
||||
before, after = self.split_at_cursor(-1)
|
||||
self.cursor_pos = wcswidth(before)
|
||||
|
||||
def _right(self):
|
||||
def _right(self) -> None:
|
||||
if not self.current_input:
|
||||
self.cursor_pos = 0
|
||||
return
|
||||
@ -87,33 +89,37 @@ class LineEdit:
|
||||
before, after = self.split_at_cursor(1)
|
||||
self.cursor_pos += 1 + int(wcswidth(before) == self.cursor_pos)
|
||||
|
||||
def _move_loop(self, func, num):
|
||||
def _move_loop(self, func: Callable[[], None], num: int) -> bool:
|
||||
before = self.cursor_pos
|
||||
while func() and num > 0:
|
||||
changed = False
|
||||
while num > 0:
|
||||
func()
|
||||
changed = self.cursor_pos != before
|
||||
if not changed:
|
||||
break
|
||||
num -= 1
|
||||
changed = self.cursor_pos != before
|
||||
if not changed:
|
||||
self.pending_bell = True
|
||||
return changed
|
||||
|
||||
def left(self, num=1):
|
||||
def left(self, num: int = 1) -> bool:
|
||||
return self._move_loop(self._left, num)
|
||||
|
||||
def right(self, num=1):
|
||||
def right(self, num: int = 1) -> bool:
|
||||
return self._move_loop(self._right, num)
|
||||
|
||||
def home(self):
|
||||
def home(self) -> bool:
|
||||
if self.cursor_pos:
|
||||
self.cursor_pos = 0
|
||||
return True
|
||||
return False
|
||||
|
||||
def end(self):
|
||||
def end(self) -> bool:
|
||||
orig = self.cursor_pos
|
||||
self.cursor_pos = wcswidth(self.current_input)
|
||||
return self.cursor_pos != orig
|
||||
|
||||
def on_key(self, key_event):
|
||||
def on_key(self, key_event: KeyEvent) -> bool:
|
||||
if key_event.type is RELEASE:
|
||||
return False
|
||||
elif key_event.key is HOME:
|
||||
|
||||
@ -10,69 +10,90 @@ import re
|
||||
import selectors
|
||||
import signal
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from contextlib import contextmanager
|
||||
from functools import partial
|
||||
from typing import List
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Callable, Dict, Generator, List, NamedTuple, Optional,
|
||||
Protocol
|
||||
)
|
||||
|
||||
from kitty.constants import is_macos
|
||||
from kitty.fast_data_types import (
|
||||
close_tty, normal_tty, open_tty, parse_input_from_terminal, raw_tty
|
||||
)
|
||||
from kitty.key_encoding import (
|
||||
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, backspace_key,
|
||||
decode_key_event, enter_key, key_defs as K
|
||||
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 .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
|
||||
|
||||
C, D = K['C'], K['D']
|
||||
|
||||
|
||||
def debug(*a, **kw):
|
||||
from base64 import standard_b64encode
|
||||
buf = io.StringIO()
|
||||
kw['file'] = buf
|
||||
print(*a, **kw)
|
||||
stext = buf.getvalue()
|
||||
text = b'\x1bP@kitty-print|' + standard_b64encode(stext.encode('utf-8')) + b'\x1b\\'
|
||||
fobj = getattr(debug, 'fobj', sys.stdout.buffer)
|
||||
fobj.write(text)
|
||||
if hasattr(fobj, 'flush'):
|
||||
class BinaryWrite(Protocol):
|
||||
|
||||
def write(self, data: bytes) -> None:
|
||||
pass
|
||||
|
||||
def flush(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class Debug:
|
||||
|
||||
fobj: Optional[BinaryWrite] = None
|
||||
|
||||
def __call__(self, *a: Any, **kw: Any) -> None:
|
||||
from base64 import standard_b64encode
|
||||
buf = io.StringIO()
|
||||
kw['file'] = buf
|
||||
print(*a, **kw)
|
||||
stext = buf.getvalue()
|
||||
text = b'\x1bP@kitty-print|' + standard_b64encode(stext.encode('utf-8')) + b'\x1b\\'
|
||||
fobj = self.fobj or sys.stdout.buffer
|
||||
fobj.write(text)
|
||||
fobj.flush()
|
||||
|
||||
|
||||
debug = Debug()
|
||||
|
||||
|
||||
class TermManager:
|
||||
|
||||
def __init__(self):
|
||||
self.extra_finalize = None
|
||||
def __init__(self) -> None:
|
||||
self.extra_finalize: Optional[str] = None
|
||||
|
||||
def set_state_for_loop(self, set_raw=True):
|
||||
def set_state_for_loop(self, set_raw: bool = True) -> None:
|
||||
if set_raw:
|
||||
raw_tty(self.tty_fd, self.original_termios)
|
||||
write_all(self.tty_fd, init_state())
|
||||
|
||||
def reset_state_to_original(self):
|
||||
def reset_state_to_original(self) -> None:
|
||||
normal_tty(self.tty_fd, self.original_termios)
|
||||
if self.extra_finalize:
|
||||
write_all(self.tty_fd, self.extra_finalize)
|
||||
write_all(self.tty_fd, reset_state())
|
||||
|
||||
@contextmanager
|
||||
def suspend(self):
|
||||
def suspend(self) -> Generator['TermManager', None, None]:
|
||||
self.reset_state_to_original()
|
||||
yield self
|
||||
self.set_state_for_loop()
|
||||
|
||||
def __enter__(self):
|
||||
def __enter__(self) -> 'TermManager':
|
||||
self.tty_fd, self.original_termios = open_tty()
|
||||
self.set_state_for_loop(set_raw=False)
|
||||
return self
|
||||
|
||||
def __exit__(self, *a):
|
||||
def __exit__(self, *a: object) -> None:
|
||||
self.reset_state_to_original()
|
||||
close_tty(self.tty_fd, self.original_termios)
|
||||
del self.tty_fd, self.original_termios
|
||||
@ -80,7 +101,6 @@ class TermManager:
|
||||
|
||||
LEFT, MIDDLE, RIGHT, FOURTH, FIFTH = 1, 2, 4, 8, 16
|
||||
DRAG = REPEAT
|
||||
MouseEvent = namedtuple('MouseEvent', 'x y type buttons mods')
|
||||
bmap = {0: LEFT, 1: MIDDLE, 2: RIGHT}
|
||||
MOTION_INDICATOR = 1 << 5
|
||||
EXTRA_BUTTON_INDICATOR = 1 << 6
|
||||
@ -89,10 +109,18 @@ ALT_INDICATOR = 1 << 3
|
||||
CTRL_INDICATOR = 1 << 4
|
||||
|
||||
|
||||
def decode_sgr_mouse(text):
|
||||
cb, x, y = text.split(';')
|
||||
m, y = y[-1], y[:-1]
|
||||
cb, x, y = map(int, (cb, x, y))
|
||||
class MouseEvent(NamedTuple):
|
||||
x: int
|
||||
y: int
|
||||
type: int
|
||||
buttons: int
|
||||
mods: int
|
||||
|
||||
|
||||
def decode_sgr_mouse(text: str) -> MouseEvent:
|
||||
cb_, x_, y_ = text.split(';')
|
||||
m, y_ = y_[-1], y_[:-1]
|
||||
cb, x, y = map(int, (cb_, x_, y_))
|
||||
typ = RELEASE if m == 'm' else (DRAG if cb & MOTION_INDICATOR else PRESS)
|
||||
buttons = 0
|
||||
cb3 = cb & 3
|
||||
@ -113,10 +141,10 @@ def decode_sgr_mouse(text):
|
||||
|
||||
class UnhandledException(Handler):
|
||||
|
||||
def __init__(self, tb):
|
||||
def __init__(self, tb: str) -> None:
|
||||
self.tb = tb
|
||||
|
||||
def initialize(self):
|
||||
def initialize(self) -> None:
|
||||
self.cmd.clear_screen()
|
||||
self.cmd.set_scrolling_region()
|
||||
self.cmd.set_cursor_visible(True)
|
||||
@ -125,37 +153,45 @@ class UnhandledException(Handler):
|
||||
self.write('\r\n')
|
||||
self.write('Press the Enter key to quit')
|
||||
|
||||
def on_key(self, key_event):
|
||||
def on_key(self, key_event: 'KeyEvent') -> None:
|
||||
if key_event is enter_key:
|
||||
self.quit_loop(1)
|
||||
|
||||
def on_interrupt(self):
|
||||
def on_interrupt(self) -> None:
|
||||
self.quit_loop(1)
|
||||
on_eot = on_term = on_interrupt
|
||||
|
||||
|
||||
class SignalManager:
|
||||
|
||||
def __init__(self, loop, on_winch, on_interrupt, on_term):
|
||||
def __init__(
|
||||
self,
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
on_winch: Callable,
|
||||
on_interrupt: Callable,
|
||||
on_term: Callable
|
||||
) -> None:
|
||||
self.asycio_loop = loop
|
||||
self.on_winch, self.on_interrupt, self.on_term = on_winch, on_interrupt, on_term
|
||||
|
||||
def __enter__(self):
|
||||
def __enter__(self) -> None:
|
||||
tuple(map(lambda x: self.asycio_loop.add_signal_handler(*x), (
|
||||
(signal.SIGWINCH, self.on_winch),
|
||||
(signal.SIGINT, self.on_interrupt),
|
||||
(signal.SIGTERM, self.on_term)
|
||||
)))
|
||||
|
||||
def __exit__(self, *a):
|
||||
def __exit__(self, *a: Any) -> None:
|
||||
tuple(map(self.asycio_loop.remove_signal_handler, (
|
||||
signal.SIGWINCH, signal.SIGINT, signal.SIGTERM)))
|
||||
|
||||
|
||||
class Loop:
|
||||
|
||||
def __init__(self,
|
||||
sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
|
||||
def __init__(
|
||||
self,
|
||||
sanitize_bracketed_paste: str = '[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'
|
||||
):
|
||||
if is_macos:
|
||||
# On macOS PTY devices are not supported by the KqueueSelector and
|
||||
# the PollSelector is broken, causes 100% CPU usage
|
||||
@ -177,7 +213,7 @@ class Loop:
|
||||
if self.sanitize_bracketed_paste:
|
||||
self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste)
|
||||
|
||||
def _read_ready(self, handler, fd):
|
||||
def _read_ready(self, handler: Handler, fd: int) -> None:
|
||||
try:
|
||||
bdata = os.read(fd, io.DEFAULT_BUFFER_SIZE)
|
||||
except BlockingIOError:
|
||||
@ -198,7 +234,7 @@ class Loop:
|
||||
del self.handler
|
||||
|
||||
# terminal input callbacks {{{
|
||||
def _on_text(self, text):
|
||||
def _on_text(self, text: str) -> None:
|
||||
if self.in_bracketed_paste and self.sanitize_bracketed_paste:
|
||||
text = self.sanitize_ibp_pat.sub('', text)
|
||||
|
||||
@ -217,8 +253,7 @@ class Loop:
|
||||
elif chunk:
|
||||
self.handler.on_text(chunk, self.in_bracketed_paste)
|
||||
|
||||
def _on_dcs(self, dcs):
|
||||
debug(dcs)
|
||||
def _on_dcs(self, dcs: str) -> None:
|
||||
if dcs.startswith('@kitty-cmd'):
|
||||
import json
|
||||
self.handler.on_kitty_cmd_response(json.loads(dcs[len('@kitty-cmd'):]))
|
||||
@ -233,7 +268,7 @@ class Loop:
|
||||
continue
|
||||
self.handler.on_capability_response(name, val)
|
||||
|
||||
def _on_csi(self, csi):
|
||||
def _on_csi(self, csi: str) -> None:
|
||||
q = csi[-1]
|
||||
if q in 'mM':
|
||||
if csi.startswith('<'):
|
||||
@ -250,10 +285,10 @@ class Loop:
|
||||
elif csi == '201~':
|
||||
self.in_bracketed_paste = False
|
||||
|
||||
def _on_pm(self, pm):
|
||||
def _on_pm(self, pm: str) -> None:
|
||||
pass
|
||||
|
||||
def _on_osc(self, osc):
|
||||
def _on_osc(self, osc: str) -> None:
|
||||
m = re.match(r'(\d+);', osc)
|
||||
if m is not None:
|
||||
code = int(m.group(1))
|
||||
@ -264,7 +299,7 @@ class Loop:
|
||||
from base64 import standard_b64decode
|
||||
self.handler.on_clipboard_response(standard_b64decode(rest).decode('utf-8'), from_primary)
|
||||
|
||||
def _on_apc(self, apc):
|
||||
def _on_apc(self, apc: str) -> None:
|
||||
if apc.startswith('K'):
|
||||
try:
|
||||
k = decode_key_event(apc)
|
||||
@ -284,7 +319,7 @@ class Loop:
|
||||
self.handler.image_manager.handle_response(apc)
|
||||
# }}}
|
||||
|
||||
def _write_ready(self, handler, fd):
|
||||
def _write_ready(self, handler: Handler, fd: int) -> None:
|
||||
if len(self.write_buf) > self.iov_limit:
|
||||
self.write_buf[self.iov_limit - 1] = b''.join(self.write_buf[self.iov_limit - 1:])
|
||||
del self.write_buf[self.iov_limit:]
|
||||
@ -312,24 +347,24 @@ class Loop:
|
||||
break
|
||||
del self.write_buf[:consumed]
|
||||
|
||||
def quit(self, return_code=None):
|
||||
def quit(self, return_code: Optional[int] = None) -> None:
|
||||
if return_code is not None:
|
||||
self.return_code = return_code
|
||||
self.asycio_loop.stop()
|
||||
|
||||
def loop_impl(self, handler, term_manager, image_manager=None):
|
||||
def loop_impl(self, handler: Handler, term_manager: TermManager, image_manager: Optional['ImageManager'] = None) -> Optional[str]:
|
||||
self.write_buf = []
|
||||
tty_fd = term_manager.tty_fd
|
||||
tb = None
|
||||
self.waiting_for_writes = True
|
||||
|
||||
def schedule_write(data):
|
||||
def schedule_write(data: bytes) -> None:
|
||||
self.write_buf.append(data)
|
||||
if not self.waiting_for_writes:
|
||||
self.asycio_loop.add_writer(tty_fd, self._write_ready, handler, tty_fd)
|
||||
self.waiting_for_writes = True
|
||||
|
||||
def handle_exception(loop, context):
|
||||
def handle_exception(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) -> None:
|
||||
nonlocal tb
|
||||
loop.stop()
|
||||
tb = context['message']
|
||||
@ -351,10 +386,10 @@ class Loop:
|
||||
self.asycio_loop.remove_writer(tty_fd)
|
||||
return tb
|
||||
|
||||
def loop(self, handler):
|
||||
tb = None
|
||||
def loop(self, handler: Handler) -> None:
|
||||
tb: Optional[str] = None
|
||||
|
||||
def _on_sigwinch():
|
||||
def _on_sigwinch() -> None:
|
||||
self._get_screen_size.changed = True
|
||||
handler.screen_size = self._get_screen_size()
|
||||
handler.on_resize(handler.screen_size)
|
||||
@ -376,5 +411,5 @@ class Loop:
|
||||
self.return_code = 1
|
||||
self._report_error_loop(tb, term_manager)
|
||||
|
||||
def _report_error_loop(self, tb, term_manager):
|
||||
def _report_error_loop(self, tb: str, term_manager: TermManager) -> None:
|
||||
self.loop_impl(UnhandledException(tb), term_manager)
|
||||
|
||||
@ -346,6 +346,9 @@ def request_from_clipboard(use_primary: bool = False) -> str:
|
||||
return '\x1b]52;{};?\x07'.format('p' if use_primary else 'c')
|
||||
|
||||
|
||||
# Boilerplate to make operations availble via Handler.cmd {{{
|
||||
|
||||
|
||||
def writer(handler: 'Handler', func: Callable) -> Callable:
|
||||
@wraps(func)
|
||||
def f(*a: Any, **kw: Any) -> None:
|
||||
@ -373,6 +376,8 @@ def as_type_stub() -> str:
|
||||
'from typing import * # noqa',
|
||||
'from kitty.utils import ScreenSize',
|
||||
'from kittens.tui.images import GraphicsCommand',
|
||||
'from kitty.rgb import Color',
|
||||
'import kitty.rgb',
|
||||
]
|
||||
methods = []
|
||||
for name, func in all_cmds.items():
|
||||
@ -383,3 +388,4 @@ def as_type_stub() -> str:
|
||||
ans += ['', '', 'class CMD:'] + methods
|
||||
|
||||
return '\n'.join(ans) + '\n\n\n'
|
||||
# }}}
|
||||
|
||||
9
kitty/key_encoding.py
generated
9
kitty/key_encoding.py
generated
@ -3,7 +3,7 @@
|
||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import string
|
||||
from collections import namedtuple
|
||||
from typing import NamedTuple
|
||||
|
||||
from . import fast_data_types as defines
|
||||
from .key_names import key_name_aliases
|
||||
@ -451,9 +451,14 @@ def update_encoding():
|
||||
subprocess.check_call(['yapf', '-i', __file__])
|
||||
|
||||
|
||||
class KeyEvent(NamedTuple):
|
||||
type: int
|
||||
mods: int
|
||||
key: str
|
||||
|
||||
|
||||
PRESS, REPEAT, RELEASE = 1, 2, 4
|
||||
SHIFT, ALT, CTRL, SUPER = 1, 2, 4, 8
|
||||
KeyEvent = namedtuple('KeyEvent', 'type mods key')
|
||||
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')}
|
||||
|
||||
@ -14,7 +14,7 @@ from contextlib import suppress
|
||||
from functools import lru_cache
|
||||
from time import monotonic
|
||||
from typing import (
|
||||
Any, Dict, Generator, List, NamedTuple, Optional, Tuple, cast
|
||||
Any, Dict, Generator, List, NamedTuple, Optional, Tuple, Union, cast
|
||||
)
|
||||
|
||||
from .constants import (
|
||||
@ -382,7 +382,7 @@ def parse_address_spec(spec):
|
||||
return family, address, socket_path
|
||||
|
||||
|
||||
def write_all(fd, data):
|
||||
def write_all(fd: int, data: Union[str, bytes]) -> None:
|
||||
if isinstance(data, str):
|
||||
data = data.encode('utf-8')
|
||||
while data:
|
||||
|
||||
@ -27,7 +27,7 @@ warn_unused_configs = True
|
||||
check_untyped_defs = True
|
||||
# disallow_untyped_defs = True
|
||||
|
||||
[mypy-kitty.rc.*,kitty.conf.*,kitty.fonts.*,kitty.launch,kitty.child,kitty.cli,kitty.config]
|
||||
[mypy-kitty.rc.*,kitty.conf.*,kitty.fonts.*,kittens.tui.*,kitty.launch,kitty.child,kitty.cli,kitty.config,kitty.choose_entry]
|
||||
disallow_untyped_defs = True
|
||||
|
||||
[mypy-conf]
|
||||
|
||||
7
setup.py
7
setup.py
@ -196,11 +196,14 @@ def get_sanitize_args(cc, ccver):
|
||||
return sanitize_args
|
||||
|
||||
|
||||
def test_compile(cc, *cflags, src=None):
|
||||
def test_compile(cc: str, *cflags: str, src: Optional[str] = None) -> bool:
|
||||
src = src or 'int main(void) { return 0; }'
|
||||
p = subprocess.Popen([cc] + list(cflags) + ['-x', 'c', '-o', os.devnull, '-'], stdin=subprocess.PIPE)
|
||||
stdin = p.stdin
|
||||
assert stdin is not None
|
||||
try:
|
||||
p.stdin.write(src.encode('utf-8')), p.stdin.close()
|
||||
stdin.write(src.encode('utf-8'))
|
||||
stdin.close()
|
||||
except BrokenPipeError:
|
||||
return False
|
||||
return p.wait() == 0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user