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