More typing work
Also use a mypy based linter when editing
This commit is contained in:
parent
9b32f18109
commit
cc1336a616
26
docs/conf.py
26
docs/conf.py
@ -7,28 +7,28 @@
|
||||
# full list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/config
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from functools import partial
|
||||
from typing import Optional, Dict, Tuple
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst.roles import set_classes
|
||||
from pygments.lexer import RegexLexer, bygroups
|
||||
from pygments.token import (
|
||||
from pygments.lexer import RegexLexer, bygroups # type: ignore
|
||||
from pygments.token import ( # type: ignore
|
||||
Comment, Keyword, Literal, Name, Number, String, Whitespace
|
||||
)
|
||||
from sphinx import addnodes
|
||||
from sphinx.environment.adapters.toctree import TocTree
|
||||
from sphinx.util.logging import getLogger
|
||||
from sphinx import addnodes # type: ignore
|
||||
from sphinx.environment.adapters.toctree import TocTree # type: ignore
|
||||
from sphinx.util.logging import getLogger # type: ignore
|
||||
|
||||
kitty_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if kitty_src not in sys.path:
|
||||
sys.path.insert(0, kitty_src)
|
||||
str_version = importlib.import_module('kitty.constants').str_version
|
||||
from kitty.constants import str_version # noqa
|
||||
|
||||
# config {{{
|
||||
# -- Project information -----------------------------------------------------
|
||||
@ -77,7 +77,7 @@ master_doc = 'index'
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
language: Optional[str] = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
@ -205,7 +205,8 @@ def num_role(which, name, rawtext, text, lineno, inliner, options={}, content=[]
|
||||
def commit_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
' Link to a github commit '
|
||||
try:
|
||||
commit_id = subprocess.check_output(f'git rev-list --max-count=1 --skip=# {text}'.split()).decode('utf-8').strip()
|
||||
commit_id = subprocess.check_output(
|
||||
f'git rev-list --max-count=1 --skip=# {text}'.split()).decode('utf-8').strip()
|
||||
except Exception:
|
||||
msg = inliner.reporter.error(
|
||||
f'GitHub commit id "{text}" not recognized.', line=lineno)
|
||||
@ -213,7 +214,8 @@ def commit_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
return [prb], [msg]
|
||||
url = f'https://github.com/kovidgoyal/kitty/commit/{commit_id}'
|
||||
set_classes(options)
|
||||
short_id = subprocess.check_output(f'git rev-list --max-count=1 --abbrev-commit --skip=# {commit_id}'.split()).decode('utf-8').strip()
|
||||
short_id = subprocess.check_output(
|
||||
f'git rev-list --max-count=1 --abbrev-commit --skip=# {commit_id}'.split()).decode('utf-8').strip()
|
||||
node = nodes.reference(rawtext, f'commit: {short_id}', refuri=url, **options)
|
||||
return [node], []
|
||||
# }}}
|
||||
@ -409,8 +411,8 @@ def expand_opt_references(conf_name, text):
|
||||
return re.sub(r':opt:`(.+?)`', expand, text)
|
||||
|
||||
|
||||
opt_aliases = {}
|
||||
shortcut_slugs = {}
|
||||
opt_aliases: Dict[str, str] = {}
|
||||
shortcut_slugs: Dict[str, Tuple[str, str]] = {}
|
||||
|
||||
|
||||
def parse_opt_node(env, sig, signode):
|
||||
|
||||
@ -10,6 +10,7 @@ from contextlib import suppress
|
||||
from functools import partial
|
||||
from gettext import gettext as _
|
||||
from weakref import WeakValueDictionary
|
||||
from typing import Optional
|
||||
|
||||
from .child import cached_process_data, cwd_of_process
|
||||
from .cli import create_opts, parse_args
|
||||
@ -43,8 +44,8 @@ from .window import Window
|
||||
|
||||
def notification_activated(identifier: str) -> None:
|
||||
if identifier == 'new-version':
|
||||
from .update_check import notification_activated
|
||||
notification_activated()
|
||||
from .update_check import notification_activated as do
|
||||
do()
|
||||
|
||||
|
||||
def listen_on(spec: str) -> int:
|
||||
@ -57,7 +58,7 @@ def listen_on(spec: str) -> int:
|
||||
return s.fileno()
|
||||
|
||||
|
||||
def data_for_at(w: Window, arg: str, add_wrap_markers: bool = False) -> str:
|
||||
def data_for_at(w: Window, arg: str, add_wrap_markers: bool = False) -> Optional[str]:
|
||||
def as_text(**kw) -> str:
|
||||
kw['add_wrap_markers'] = add_wrap_markers
|
||||
return w.as_text(**kw)
|
||||
@ -80,6 +81,7 @@ def data_for_at(w: Window, arg: str, add_wrap_markers: bool = False) -> str:
|
||||
return as_text(as_ansi=True, alternate_screen=True)
|
||||
if arg == '@ansi_alternate_scrollback':
|
||||
return as_text(as_ansi=True, alternate_screen=True, add_history=True)
|
||||
return None
|
||||
|
||||
|
||||
class DumpCommands: # {{{
|
||||
@ -935,7 +937,7 @@ class Boss:
|
||||
stdin = stdin.encode('utf-8')
|
||||
return env, stdin
|
||||
|
||||
def data_for_at(self, which, window=None, add_wrap_markers=False):
|
||||
def data_for_at(self, which, window=None, add_wrap_markers=False) -> Optional[str]:
|
||||
return data_for_at(window or self.active_window, which, add_wrap_markers=add_wrap_markers)
|
||||
|
||||
def special_window_for_cmd(self, cmd, window=None, stdin=None, cwd_from=None, as_overlay=False):
|
||||
|
||||
@ -7,7 +7,7 @@ import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager, suppress
|
||||
from typing import DefaultDict, List
|
||||
from typing import DefaultDict, List, Optional
|
||||
|
||||
import kitty.fast_data_types as fast_data_types
|
||||
|
||||
@ -158,7 +158,8 @@ def openpty():
|
||||
|
||||
class Child:
|
||||
|
||||
child_fd = pid = None
|
||||
child_fd: Optional[int] = None
|
||||
pid: Optional[int] = None
|
||||
forked = False
|
||||
|
||||
def __init__(self, argv, cwd, opts, stdin=None, env=None, cwd_from=None, allow_remote_control=False):
|
||||
|
||||
24
kitty/cli.py
24
kitty/cli.py
@ -7,13 +7,13 @@ import re
|
||||
import sys
|
||||
from collections import deque
|
||||
from typing import (
|
||||
Any, Callable, Dict, FrozenSet, Iterator, List, Optional, Tuple, Type,
|
||||
TypeVar, Union, cast
|
||||
Any, Callable, Dict, FrozenSet, Iterator, List, Optional, Sequence, Tuple,
|
||||
Type, TypeVar, Union, cast
|
||||
)
|
||||
|
||||
from .cli_stub import CLIOptions
|
||||
from .conf.utils import resolve_config
|
||||
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
||||
from .cli_stub import CLIOptions
|
||||
from .options_stub import Options as OptionsStub
|
||||
|
||||
try:
|
||||
@ -135,7 +135,8 @@ def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, Option
|
||||
disabled: OptionSpecSeq = []
|
||||
mpat = re.compile('([a-z]+)=(.+)')
|
||||
current_cmd: OptionDict = {
|
||||
'dest': '', 'aliases': frozenset(), 'help': '', 'choices': frozenset(), 'type': '', 'condition': False, 'default': None
|
||||
'dest': '', 'aliases': frozenset(), 'help': '', 'choices': frozenset(),
|
||||
'type': '', 'condition': False, 'default': None
|
||||
}
|
||||
empty_cmd = current_cmd
|
||||
|
||||
@ -150,7 +151,8 @@ def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, Option
|
||||
if line.startswith('--'):
|
||||
parts = line.split(' ')
|
||||
current_cmd = {
|
||||
'dest': parts[0][2:].replace('-', '_'), 'aliases': frozenset(parts), 'help': '', 'choices': frozenset(), 'type': '',
|
||||
'dest': parts[0][2:].replace('-', '_'), 'aliases': frozenset(parts), 'help': '',
|
||||
'choices': frozenset(), 'type': '',
|
||||
'default': None, 'condition': True
|
||||
}
|
||||
state = METADATA
|
||||
@ -335,7 +337,13 @@ class PrintHelpForSeq:
|
||||
print_help_for_seq = PrintHelpForSeq()
|
||||
|
||||
|
||||
def seq_as_rst(seq: OptionSpecSeq, usage: Optional[str], message: Optional[str], appname: Optional[str], heading_char: str = '-') -> str:
|
||||
def seq_as_rst(
|
||||
seq: OptionSpecSeq,
|
||||
usage: Optional[str],
|
||||
message: Optional[str],
|
||||
appname: Optional[str],
|
||||
heading_char: str = '-'
|
||||
) -> str:
|
||||
import textwrap
|
||||
blocks: List[str] = []
|
||||
a = blocks.append
|
||||
@ -382,7 +390,7 @@ def seq_as_rst(seq: OptionSpecSeq, usage: Optional[str], message: Optional[str],
|
||||
return text
|
||||
|
||||
|
||||
def as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str) -> str:
|
||||
def as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str, extra_fields: Sequence[str] = ()) -> str:
|
||||
from itertools import chain
|
||||
ans: List[str] = ['class {}:'.format(class_name)]
|
||||
for opt in chain(seq, disabled):
|
||||
@ -401,6 +409,8 @@ def as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str) -
|
||||
else:
|
||||
raise ValueError('Unknown CLI option type: {}'.format(otype))
|
||||
ans.append(' {}: {}'.format(name, t))
|
||||
for x in extra_fields:
|
||||
ans.append(' {}'.format(x))
|
||||
return '\n'.join(ans) + '\n\n\n'
|
||||
|
||||
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
class CLIOptions:
|
||||
pass
|
||||
|
||||
@ -17,9 +20,9 @@ def generate_stub() -> None:
|
||||
from .conf.definition import save_type_stub
|
||||
text = 'import typing\n\n\n'
|
||||
|
||||
def do(otext=None, cls: str = 'CLIOptions'):
|
||||
def do(otext=None, cls: str = 'CLIOptions', extra_fields: Sequence[str] = ()):
|
||||
nonlocal text
|
||||
text += as_type_stub(*parse_option_spec(otext), class_name=cls)
|
||||
text += as_type_stub(*parse_option_spec(otext), class_name=cls, extra_fields=extra_fields)
|
||||
|
||||
do()
|
||||
|
||||
@ -27,7 +30,7 @@ def generate_stub() -> None:
|
||||
do(options_spec(), 'LaunchCLIOptions')
|
||||
|
||||
from .remote_control import global_options_spec
|
||||
do(global_options_spec(), 'RCOptions')
|
||||
do(global_options_spec(), 'RCOptions', extra_fields=['no_command_response: typing.Optional[bool]'])
|
||||
|
||||
from kittens.ask.main import option_text
|
||||
do(option_text(), 'AskCLIOptions')
|
||||
|
||||
@ -6,7 +6,7 @@ import os
|
||||
import shlex
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from typing import Tuple
|
||||
from typing import Callable, Dict, Tuple
|
||||
|
||||
from kittens.runner import all_kitten_names, get_kitten_cli_docs
|
||||
|
||||
@ -33,7 +33,8 @@ taking the results from kitty's completion system and converting
|
||||
them into something your shell will understand.
|
||||
'''
|
||||
|
||||
parsers, serializers = {}, {}
|
||||
parsers: Dict[str, Callable] = {}
|
||||
serializers: Dict[str, Callable] = {}
|
||||
|
||||
|
||||
def debug(*a, **kw):
|
||||
|
||||
@ -7,7 +7,7 @@ import re
|
||||
import shlex
|
||||
from collections import namedtuple
|
||||
from typing import (
|
||||
Any, Callable, Dict, FrozenSet, Iterator, List, Optional, Sequence, Tuple,
|
||||
Any, Callable, Dict, FrozenSet, Iterable, List, Optional, Sequence, Tuple,
|
||||
Type, Union
|
||||
)
|
||||
|
||||
@ -118,7 +118,7 @@ def parse_line(
|
||||
|
||||
|
||||
def _parse(
|
||||
lines: Iterator[str],
|
||||
lines: Iterable[str],
|
||||
type_convert: Callable[[str, Any], Any],
|
||||
special_handling: Callable,
|
||||
ans: Dict[str, Any],
|
||||
@ -144,7 +144,7 @@ def _parse(
|
||||
|
||||
|
||||
def parse_config_base(
|
||||
lines: Iterator[str],
|
||||
lines: Iterable[str],
|
||||
defaults: Any,
|
||||
type_convert: Callable[[str, Any], Any],
|
||||
special_handling: Callable,
|
||||
@ -158,7 +158,7 @@ def parse_config_base(
|
||||
)
|
||||
|
||||
|
||||
def create_options_class(all_keys: Iterator[str]) -> Type:
|
||||
def create_options_class(all_keys: Iterable[str]) -> Type:
|
||||
keys = tuple(sorted(all_keys))
|
||||
slots = keys + ('_fields', )
|
||||
|
||||
@ -223,10 +223,10 @@ def resolve_config(SYSTEM_CONF: str, defconf: str, config_files_on_cmd_line: Seq
|
||||
def load_config(
|
||||
Options: Type,
|
||||
defaults: Any,
|
||||
parse_config: Callable[[Iterator[str]], Dict[str, Any]],
|
||||
parse_config: Callable[[Iterable[str]], Dict[str, Any]],
|
||||
merge_configs: Callable[[Dict, Dict], Dict],
|
||||
*paths: str,
|
||||
overrides: Optional[Iterator[str]] = None
|
||||
overrides: Optional[Iterable[str]] = None
|
||||
):
|
||||
ans: Dict = defaults._asdict()
|
||||
for path in paths:
|
||||
@ -244,7 +244,7 @@ def load_config(
|
||||
return Options(ans)
|
||||
|
||||
|
||||
def init_config(default_config_lines: Iterator[str], parse_config: Callable):
|
||||
def init_config(default_config_lines: Iterable[str], parse_config: Callable):
|
||||
defaults = parse_config(default_config_lines, check_keys=False)
|
||||
Options = create_options_class(defaults.keys())
|
||||
defaults = Options(defaults)
|
||||
|
||||
@ -10,8 +10,8 @@ from collections import namedtuple
|
||||
from contextlib import contextmanager, suppress
|
||||
from functools import partial
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Sequence, Tuple, Type,
|
||||
cast
|
||||
TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional,
|
||||
Sequence, Set, Tuple, Type, cast
|
||||
)
|
||||
|
||||
from . import fast_data_types as defines
|
||||
@ -310,7 +310,7 @@ def scroll_to_mark(func, rest):
|
||||
return func, [parts[0] != 'next', max(0, min(int(parts[1]), 3))]
|
||||
|
||||
|
||||
def parse_key_action(action):
|
||||
def parse_key_action(action: str) -> Optional[KeyAction]:
|
||||
parts = action.strip().split(maxsplit=1)
|
||||
func = parts[0]
|
||||
if len(parts) == 1:
|
||||
@ -324,9 +324,10 @@ def parse_key_action(action):
|
||||
log_error('Ignoring invalid key action: {} with err: {}'.format(action, err))
|
||||
else:
|
||||
return KeyAction(func, args)
|
||||
return None
|
||||
|
||||
|
||||
all_key_actions = set()
|
||||
all_key_actions: Set[str] = set()
|
||||
sequence_sep = '>'
|
||||
|
||||
|
||||
@ -454,16 +455,17 @@ def parse_send_text(val, key_definitions):
|
||||
return parse_key(key_str, key_definitions)
|
||||
|
||||
|
||||
special_handlers = {}
|
||||
SpecialHandlerFunc = Callable[[str, str, Dict[str, Any]], None]
|
||||
special_handlers: Dict[str, SpecialHandlerFunc] = {}
|
||||
|
||||
|
||||
def special_handler(func):
|
||||
def special_handler(func: SpecialHandlerFunc) -> SpecialHandlerFunc:
|
||||
special_handlers[func.__name__.partition('_')[2]] = func
|
||||
return func
|
||||
|
||||
|
||||
def deprecated_handler(*names):
|
||||
def special_handler(func):
|
||||
def deprecated_handler(*names: str) -> Callable[[SpecialHandlerFunc], SpecialHandlerFunc]:
|
||||
def special_handler(func: SpecialHandlerFunc) -> SpecialHandlerFunc:
|
||||
for name in names:
|
||||
special_handlers[name] = func
|
||||
return func
|
||||
@ -588,7 +590,7 @@ def option_names_for_completion():
|
||||
yield from special_handlers
|
||||
|
||||
|
||||
def parse_config(lines: Iterator[str], check_keys=True, accumulate_bad_lines: Optional[List[BadLine]] = None):
|
||||
def parse_config(lines: Iterable[str], check_keys=True, accumulate_bad_lines: Optional[List[BadLine]] = None):
|
||||
ans: Dict[str, Any] = {
|
||||
'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [],
|
||||
'env': {}, 'kitten_aliases': {}, 'font_features': {}
|
||||
@ -777,7 +779,7 @@ def finalize_keys(opts: OptionsStub) -> None:
|
||||
opts.sequence_map = sequence_map
|
||||
|
||||
|
||||
def load_config(*paths: str, overrides: Optional[Iterator[str]] = None, accumulate_bad_lines: Optional[List[BadLine]] = None) -> OptionsStub:
|
||||
def load_config(*paths: str, overrides: Optional[Iterable[str]] = None, accumulate_bad_lines: Optional[List[BadLine]] = None) -> OptionsStub:
|
||||
parser = parse_config
|
||||
if accumulate_bad_lines is not None:
|
||||
parser = partial(parse_config, accumulate_bad_lines=accumulate_bad_lines)
|
||||
|
||||
@ -175,4 +175,4 @@ supports_primary_selection = not is_macos
|
||||
def running_in_kitty(set_val: Optional[bool] = None) -> bool:
|
||||
if set_val is not None:
|
||||
setattr(running_in_kitty, 'ans', set_val)
|
||||
return getattr(running_in_kitty, 'ans', False)
|
||||
return bool(getattr(running_in_kitty, 'ans', False))
|
||||
|
||||
@ -348,7 +348,11 @@ StartupCtx = NewType('StartupCtx', int)
|
||||
Display = NewType('Display', int)
|
||||
|
||||
|
||||
def init_x11_startup_notification(display: Display, window_id: int, startup_id: Optional[str] = None) -> StartupCtx:
|
||||
def init_x11_startup_notification(
|
||||
display: Display,
|
||||
window_id: int,
|
||||
startup_id: Optional[str] = None
|
||||
) -> StartupCtx:
|
||||
pass
|
||||
|
||||
|
||||
@ -387,7 +391,10 @@ def default_color_table() -> Tuple[int, ...]:
|
||||
FontConfigPattern = Dict[str, Union[str, int, bool, float]]
|
||||
|
||||
|
||||
def fc_list(spacing: int = -1, allow_bitmapped_fonts: bool = False) -> Tuple[FontConfigPattern, ...]:
|
||||
def fc_list(
|
||||
spacing: int = -1,
|
||||
allow_bitmapped_fonts: bool = False
|
||||
) -> Tuple[FontConfigPattern, ...]:
|
||||
pass
|
||||
|
||||
|
||||
@ -407,7 +414,11 @@ def coretext_all_fonts() -> Tuple[Dict[str, Any], ...]:
|
||||
pass
|
||||
|
||||
|
||||
def add_timer(callback: Callable[[Optional[int]], bool], interval: float, repeats: bool = True) -> int:
|
||||
def add_timer(
|
||||
callback: Callable[[Optional[int]], bool],
|
||||
interval: float,
|
||||
repeats: bool = True
|
||||
) -> int:
|
||||
pass
|
||||
|
||||
|
||||
@ -419,7 +430,9 @@ def add_window(os_window_id: int, tab_id: int, title: str) -> int:
|
||||
pass
|
||||
|
||||
|
||||
def compile_program(which: int, vertex_shader: str, fragment_shader: str) -> int:
|
||||
def compile_program(
|
||||
which: int, vertex_shader: str, fragment_shader: str
|
||||
) -> int:
|
||||
pass
|
||||
|
||||
|
||||
@ -431,7 +444,10 @@ def set_titlebar_color(os_window_id: int, color: int) -> bool:
|
||||
pass
|
||||
|
||||
|
||||
def add_borders_rect(os_window_id: int, tab_id: int, left: int, top: int, right: int, bottom: int, color: int) -> None:
|
||||
def add_borders_rect(
|
||||
os_window_id: int, tab_id: int, left: int, top: int, right: int,
|
||||
bottom: int, color: int
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@ -443,16 +459,30 @@ def os_window_has_background_image(os_window_id: int) -> bool:
|
||||
pass
|
||||
|
||||
|
||||
def dbus_send_notification(app_name: str, icon: str, summary: str, body: str, action_name: str, timeout: int = -1) -> int:
|
||||
def dbus_send_notification(
|
||||
app_name: str,
|
||||
icon: str,
|
||||
summary: str,
|
||||
body: str,
|
||||
action_name: str,
|
||||
timeout: int = -1
|
||||
) -> int:
|
||||
pass
|
||||
|
||||
|
||||
def cocoa_send_notification(identifier: Optional[str], title: str, informative_text: str, path_to_img: Optional[str], subtitle: Optional[str] = None) -> None:
|
||||
def cocoa_send_notification(
|
||||
identifier: Optional[str],
|
||||
title: str,
|
||||
informative_text: str,
|
||||
path_to_img: Optional[str],
|
||||
subtitle: Optional[str] = None
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def create_os_window(
|
||||
get_window_size: Callable[[int, int, int, int, float, float], Tuple[int, int]],
|
||||
get_window_size: Callable[[int, int, int, int, float, float], Tuple[int,
|
||||
int]],
|
||||
pre_show_callback: Callable[[object], None],
|
||||
title: str,
|
||||
wm_class_name: str,
|
||||
@ -464,11 +494,16 @@ def create_os_window(
|
||||
pass
|
||||
|
||||
|
||||
def update_window_title(os_window_id: int, tab_id: int, window_id: int, title: str) -> None:
|
||||
def update_window_title(
|
||||
os_window_id: int, tab_id: int, window_id: int, title: str
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def update_window_visibility(os_window_id: int, tab_id: int, window_id: int, window_idx: int, visible: bool) -> None:
|
||||
def update_window_visibility(
|
||||
os_window_id: int, tab_id: int, window_id: int, window_idx: int,
|
||||
visible: bool
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@ -501,7 +536,12 @@ def set_default_window_icon(data: bytes, width: int, height: int) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def set_custom_cursor(cursor_type: int, images: Tuple[Tuple[bytes, int, int], ...], x: int = 0, y: int = 0) -> None:
|
||||
def set_custom_cursor(
|
||||
cursor_type: int,
|
||||
images: Tuple[Tuple[bytes, int, int], ...],
|
||||
x: int = 0,
|
||||
y: int = 0
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@ -542,8 +582,10 @@ def set_clipboard_string(data: bytes) -> None:
|
||||
|
||||
|
||||
def set_background_image(
|
||||
path: Optional[str], os_window_ids: Tuple[int, ...],
|
||||
configured: bool = True, layout_name: Optional[str] = None
|
||||
path: Optional[str],
|
||||
os_window_ids: Tuple[int, ...],
|
||||
configured: bool = True,
|
||||
layout_name: Optional[str] = None
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -564,7 +606,20 @@ def patch_global_colors(spec: Dict[str, int], configured: bool) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def os_window_font_size(os_window_id: int, new_sz: float = -1., force: bool = False) -> float:
|
||||
class ColorProfile:
|
||||
pass
|
||||
|
||||
|
||||
def patch_color_profiles(
|
||||
spec: Dict[str, int], cursor_text_color: Optional[Union[bool, int]],
|
||||
profiles: Tuple[ColorProfile, ...], change_configured: bool
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def os_window_font_size(
|
||||
os_window_id: int, new_sz: float = -1., force: bool = False
|
||||
) -> float:
|
||||
pass
|
||||
|
||||
|
||||
@ -692,7 +747,9 @@ class Region:
|
||||
pass
|
||||
|
||||
|
||||
def viewport_for_window(os_window_id: int) -> Tuple[Region, Region, int, int, int, int]:
|
||||
def viewport_for_window(
|
||||
os_window_id: int
|
||||
) -> Tuple[Region, Region, int, int, int, int]:
|
||||
pass
|
||||
|
||||
|
||||
@ -716,14 +773,10 @@ def open_tty(read_with_timeout: bool = False) -> Tuple[int, TermiosPtr]:
|
||||
|
||||
|
||||
def parse_input_from_terminal(
|
||||
text_callback: Callable[[str], None],
|
||||
dcs_callback: Callable[[str], None],
|
||||
csi_callback: Callable[[str], None],
|
||||
osc_callback: Callable[[str], None],
|
||||
pm_callback: Callable[[str], None],
|
||||
apc_callback: Callable[[str], None],
|
||||
data: str,
|
||||
in_bracketed_paste: bool
|
||||
text_callback: Callable[[str], None], dcs_callback: Callable[[str], None],
|
||||
csi_callback: Callable[[str], None], osc_callback: Callable[[str], None],
|
||||
pm_callback: Callable[[str], None], apc_callback: Callable[[str], None],
|
||||
data: str, in_bracketed_paste: bool
|
||||
):
|
||||
pass
|
||||
|
||||
@ -732,7 +785,9 @@ class Line:
|
||||
pass
|
||||
|
||||
|
||||
def test_shape(line: Line, path: Optional[str] = None, index: int = 0) -> List[Tuple[int, int, int, Tuple[int, ...]]]:
|
||||
def test_shape(line: Line,
|
||||
path: Optional[str] = None,
|
||||
index: int = 0) -> List[Tuple[int, int, int, Tuple[int, ...]]]:
|
||||
pass
|
||||
|
||||
|
||||
@ -744,17 +799,21 @@ def sprite_map_set_limits(w: int, h: int) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def set_send_sprite_to_gpu(func: Callable[[int, int, int, bytes], None]) -> None:
|
||||
def set_send_sprite_to_gpu(
|
||||
func: Callable[[int, int, int, bytes], None]
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def set_font_data(
|
||||
box_drawing_func: Callable[[int, int, int, float], Tuple[int, Union[bytearray, bytes]]],
|
||||
prerender_func: Callable[[int, int, int, int, int, float, float, float, float], Tuple[int, ...]],
|
||||
descriptor_for_idx: Callable[[int], Tuple[dict, bool, bool]],
|
||||
box_drawing_func: Callable[[int, int, int, float],
|
||||
Tuple[int, Union[bytearray, bytes]]],
|
||||
prerender_func: Callable[
|
||||
[int, int, int, int, int, float, float, float, float],
|
||||
Tuple[int, ...]], descriptor_for_idx: Callable[[int], Tuple[dict, bool,
|
||||
bool]],
|
||||
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, ...]]
|
||||
):
|
||||
pass
|
||||
@ -764,7 +823,8 @@ def get_fallback_font(text: str, bold: bool, italic: bool):
|
||||
pass
|
||||
|
||||
|
||||
def create_test_font_group(sz: float, dpix: float, dpiy: float) -> Tuple[int, int]:
|
||||
def create_test_font_group(sz: float, dpix: float,
|
||||
dpiy: float) -> Tuple[int, int]:
|
||||
pass
|
||||
|
||||
|
||||
@ -772,30 +832,35 @@ class Screen:
|
||||
pass
|
||||
|
||||
|
||||
def set_tab_bar_render_data(os_window_id: int, xstart: float, ystart: float, dx: float, dy: float, screen: Screen) -> None:
|
||||
def set_tab_bar_render_data(
|
||||
os_window_id: int, xstart: float, ystart: float, dx: float, dy: float,
|
||||
screen: Screen
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def set_window_render_data(
|
||||
os_window_id: int, tab_id: int, window_id: int, window_idx: int,
|
||||
xstart: float, ystart: float, dx: float, dy: float,
|
||||
screen: Screen,
|
||||
left: int, top: int, right: int, bottom: int
|
||||
os_window_id: int, tab_id: int, window_id: int, window_idx: int,
|
||||
xstart: float, ystart: float, dx: float, dy: float, screen: Screen,
|
||||
left: int, top: int, right: int, bottom: int
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def truncate_point_for_length(text: str, num_cells: int, start_pos: int = 0) -> int:
|
||||
def truncate_point_for_length(
|
||||
text: str, num_cells: int, start_pos: int = 0
|
||||
) -> int:
|
||||
pass
|
||||
|
||||
|
||||
class ChildMonitor:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
death_notify: Callable[[int], None],
|
||||
dump_callback: Optional[Callable],
|
||||
talk_fd: int = -1, listen_fd: int = -1
|
||||
self,
|
||||
death_notify: Callable[[int], None],
|
||||
dump_callback: Optional[Callable],
|
||||
talk_fd: int = -1,
|
||||
listen_fd: int = -1
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
@ -45,10 +45,14 @@ def all_fonts_map(monospaced=True):
|
||||
def list_fonts() -> Generator[ListedFont, None, None]:
|
||||
for fd in fc_list():
|
||||
f = fd.get('family')
|
||||
if f:
|
||||
fn = fd.get('full_name') or (f + ' ' + fd.get('style', '')).strip()
|
||||
if f and isinstance(f, str):
|
||||
fn_ = fd.get('full_name')
|
||||
if fn_:
|
||||
fn = str(fn_)
|
||||
else:
|
||||
fn = (f + ' ' + str(fd.get('style', ''))).strip()
|
||||
is_mono = fd.get('spacing') in ('MONO', 'DUAL')
|
||||
yield {'family': f, 'full_name': fn, 'postscript_name': fd.get('postscript_name', ''), 'is_monospace': is_mono}
|
||||
yield {'family': f, 'full_name': fn, 'postscript_name': str(fd.get('postscript_name', '')), 'is_monospace': is_mono}
|
||||
|
||||
|
||||
def family_name_to_key(family):
|
||||
|
||||
@ -5,7 +5,8 @@
|
||||
import ctypes
|
||||
import sys
|
||||
from functools import partial
|
||||
from math import ceil, pi, cos, floor
|
||||
from math import ceil, cos, floor, pi
|
||||
from typing import Any, List, Tuple
|
||||
|
||||
from kitty.config import defaults
|
||||
from kitty.constants import is_macos
|
||||
@ -22,7 +23,7 @@ if is_macos:
|
||||
else:
|
||||
from .fontconfig import get_font_files, font_for_family
|
||||
|
||||
current_faces = None
|
||||
current_faces: List[Tuple[Any, bool, bool]] = []
|
||||
|
||||
|
||||
def coalesce_symbol_maps(maps):
|
||||
|
||||
@ -11,10 +11,9 @@ from .terminfo import key_as_bytes, modify_key_bytes
|
||||
from .utils import base64_encode
|
||||
|
||||
|
||||
def modify_complex_key(name, amt):
|
||||
if not isinstance(name, bytes):
|
||||
name = key_as_bytes(name)
|
||||
return modify_key_bytes(name, amt)
|
||||
def modify_complex_key(name: Union[str, bytes], amt: int) -> bytes:
|
||||
q = name if isinstance(name, bytes) else key_as_bytes(name)
|
||||
return modify_key_bytes(q, amt)
|
||||
|
||||
|
||||
control_codes: Dict[int, Union[bytes, Tuple[int, ...]]] = {
|
||||
@ -48,7 +47,7 @@ SHIFTED_KEYS = {
|
||||
control_alt_codes = {
|
||||
defines.GLFW_KEY_SPACE: b'\x1b\0',
|
||||
}
|
||||
control_alt_shift_codes = {}
|
||||
control_alt_shift_codes: Dict[int, bytes] = {}
|
||||
ASCII_C0_SHIFTED = {
|
||||
# ^@
|
||||
'2': b'\x00',
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
from contextlib import suppress
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Callable, Dict, FrozenSet, Generator, List, NoReturn,
|
||||
Optional, Tuple, Union
|
||||
Optional, Tuple, Type, Union, cast
|
||||
)
|
||||
|
||||
from kitty.cli import get_defaults_from_seq, parse_args, parse_option_spec
|
||||
@ -42,12 +42,26 @@ class UnknownLayout(ValueError):
|
||||
hide_traceback = True
|
||||
|
||||
|
||||
class PayloadGetter:
|
||||
|
||||
def __init__(self, cmd: 'RemoteCommand', payload: Dict[str, Any]):
|
||||
self.payload = payload
|
||||
self.cmd = cmd
|
||||
|
||||
def __call__(self, key: str, opt_name: Optional[str] = None, missing: Any = None):
|
||||
ans = self.payload.get(key, payload_get)
|
||||
if ans is not payload_get:
|
||||
return ans
|
||||
return self.cmd.get_default(opt_name or key, missing=missing)
|
||||
|
||||
|
||||
no_response = NoResponse()
|
||||
payload_get = object()
|
||||
ResponseType = Optional[Union[bool, str]]
|
||||
CmdReturnType = Union[Dict[str, Any], List, Tuple, str, int, float, bool]
|
||||
CmdGenerator = Generator[CmdReturnType, None, None]
|
||||
PayloadType = Optional[Union[CmdReturnType, CmdGenerator]]
|
||||
PayloadGetType = Callable[[str, str], Any]
|
||||
PayloadGetType = PayloadGetter
|
||||
ArgsType = List[str]
|
||||
|
||||
|
||||
@ -106,6 +120,7 @@ class RemoteCommand:
|
||||
args_count: Optional[int] = None
|
||||
args_completion: Optional[Dict[str, Tuple[str, Tuple[str, ...]]]] = None
|
||||
defaults: Optional[Dict[str, Any]] = None
|
||||
options_class: Type = RCOptions
|
||||
|
||||
def __init__(self):
|
||||
self.desc = self.desc or self.short_desc
|
||||
@ -124,13 +139,6 @@ class RemoteCommand:
|
||||
return self.defaults.get(name, missing)
|
||||
return missing
|
||||
|
||||
def payload_get(self, payload: Dict[str, Any], key: str, opt_name: Optional[str] = None) -> Any:
|
||||
payload_get = object()
|
||||
ans = payload.get(key, payload_get)
|
||||
if ans is not payload_get:
|
||||
return ans
|
||||
return self.get_default(opt_name or key)
|
||||
|
||||
def message_to_kitty(self, global_opts: RCOptions, opts: Any, args: ArgsType) -> PayloadType:
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -143,7 +151,7 @@ def cli_params_for(command: RemoteCommand) -> Tuple[Callable[[], str], str, str,
|
||||
|
||||
|
||||
def parse_subcommand_cli(command: RemoteCommand, args: ArgsType) -> Tuple[Any, ArgsType]:
|
||||
opts, items = parse_args(args[1:], *cli_params_for(command))
|
||||
opts, items = parse_args(args[1:], *cli_params_for(command), result_class=command.options_class)
|
||||
if command.args_count is not None and command.args_count != len(items):
|
||||
if command.args_count == 0:
|
||||
raise SystemExit('Unknown extra argument(s) supplied to {}'.format(command.name))
|
||||
@ -163,17 +171,17 @@ def command_for_name(cmd_name: str) -> RemoteCommand:
|
||||
m = import_module(f'kitty.rc.{cmd_name}')
|
||||
except ImportError:
|
||||
raise KeyError(f'{cmd_name} is not a known kitty remote control command')
|
||||
return getattr(m, cmd_name)
|
||||
return cast(RemoteCommand, getattr(m, cmd_name))
|
||||
|
||||
|
||||
def all_command_names() -> FrozenSet[str]:
|
||||
try:
|
||||
from importlib.resources import contents
|
||||
except ImportError:
|
||||
from importlib_resources import contents
|
||||
from importlib_resources import contents # type:ignore
|
||||
|
||||
def ok(name: str) -> bool:
|
||||
root, _, ext = name.rpartition('.')
|
||||
return ext in ('py', 'pyc', 'pyo') and root and root not in ('base', '__init__')
|
||||
return bool(ext in ('py', 'pyc', 'pyo') and root and root not in ('base', '__init__'))
|
||||
|
||||
return frozenset({x.rpartition('.')[0] for x in filter(ok, contents('kitty.rc'))})
|
||||
|
||||
@ -39,7 +39,7 @@ If specified close the tab this command is run in, rather than the active tab.
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
else:
|
||||
tabs = [boss.tab_for_window(window) if window and payload_get('self') else boss.active_tab]
|
||||
tabs = tuple(boss.tab_for_window(window) if window and payload_get('self') else boss.active_tab)
|
||||
for tab in tabs:
|
||||
if window:
|
||||
if tab:
|
||||
|
||||
@ -38,7 +38,7 @@ If specified close the window this command is run in, rather than the active win
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
windows = [window if window and payload_get('self') else boss.active_window]
|
||||
windows = tuple(window if window and payload_get('self') else boss.active_window)
|
||||
for window in windows:
|
||||
if window:
|
||||
boss.close_window(window)
|
||||
|
||||
@ -48,7 +48,7 @@ If specified apply marker to the window this command is run in, rather than the
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
windows = [window if window and payload_get('self') else boss.active_window]
|
||||
windows = tuple(window if window and payload_get('self') else boss.active_window)
|
||||
args = payload_get('marker_spec')
|
||||
|
||||
for window in windows:
|
||||
|
||||
@ -43,7 +43,7 @@ If specified detach the tab this command is run in, rather than the active tab.
|
||||
if not tabs:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
tabs = [window.tabref() if payload_get('self') and window and window.tabref() else boss.active_tab]
|
||||
tabs = tuple(window.tabref() if payload_get('self') and window and window.tabref() else boss.active_tab)
|
||||
match = payload_get('target_tab')
|
||||
kwargs = {}
|
||||
if match:
|
||||
|
||||
@ -45,7 +45,7 @@ If specified detach the window this command is run in, rather than the active wi
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
windows = [window if window and payload_get('self') else boss.active_window]
|
||||
windows = tuple(window if window and payload_get('self') else boss.active_window)
|
||||
match = payload_get('target_tab')
|
||||
kwargs = {}
|
||||
if match:
|
||||
|
||||
@ -42,7 +42,7 @@ using this option means that you will not be notified of failures.
|
||||
if match:
|
||||
tabs = tuple(boss.match_tabs(match))
|
||||
else:
|
||||
tabs = [boss.tab_for_window(window) if window else boss.active_tab]
|
||||
tabs = tuple(boss.tab_for_window(window) if window else boss.active_tab)
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
tab = tabs[0]
|
||||
|
||||
@ -41,7 +41,7 @@ the command will exit with a success code.
|
||||
windows = [window or boss.active_window]
|
||||
match = payload_get('match')
|
||||
if match:
|
||||
windows = tuple(boss.match_windows(match))
|
||||
windows = [boss.match_windows(match)]
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
for window in windows:
|
||||
|
||||
@ -42,9 +42,9 @@ configured colors.
|
||||
def response_from_kitty(self, boss: 'Boss', window: 'Window', payload_get: PayloadGetType) -> ResponseType:
|
||||
ans = {k: getattr(boss.opts, k) for k in boss.opts if isinstance(getattr(boss.opts, k), Color)}
|
||||
if not payload_get('configured'):
|
||||
windows = (window or boss.active_window,)
|
||||
windows = [window or boss.active_window]
|
||||
if payload_get('match'):
|
||||
windows = tuple(boss.match_windows(payload_get('match')))
|
||||
windows = list(boss.match_windows(payload_get('match')))
|
||||
if not windows:
|
||||
raise MatchError(payload_get('match'))
|
||||
ans.update({k: color_from_int(v) for k, v in windows[0].current_colors.items()})
|
||||
|
||||
@ -55,7 +55,7 @@ If specified get text from the window this command is run in, rather than the ac
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
windows = [window if window and payload_get('self') else boss.active_window]
|
||||
windows = tuple(window if window and payload_get('self') else boss.active_window)
|
||||
window = windows[0]
|
||||
if payload_get('extent') == 'selection':
|
||||
ans = window.text_for_selection()
|
||||
|
||||
@ -44,7 +44,7 @@ class GotoLayout(RemoteCommand):
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
else:
|
||||
tabs = [boss.tab_for_window(window) if window else boss.active_tab]
|
||||
tabs = tuple(boss.tab_for_window(window) if window else boss.active_tab)
|
||||
for tab in tabs:
|
||||
if tab:
|
||||
try:
|
||||
|
||||
@ -40,7 +40,7 @@ class Kitten(RemoteCommand):
|
||||
windows = [window or boss.active_window]
|
||||
match = payload_get('match')
|
||||
if match:
|
||||
windows = tuple(boss.match_windows(match))
|
||||
windows = list(boss.match_windows(match))
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
for window in windows:
|
||||
|
||||
@ -39,7 +39,7 @@ class LastUsedLayout(RemoteCommand):
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
else:
|
||||
tabs = [boss.tab_for_window(window) if window else boss.active_tab]
|
||||
tabs = tuple(boss.tab_for_window(window) if window else boss.active_tab)
|
||||
for tab in tabs:
|
||||
if tab:
|
||||
tab.last_used_layout()
|
||||
|
||||
@ -80,7 +80,7 @@ instead of the active tab
|
||||
setattr(opts, key, val)
|
||||
match = payload_get('match')
|
||||
if match:
|
||||
tabs = tuple(boss.match_tabs(match))
|
||||
tabs = list(boss.match_tabs(match))
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
else:
|
||||
@ -88,7 +88,7 @@ instead of the active tab
|
||||
if payload_get('self') and window and window.tabref():
|
||||
tabs = [window.tabref()]
|
||||
tab = tabs[0]
|
||||
w = do_launch(boss, opts, payload_get('args') or None, target_tab=tab)
|
||||
w = do_launch(boss, opts, payload_get('args') or [], target_tab=tab)
|
||||
return None if payload_get('no_response') else str(getattr(w, 'id', 0))
|
||||
|
||||
|
||||
|
||||
@ -32,7 +32,8 @@ class NewWindow(RemoteCommand):
|
||||
desc = (
|
||||
'Open a new window in the specified tab. If you use the :option:`kitty @ new-window --match` option'
|
||||
' the first matching tab is used. Otherwise the currently active tab is used.'
|
||||
' Prints out the id of the newly opened window (unless :option:`--no-response` is used). Any command line arguments'
|
||||
' Prints out the id of the newly opened window'
|
||||
' (unless :option:`--no-response` is used). Any command line arguments'
|
||||
' are assumed to be the command line used to run in the new window, if none'
|
||||
' are provided, the default shell is run. For example:\n'
|
||||
':italic:`kitty @ new-window --title Email mutt`'
|
||||
@ -109,7 +110,7 @@ the id of the new window will not be printed out.
|
||||
|
||||
match = payload_get('match')
|
||||
if match:
|
||||
tabs = tuple(boss.match_tabs(match))
|
||||
tabs = list(boss.match_tabs(match))
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
else:
|
||||
|
||||
@ -39,7 +39,7 @@ If specified apply marker to the window this command is run in, rather than the
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
windows = [window if window and payload_get('self') else boss.active_window]
|
||||
windows = tuple(window if window and payload_get('self') else boss.active_window)
|
||||
|
||||
for window in windows:
|
||||
window.remove_marker()
|
||||
|
||||
@ -22,7 +22,10 @@ class ResizeWindow(RemoteCommand):
|
||||
'''
|
||||
|
||||
short_desc = 'Resize the specified window'
|
||||
desc = 'Resize the specified window in the current layout. Note that not all layouts can resize all windows in all directions.'
|
||||
desc = (
|
||||
'Resize the specified window in the current layout.'
|
||||
' Note that not all layouts can resize all windows in all directions.'
|
||||
)
|
||||
options_spec = MATCH_WINDOW_OPTION + '''\n
|
||||
--increment -i
|
||||
type=int
|
||||
@ -34,9 +37,10 @@ The number of cells to change the size by, can be negative to decrease the size.
|
||||
type=choices
|
||||
choices=horizontal,vertical,reset
|
||||
default=horizontal
|
||||
The axis along which to resize. If :italic:`horizontal`, it will make the window wider or narrower by the specified increment.
|
||||
If :italic:`vertical`, it will make the window taller or shorter by the specified increment. The special value :italic:`reset` will
|
||||
reset the layout to its default configuration.
|
||||
The axis along which to resize. If :italic:`horizontal`,
|
||||
it will make the window wider or narrower by the specified increment.
|
||||
If :italic:`vertical`, it will make the window taller or shorter by the specified increment.
|
||||
The special value :italic:`reset` will reset the layout to its default configuration.
|
||||
|
||||
|
||||
--self
|
||||
@ -56,7 +60,7 @@ If specified resize the window this command is run in, rather than the active wi
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
else:
|
||||
windows = [window if window and payload_get('self') else boss.active_window]
|
||||
windows = tuple(window if window and payload_get('self') else boss.active_window)
|
||||
resized = False
|
||||
if windows and windows[0]:
|
||||
resized = boss.resize_layout_window(
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional, Tuple, Union
|
||||
|
||||
from .base import (
|
||||
MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError, PayloadGetType,
|
||||
@ -35,23 +35,22 @@ class ScrollWindow(RemoteCommand):
|
||||
|
||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||
amt = args[0]
|
||||
ans = {'match': opts.match}
|
||||
if amt in ('start', 'end'):
|
||||
ans['amount'] = amt, None
|
||||
else:
|
||||
amount: Tuple[Union[str, int], Optional[str]] = amt, None
|
||||
if amt not in ('start', 'end'):
|
||||
pages = 'p' in amt
|
||||
amt = amt.replace('p', '')
|
||||
mult = -1 if amt.endswith('-') else 1
|
||||
amt = int(amt.replace('-', ''))
|
||||
ans['amount'] = [amt * mult, 'p' if pages else 'l']
|
||||
return ans
|
||||
q = int(amt.replace('-', ''))
|
||||
amount = q * mult, 'p' if pages else 'l'
|
||||
|
||||
return {'match': opts.match, 'amount': amount}
|
||||
|
||||
def response_from_kitty(self, boss: 'Boss', window: 'Window', payload_get: PayloadGetType) -> ResponseType:
|
||||
windows = [window or boss.active_window]
|
||||
match = payload_get('match')
|
||||
amt = payload_get('amount')
|
||||
if match:
|
||||
windows = tuple(boss.match_windows(match))
|
||||
windows = list(boss.match_windows(match))
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
for window in windows:
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Generator, Dict
|
||||
|
||||
from kitty.config import parse_send_text_bytes
|
||||
|
||||
@ -51,8 +51,7 @@ are sent as is, not interpreted for escapes.
|
||||
limit = 1024
|
||||
ret = {'match': opts.match, 'is_binary': False, 'match_tab': opts.match_tab}
|
||||
|
||||
def pipe():
|
||||
ret['is_binary'] = True
|
||||
def pipe() -> Generator[Dict, None, None]:
|
||||
if sys.stdin.isatty():
|
||||
import select
|
||||
fd = sys.stdin.fileno()
|
||||
@ -64,28 +63,29 @@ are sent as is, not interpreted for escapes.
|
||||
data = os.read(fd, limit)
|
||||
if not data:
|
||||
break # eof
|
||||
data = data.decode('utf-8')
|
||||
if '\x04' in data:
|
||||
data = data[:data.index('\x04')]
|
||||
decoded_data = data.decode('utf-8')
|
||||
if '\x04' in decoded_data:
|
||||
decoded_data = decoded_data[:decoded_data.index('\x04')]
|
||||
keep_going = False
|
||||
ret['text'] = data
|
||||
ret['text'] = decoded_data
|
||||
yield ret
|
||||
else:
|
||||
ret['is_binary'] = True
|
||||
while True:
|
||||
data = sys.stdin.read(limit)
|
||||
data = sys.stdin.buffer.read(limit)
|
||||
if not data:
|
||||
break
|
||||
ret['text'] = data[:limit]
|
||||
yield ret
|
||||
|
||||
def chunks(text):
|
||||
def chunks(text: str) -> Generator[Dict, None, None]:
|
||||
ret['is_binary'] = False
|
||||
while text:
|
||||
ret['text'] = text[:limit]
|
||||
yield ret
|
||||
text = text[limit:]
|
||||
|
||||
def file_pipe(path):
|
||||
def file_pipe(path: str) -> Generator[Dict, None, None]:
|
||||
ret['is_binary'] = True
|
||||
with open(path, encoding='utf-8') as f:
|
||||
while True:
|
||||
@ -105,7 +105,7 @@ are sent as is, not interpreted for escapes.
|
||||
text = ' '.join(args)
|
||||
sources.append(chunks(text))
|
||||
|
||||
def chain():
|
||||
def chain() -> Generator[Dict, None, None]:
|
||||
for src in sources:
|
||||
yield from src
|
||||
return chain()
|
||||
@ -114,7 +114,7 @@ are sent as is, not interpreted for escapes.
|
||||
windows = [boss.active_window]
|
||||
match = payload_get('match')
|
||||
if match:
|
||||
windows = tuple(boss.match_windows(match))
|
||||
windows = list(boss.match_windows(match))
|
||||
mt = payload_get('match_tab')
|
||||
if mt:
|
||||
windows = []
|
||||
@ -123,7 +123,8 @@ are sent as is, not interpreted for escapes.
|
||||
raise MatchError(payload_get('match_tab'), 'tabs')
|
||||
for tab in tabs:
|
||||
windows += tuple(tab)
|
||||
data = payload_get('text').encode('utf-8') if payload_get('is_binary') else parse_send_text_bytes(payload_get('text'))
|
||||
data = payload_get('text').encode('utf-8') if payload_get('is_binary') else parse_send_text_bytes(
|
||||
payload_get('text'))
|
||||
for window in windows:
|
||||
if window is not None:
|
||||
window.write_to_child(data)
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
import imghdr
|
||||
import tempfile
|
||||
from base64 import standard_b64decode, standard_b64encode
|
||||
from typing import TYPE_CHECKING, BinaryIO, Optional
|
||||
from typing import IO, TYPE_CHECKING, Dict, Generator, Optional
|
||||
from uuid import uuid4
|
||||
|
||||
from .base import (
|
||||
MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType,
|
||||
RCOptions, RemoteCommand, ResponseType, Window, no_response,
|
||||
RCOptions, RemoteCommand, ResponseType, Window,
|
||||
windows_for_payload
|
||||
)
|
||||
|
||||
@ -59,20 +59,26 @@ How the image should be displayed. The value of configured will use the configur
|
||||
args_count = 1
|
||||
args_completion = {'files': ('PNG Images', ('*.png',))}
|
||||
current_img_id: Optional[str] = None
|
||||
current_file_obj: Optional[BinaryIO] = None
|
||||
current_file_obj: Optional[IO[bytes]] = None
|
||||
|
||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||
if not args:
|
||||
self.fatal('Must specify path to PNG image')
|
||||
path = args[0]
|
||||
ret = {'match': opts.match, 'configured': opts.configured, 'layout': opts.layout, 'all': opts.all, 'img_id': str(uuid4())}
|
||||
ret = {
|
||||
'match': opts.match,
|
||||
'configured': opts.configured,
|
||||
'layout': opts.layout,
|
||||
'all': opts.all,
|
||||
'img_id': str(uuid4())
|
||||
}
|
||||
if path.lower() == 'none':
|
||||
ret['data'] = '-'
|
||||
return ret
|
||||
if imghdr.what(path) != 'png':
|
||||
self.fatal('{} is not a PNG image'.format(path))
|
||||
|
||||
def file_pipe(path):
|
||||
def file_pipe(path) -> Generator[Dict, None, None]:
|
||||
with open(path, 'rb') as f:
|
||||
while True:
|
||||
data = f.read(512)
|
||||
@ -92,8 +98,9 @@ How the image should be displayed. The value of configured will use the configur
|
||||
set_background_image.current_img_id = img_id
|
||||
set_background_image.current_file_obj = tempfile.NamedTemporaryFile()
|
||||
if data:
|
||||
assert set_background_image.current_file_obj is not None
|
||||
set_background_image.current_file_obj.write(standard_b64decode(data))
|
||||
return no_response
|
||||
return None
|
||||
|
||||
windows = windows_for_payload(boss, window, payload_get)
|
||||
os_windows = tuple({w.os_window_id for w in windows})
|
||||
@ -101,6 +108,7 @@ How the image should be displayed. The value of configured will use the configur
|
||||
if data == '-':
|
||||
path = None
|
||||
else:
|
||||
assert set_background_image.current_file_obj is not None
|
||||
f = set_background_image.current_file_obj
|
||||
path = f.name
|
||||
set_background_image.current_file_obj = None
|
||||
@ -109,7 +117,7 @@ How the image should be displayed. The value of configured will use the configur
|
||||
try:
|
||||
boss.set_background_image(path, os_windows, payload_get('configured'), layout)
|
||||
except ValueError as err:
|
||||
err.hide_traceback = True
|
||||
err.hide_traceback = True # type: ignore
|
||||
raise
|
||||
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Dict, Optional
|
||||
|
||||
from kitty.config import parse_config
|
||||
from kitty.fast_data_types import patch_color_profiles
|
||||
@ -34,7 +34,8 @@ class SetColors(RemoteCommand):
|
||||
|
||||
short_desc = 'Set terminal colors'
|
||||
desc = (
|
||||
'Set the terminal colors for the specified windows/tabs (defaults to active window). You can either specify the path to a conf file'
|
||||
'Set the terminal colors for the specified windows/tabs (defaults to active window).'
|
||||
' You can either specify the path to a conf file'
|
||||
' (in the same format as kitty.conf) to read the colors from or you can specify individual colors,'
|
||||
' for example: kitty @ set-colors foreground=red background=white'
|
||||
)
|
||||
@ -59,21 +60,28 @@ this option, any color arguments are ignored and --configured and --all are impl
|
||||
argspec = 'COLOR_OR_FILE ...'
|
||||
|
||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||
colors, cursor_text_color = {}, False
|
||||
final_colors: Dict[str, int] = {}
|
||||
cursor_text_color: Optional[int] = None
|
||||
if not opts.reset:
|
||||
colors: Dict[str, Optional[Color]] = {}
|
||||
for spec in args:
|
||||
if '=' in spec:
|
||||
colors.update(parse_config((spec.replace('=', ' '),)))
|
||||
else:
|
||||
with open(os.path.expanduser(spec), encoding='utf-8', errors='replace') as f:
|
||||
colors.update(parse_config(f))
|
||||
cursor_text_color = colors.pop('cursor_text_color', False)
|
||||
colors = {k: color_as_int(v) for k, v in colors.items() if isinstance(v, Color)}
|
||||
return {
|
||||
ctc = colors.pop('cursor_text_color')
|
||||
if isinstance(ctc, Color):
|
||||
cursor_text_color = color_as_int(ctc)
|
||||
final_colors = {k: color_as_int(v) for k, v in colors.items() if isinstance(v, Color)}
|
||||
ans = {
|
||||
'match_window': opts.match, 'match_tab': opts.match_tab,
|
||||
'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset,
|
||||
'colors': colors, 'reset': opts.reset, 'cursor_text_color': cursor_text_color
|
||||
'colors': final_colors, 'reset': opts.reset
|
||||
}
|
||||
if cursor_text_color is not None:
|
||||
ans['cursor_text_color'] = cursor_text_color
|
||||
return ans
|
||||
|
||||
def response_from_kitty(self, boss: 'Boss', window: 'Window', payload_get: PayloadGetType) -> ResponseType:
|
||||
windows = windows_for_payload(boss, window, payload_get)
|
||||
|
||||
@ -41,7 +41,7 @@ class SetTabTitle(RemoteCommand):
|
||||
if not tabs:
|
||||
raise MatchError(match, 'tabs')
|
||||
else:
|
||||
tabs = [boss.tab_for_window(window) if window else boss.active_tab]
|
||||
tabs = tuple(boss.tab_for_window(window) if window else boss.active_tab)
|
||||
for tab in tabs:
|
||||
if tab:
|
||||
tab.set_title(payload_get('title'))
|
||||
|
||||
@ -44,7 +44,7 @@ want to allow other programs to change it afterwards, use this option.
|
||||
windows = [window or boss.active_window]
|
||||
match = payload_get('match')
|
||||
if match:
|
||||
windows = tuple(boss.match_windows(match))
|
||||
windows = list(boss.match_windows(match))
|
||||
if not windows:
|
||||
raise MatchError(match)
|
||||
for window in windows:
|
||||
|
||||
@ -9,15 +9,15 @@ import sys
|
||||
import types
|
||||
from contextlib import suppress
|
||||
from functools import partial
|
||||
from typing import Any, List, Optional
|
||||
from typing import List
|
||||
|
||||
from .cli import emph, parse_args
|
||||
from .cli_stub import RCOptions
|
||||
from .constants import appname, version
|
||||
from .fast_data_types import read_command_response
|
||||
from .rc.base import (
|
||||
all_command_names, command_for_name, no_response as no_response_sentinel,
|
||||
parse_subcommand_cli
|
||||
PayloadGetter, all_command_names, command_for_name,
|
||||
no_response as no_response_sentinel, parse_subcommand_cli
|
||||
)
|
||||
from .utils import TTYIO, parse_address_spec
|
||||
|
||||
@ -33,11 +33,8 @@ def handle_cmd(boss, window, cmd):
|
||||
c = command_for_name(cmd['cmd'])
|
||||
payload = cmd.get('payload') or {}
|
||||
|
||||
def payload_get(key: str, opt_name: Optional[str] = None) -> Any:
|
||||
return c.payload_get(payload, key, opt_name)
|
||||
|
||||
try:
|
||||
ans = c.response_from_kitty(boss, window, payload_get)
|
||||
ans = c.response_from_kitty(boss, window, PayloadGetter(c, payload))
|
||||
except Exception:
|
||||
if no_response: # don't report errors if --no-response was used
|
||||
return
|
||||
|
||||
@ -48,7 +48,7 @@ def cmd_names_matching(prefix):
|
||||
|
||||
@lru_cache()
|
||||
def options_for_cmd(cmd: str) -> Tuple[Tuple[str, ...], Dict[str, OptionDict]]:
|
||||
alias_map = {}
|
||||
alias_map: Dict[str, OptionDict] = {}
|
||||
try:
|
||||
func = command_for_name(cmd)
|
||||
except KeyError:
|
||||
|
||||
@ -7,15 +7,15 @@ from binascii import hexlify, unhexlify
|
||||
from typing import cast, Dict
|
||||
|
||||
|
||||
def modify_key_bytes(keybytes, amt):
|
||||
def modify_key_bytes(keybytes: bytes, amt: int) -> bytes:
|
||||
if amt == 0:
|
||||
return keybytes
|
||||
ans = bytearray(keybytes)
|
||||
amt = str(amt).encode('ascii')
|
||||
samt = str(amt).encode('ascii')
|
||||
if ans[-1] == ord('~'):
|
||||
return bytes(ans[:-1] + bytearray(b';' + amt + b'~'))
|
||||
return bytes(ans[:-1] + bytearray(b';' + samt + b'~'))
|
||||
if ans[1] == ord('O'):
|
||||
return bytes(ans[:1] + bytearray(b'[1;' + amt) + ans[-1:])
|
||||
return bytes(ans[:1] + bytearray(b'[1;' + samt) + ans[-1:])
|
||||
raise ValueError('Unknown key type in key: {!r}'.format(keybytes))
|
||||
|
||||
|
||||
@ -433,7 +433,7 @@ octal_escape = re.compile(r'\\([0-7]{3})')
|
||||
escape_escape = re.compile(r'\\[eE]')
|
||||
|
||||
|
||||
def key_as_bytes(name):
|
||||
def key_as_bytes(name: str) -> bytes:
|
||||
ans = string_capabilities[name]
|
||||
ans = octal_escape.sub(lambda m: chr(int(m.group(1), 8)), ans)
|
||||
ans = escape_escape.sub('\033', ans)
|
||||
|
||||
@ -9,6 +9,7 @@ import weakref
|
||||
from collections import deque
|
||||
from enum import IntEnum
|
||||
from itertools import chain
|
||||
from typing import List
|
||||
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import ScreenGeometry, WindowGeometry, appname, wakeup
|
||||
@ -483,7 +484,7 @@ class Window:
|
||||
self.title_stack.append(self.child_title)
|
||||
# }}}
|
||||
|
||||
def text_for_selection(self):
|
||||
def text_for_selection(self) -> str:
|
||||
lines = self.screen.text_for_selection()
|
||||
if self.opts.strip_trailing_spaces == 'always' or (
|
||||
self.opts.strip_trailing_spaces == 'smart' and not self.screen.is_rectangle_select()):
|
||||
@ -497,8 +498,8 @@ class Window:
|
||||
self.screen.reset_callbacks()
|
||||
self.screen = None
|
||||
|
||||
def as_text(self, as_ansi=False, add_history=False, add_wrap_markers=False, alternate_screen=False):
|
||||
lines = []
|
||||
def as_text(self, as_ansi=False, add_history=False, add_wrap_markers=False, alternate_screen=False) -> str:
|
||||
lines: List[str] = []
|
||||
add_history = add_history and not (self.screen.is_using_alternate_linebuf() ^ alternate_screen)
|
||||
if alternate_screen:
|
||||
f = self.screen.as_text_alternate
|
||||
@ -506,7 +507,7 @@ class Window:
|
||||
f = self.screen.as_text_non_visual if add_history else self.screen.as_text
|
||||
f(lines.append, as_ansi, add_wrap_markers)
|
||||
if add_history:
|
||||
h = []
|
||||
h: List[str] = []
|
||||
self.screen.historybuf.pagerhist_as_text(h.append)
|
||||
if h and (not as_ansi or not add_wrap_markers):
|
||||
sanitizer = text_sanitizer(as_ansi, add_wrap_markers)
|
||||
@ -517,7 +518,7 @@ class Window:
|
||||
h[-1] += '\n'
|
||||
if as_ansi:
|
||||
h[-1] += '\x1b[m'
|
||||
lines = chain(h, lines)
|
||||
return ''.join(chain(h, lines))
|
||||
return ''.join(lines)
|
||||
|
||||
@property
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
" Scan the following dirs recursively for tags
|
||||
let g:project_tags_dirs = ['kitty']
|
||||
let g:syntastic_python_flake8_exec = 'flake8'
|
||||
let g:project_tags_dirs = ['kitty', 'kittens']
|
||||
let g:syntastic_python_checkers = ['pylama']
|
||||
let g:ycm_python_binary_path = 'python3'
|
||||
set wildignore+==template.py
|
||||
set wildignore+=tags
|
||||
|
||||
24
setup.cfg
24
setup.cfg
@ -13,8 +13,15 @@ blank_line_before_nested_class_or_def = True
|
||||
combine_as_imports = True
|
||||
multi_line_output = 5
|
||||
|
||||
[mypy]
|
||||
files = kitty,kittens,glfw,*.py
|
||||
[pylama]
|
||||
linters=mypy,pycodestyle,pyflakes
|
||||
|
||||
[pylama:pycodestyle]
|
||||
max_line_length = 120
|
||||
exclude==template.py,linux-package
|
||||
|
||||
[pylama:mypy]
|
||||
files = kitty,kittens,glfw,*.py,docs/conf.py
|
||||
no_implicit_optional = True
|
||||
sqlite_cache = True
|
||||
cache_fine_grained = True
|
||||
@ -22,4 +29,17 @@ warn_redundant_casts = True
|
||||
warn_unused_ignores = True
|
||||
warn_return_any = True
|
||||
warn_unreachable = True
|
||||
warn_no_return = False
|
||||
check_untyped_defs = True
|
||||
|
||||
[mypy]
|
||||
files = kitty,kittens,glfw,*.py,docs/conf.py
|
||||
no_implicit_optional = True
|
||||
sqlite_cache = True
|
||||
cache_fine_grained = True
|
||||
warn_redundant_casts = True
|
||||
warn_unused_ignores = True
|
||||
warn_return_any = True
|
||||
warn_unreachable = True
|
||||
warn_no_return = False
|
||||
# check_untyped_defs = True
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user