Move type parsers for config into own module

This commit is contained in:
Kovid Goyal 2021-05-24 22:29:28 +05:30
parent a4daa49f70
commit fe94f4cbb4
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
11 changed files with 392 additions and 372 deletions

View File

@ -9,8 +9,9 @@ from gettext import gettext as _
from typing import Dict
from kitty.conf.definition import OptionOrAction, option_func
from kitty.conf.utils import python_string, to_color, to_color_or_none
from kitty.utils import positive_int
from kitty.conf.utils import (
positive_int, python_string, to_color, to_color_or_none
)
# }}}

View File

@ -15,13 +15,12 @@ from typing import (
Sequence, Tuple, Union
)
from kitty.conf.utils import positive_float, positive_int
from kitty.fast_data_types import create_canvas
from kitty.typing import (
CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t, HandlerType
)
from kitty.utils import (
ScreenSize, find_exe, fit_image, positive_float, positive_int
)
from kitty.utils import ScreenSize, find_exe, fit_image
from .operations import cursor

View File

@ -23,7 +23,7 @@ from .config import (
KeyAction, SubSequenceMap, common_opts_as_dict,
prepare_config_file_for_editing
)
from .config_data import MINIMUM_FONT_SIZE
from .options_types import MINIMUM_FONT_SIZE
from .constants import (
appname, config_dir, is_macos, kitty_exe, supports_primary_selection
)

View File

@ -174,7 +174,7 @@ def option_func(all_options: Dict[str, Any], all_groups: Dict[str, Sequence[str]
return partial(option, all_options, group), partial(shortcut, all_options, group), partial(mouse_action, all_options, group), change_group, all_groups_
OptionOrAction = Union[Option, Sequence[Shortcut], Sequence[MouseAction]]
OptionOrAction = Union[Option, List[Union[Shortcut, MouseAction]]]
def merged_opts(all_options: Sequence[OptionOrAction], opt: Option, i: int) -> Generator[Option, None, None]:

View File

@ -7,7 +7,7 @@ import re
import shlex
from typing import (
Any, Callable, Dict, FrozenSet, Generator, Iterable, Iterator, List,
NamedTuple, Optional, Sequence, Tuple, Type, TypeVar, Union
NamedTuple, Optional, Sequence, Tuple, Type, TypeVar, Union, Set
)
from ..rgb import Color, to_color as as_color
@ -24,6 +24,14 @@ class BadLine(NamedTuple):
exception: Exception
def positive_int(x: ConvertibleToNumbers) -> int:
return max(0, int(x))
def positive_float(x: ConvertibleToNumbers) -> float:
return max(0, float(x))
def to_color(x: str) -> Color:
ans = as_color(x, validate=True)
if ans is None: # this is only for type-checking
@ -342,3 +350,9 @@ def parse_kittens_key(
return None
ans = parse_kittens_func_args(action, funcs_with_args)
return ans, parse_shortcut(sc)
def uniq(vals: Iterable[T]) -> List[T]:
seen: Set[T] = set()
seen_add = seen.add
return [x for x in vals if x not in seen and not seen_add(x)]

View File

@ -9,8 +9,8 @@ import sys
from contextlib import contextmanager, suppress
from functools import partial
from typing import (
Any, Callable, Dict, Generator, Iterable, List, NamedTuple, Optional,
Sequence, Set, Tuple, Type, Union, FrozenSet
Any, Callable, Dict, FrozenSet, Generator, Iterable, List, NamedTuple,
Optional, Sequence, Set, Tuple, Type, Union
)
from . import fast_data_types as defines
@ -19,10 +19,11 @@ from .conf.utils import (
BadLine, init_config, key_func, load_config as _load_config, merge_dicts,
parse_config_base, python_string, to_bool, to_cmdline
)
from .config_data import InvalidMods, all_options, parse_mods, parse_shortcut
from .config_data import all_options
from .constants import cache_dir, defconf, is_macos
from .fonts import FontFeature
from .options_stub import Options as OptionsStub
from .options_types import InvalidMods, parse_mods, parse_shortcut
from .types import MouseEvent, SingleKey
from .typing import TypedDict
from .utils import expandvars, log_error

View File

@ -3,106 +3,31 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
# Utils {{{
import os
from gettext import gettext as _
from typing import (
Callable, Dict, FrozenSet, Iterable, List, Optional, Set,
Tuple, TypeVar, Union
)
from typing import Dict
from . import fast_data_types as defines
from .conf.definition import OptionOrAction, option_func
from .conf.utils import (
choices, to_bool, to_cmdline, to_color, to_color_or_none, unit_float
choices, positive_float, positive_int, to_cmdline, to_color,
to_color_or_none, unit_float
)
from .constants import config_dir, is_macos
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .key_names import (
character_key_name_aliases, functional_key_name_aliases,
get_key_name_lookup
from .constants import is_macos
from .options_types import (
active_tab_title_template, adjust_line_height, allow_hyperlinks,
allow_remote_control, box_drawing_scale, clipboard_control,
config_or_absolute_path, copy_on_select, cursor_text_color,
default_tab_separator, disable_ligatures, edge_width,
hide_window_decorations, macos_option_as_alt, macos_titlebar_color,
optional_edge_width, resize_draw_strategy, scrollback_lines,
scrollback_pager_history_size, tab_activity_symbol, tab_bar_edge,
tab_bar_min_tabs, tab_fade, tab_font_style, tab_separator,
tab_title_template, to_cursor_shape, to_font_size, to_layout_names,
url_prefixes, url_style, window_border_width, window_size, to_modifiers
)
from .layout.interface import all_layouts
from .rgb import Color, color_as_int, color_as_sharp, color_from_int
from .types import FloatEdges, SingleKey
from .utils import log_error, positive_float, positive_int
from .rgb import color_as_sharp, color_from_int
class InvalidMods(ValueError):
pass
MINIMUM_FONT_SIZE = 4
mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '': 'SUPER',
'': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'}
character_key_name_aliases_with_ascii_lowercase = character_key_name_aliases.copy()
for x in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
character_key_name_aliases_with_ascii_lowercase[x] = x.lower()
def parse_mods(parts: Iterable[str], sc: str) -> Optional[int]:
def map_mod(m: str) -> str:
return mod_map.get(m, m)
mods = 0
for m in parts:
try:
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
except AttributeError:
if m.upper() != 'NONE':
log_error('Shortcut: {} has unknown modifier, ignoring'.format(sc))
return None
return mods
def to_modifiers(val: str) -> int:
return parse_mods(val.split('+'), val) or 0
def parse_shortcut(sc: str) -> SingleKey:
if sc.endswith('+') and len(sc) > 1:
sc = sc[:-1] + 'plus'
parts = sc.split('+')
mods = 0
if len(parts) > 1:
mods = parse_mods(parts[:-1], sc) or 0
if not mods:
raise InvalidMods('Invalid shortcut')
q = parts[-1]
q = character_key_name_aliases_with_ascii_lowercase.get(q.upper(), q)
is_native = False
if q.startswith('0x'):
try:
key = int(q, 16)
except Exception:
key = 0
else:
is_native = True
else:
try:
key = ord(q)
except Exception:
uq = q.upper()
uq = functional_key_name_aliases.get(uq, uq)
x: Optional[int] = getattr(defines, f'GLFW_FKEY_{uq}', None)
if x is None:
lf = get_key_name_lookup()
key = lf(q, False) or 0
is_native = key > 0
else:
key = x
return SingleKey(mods, is_native, key or 0)
T = TypeVar('T')
def uniq(vals: Iterable[T]) -> List[T]:
seen: Set[T] = set()
seen_add = seen.add
return [x for x in vals if x not in seen and not seen_add(x)]
# }}}
# Groups {{{
@ -306,11 +231,6 @@ o('bold_font', 'auto')
o('italic_font', 'auto')
o('bold_italic_font', 'auto')
def to_font_size(x: str) -> float:
return max(MINIMUM_FONT_SIZE, float(x))
o('font_size', 11.0, long_text=_('Font size (in pts)'), option_type=to_font_size)
o('force_ltr', False, long_text=_("""
@ -331,16 +251,6 @@ support, because it will force kitty to always treat the text as LTR, which
FriBidi expects for terminals."""))
def adjust_line_height(x: str) -> Union[int, float]:
if x.endswith('%'):
ans = float(x[:-1].strip()) / 100.0
if ans < 0:
log_error('Percentage adjustments of cell sizes must be positive numbers')
return 0
return ans
return int(x)
o('adjust_line_height', 0, option_type=adjust_line_height, long_text=_('''
Change the size of each character cell kitty renders. You can use either numbers,
which are interpreted as pixels or percentages (number followed by %), which
@ -367,11 +277,6 @@ Syntax is::
'''))
def disable_ligatures(x: str) -> int:
cmap = {'never': 0, 'cursor': 1, 'always': 2}
return cmap.get(x.lower(), 0)
o('disable_ligatures', 'never', option_type=disable_ligatures, long_text=_('''
Choose how you want to handle multi-character ligatures. The default is to
always render them. You can tell kitty to not render them when the cursor is
@ -439,13 +344,6 @@ You can do this with e.g.::
'''))
def box_drawing_scale(x: str) -> Tuple[float, float, float, float]:
ans = tuple(float(q.strip()) for q in x.split(','))
if len(ans) != 4:
raise ValueError('Invalid box_drawing scale, must have four entries')
return ans[0], ans[1], ans[2], ans[3]
o(
'box_drawing_scale',
'0.001, 1, 1.5, 2',
@ -461,30 +359,6 @@ and very thick lines.
g('cursor') # {{{
cshapes = {
'block': CURSOR_BLOCK,
'beam': CURSOR_BEAM,
'underline': CURSOR_UNDERLINE
}
def to_cursor_shape(x: str) -> int:
try:
return cshapes[x.lower()]
except KeyError:
raise ValueError(
'Invalid cursor shape: {} allowed values are {}'.format(
x, ', '.join(cshapes)
)
)
def cursor_text_color(x: str) -> Optional[Color]:
if x.lower() == 'background':
return None
return to_color(x)
o('cursor', '#cccccc', _('Default cursor color'), option_type=to_color)
o('cursor_text_color', '#111111', option_type=cursor_text_color, long_text=_('''
Choose the color of text under the cursor. If you want it rendered with the
@ -510,18 +384,6 @@ inactivity. Set to zero to never stop blinking.
g('scrollback') # {{{
def scrollback_lines(x: str) -> int:
ans = int(x)
if ans < 0:
ans = 2 ** 32 - 1
return ans
def scrollback_pager_history_size(x: str) -> int:
ans = int(max(0, float(x)) * 1024 * 1024)
return min(ans, 4096 * 1024 * 1024 - 1)
o('scrollback_lines', 2000, option_type=scrollback_lines, long_text=_('''
Number of lines of history to keep in memory for scrolling back. Memory is allocated
on demand. Negative numbers are (effectively) infinite scrollback. Note that using
@ -574,16 +436,6 @@ o('url_color', '#0087bd', option_type=to_color, long_text=_('''
The color and style for highlighting URLs on mouse-over.
:code:`url_style` can be one of: none, single, double, curly'''))
def url_style(x: str) -> int:
return url_style_map.get(x, url_style_map['curly'])
url_style_map = dict(
((v, i) for i, v in enumerate('none single double curly'.split()))
)
o('url_style', 'curly', option_type=url_style)
o('open_url_with', 'default', option_type=to_cmdline, long_text=_('''
@ -592,10 +444,6 @@ The special value :code:`default` means to use the
operating system's default URL handler.'''))
def url_prefixes(x: str) -> Tuple[str, ...]:
return tuple(a.lower() for a in x.replace(',', ' ').split())
o('url_prefixes', 'http https file ftp gemini irc gopher mailto news git', option_type=url_prefixes, long_text=_('''
The set of URL prefixes to look for when detecting a URL under the mouse cursor.'''))
@ -605,16 +453,6 @@ with an underline and the mouse cursor becomes a hand over them.
Even if this option is disabled, URLs are still clickable.'''))
def copy_on_select(raw: str) -> str:
q = raw.lower()
# boolean values special cased for backwards compat
if q in ('y', 'yes', 'true', 'clipboard'):
return 'clipboard'
if q in ('n', 'no', 'false', ''):
return ''
return raw
o('copy_on_select', 'no', option_type=copy_on_select, long_text=_('''
Copy to clipboard or a private buffer on select. With this set to
:code:`clipboard`, simply selecting text with the mouse will cause the text to
@ -747,30 +585,10 @@ number of cells instead of pixels.
'''))
def window_size(val: str) -> Tuple[int, str]:
val = val.lower()
unit = 'cells' if val.endswith('c') else 'px'
return positive_int(val.rstrip('c')), unit
o('initial_window_width', '640', option_type=window_size)
o('initial_window_height', '400', option_type=window_size)
def to_layout_names(raw: str) -> List[str]:
parts = [x.strip().lower() for x in raw.split(',')]
ans: List[str] = []
for p in parts:
if p in ('*', 'all'):
ans.extend(sorted(all_layouts))
continue
name = p.partition(':')[0]
if name not in all_layouts:
raise ValueError('The window layout {} is unknown'.format(p))
ans.append(p)
return uniq(ans)
o('enabled_layouts', '*', option_type=to_layout_names, long_text=_('''
The enabled window layouts. A comma separated list of layout names. The special
value :code:`all` means all layouts. The first listed layout will be used as the
@ -786,20 +604,6 @@ for vertical resizing.
o('window_resize_step_lines', 2, option_type=positive_int)
def window_border_width(x: Union[str, int, float]) -> Tuple[float, str]:
unit = 'pt'
if isinstance(x, str):
trailer = x[-2:]
if trailer in ('px', 'pt'):
unit = trailer
val = float(x[:-2])
else:
val = float(x)
else:
val = float(x)
return max(0, val), unit
o('window_border_width', '0.5pt', option_type=window_border_width, long_text=_('''
The width of window borders. Can be either in pixels (px) or pts (pt). Values
in pts will be rounded to the nearest number of pixels based on screen
@ -815,27 +619,6 @@ a non-zero window margin overrides this and causes all borders to be drawn.
'''))
def edge_width(x: str, converter: Callable[[str], float] = positive_float) -> FloatEdges:
parts = str(x).split()
num = len(parts)
if num == 1:
val = converter(parts[0])
return FloatEdges(val, val, val, val)
if num == 2:
v = converter(parts[0])
h = converter(parts[1])
return FloatEdges(h, v, h, v)
if num == 3:
top, h, bottom = map(converter, parts)
return FloatEdges(h, top, h, bottom)
top, right, bottom, left = map(converter, parts)
return FloatEdges(left, top, right, bottom)
def optional_edge_width(x: str) -> FloatEdges:
return edge_width(x, float)
edge_desc = _(
'A single value sets all four sides. Two values set the vertical and horizontal sides.'
' Three values set top, horizontal and bottom. Four values set top, right, bottom and left.')
@ -875,14 +658,6 @@ zero and one, with zero being fully faded).
'''))
def hide_window_decorations(x: str) -> int:
if x == 'titlebar-only':
return 0b10
if to_bool(x):
return 0b01
return 0b00
o('hide_window_decorations', 'no', option_type=hide_window_decorations, long_text=_('''
Hide the window decorations (title-bar and window borders) with :code:`yes`.
On macOS, :code:`titlebar-only` can be used to only hide the titlebar.
@ -897,11 +672,6 @@ operating system sends events corresponding to the start and end
of a resize, this number is ignored.'''))
def resize_draw_strategy(x: str) -> int:
cmap = {'static': 0, 'scale': 1, 'blank': 2, 'size': 3}
return cmap.get(x.lower(), 0)
o('resize_draw_strategy', 'static', option_type=resize_draw_strategy, long_text=_('''
Choose how kitty draws a window while a resize is in progress.
A value of :code:`static` means draw the current window contents, mostly unchanged.
@ -926,31 +696,6 @@ OS windows, via the quit action).
# }}}
g('tabbar') # {{{
default_tab_separator = ''
def tab_separator(x: str) -> str:
for q in '\'"':
if x.startswith(q) and x.endswith(q):
x = x[1:-1]
if not x:
return ''
break
if not x.strip():
x = ('\xa0' * len(x)) if x else default_tab_separator
return x
def tab_bar_edge(x: str) -> int:
return {'top': 1, 'bottom': 3}.get(x.lower(), 3)
def tab_font_style(x: str) -> Tuple[bool, bool]:
return {
'bold-italic': (True, True),
'bold': (True, False),
'italic': (False, True)
}.get(x.lower().replace('_', '-'), (False, False))
o('tab_bar_edge', 'bottom', option_type=tab_bar_edge, long_text=_('''
@ -968,10 +713,6 @@ presents you with a list of tabs and allows for easy switching to a tab.
'''))
def tab_bar_min_tabs(x: str) -> int:
return max(1, positive_int(x))
o('tab_bar_min_tabs', 2, option_type=tab_bar_min_tabs, long_text=_('''
The minimum number of tabs that must exist before the tab bar is shown
'''))
@ -985,10 +726,6 @@ A value of :code:`last` will switch to the right-most tab.
'''))
def tab_fade(x: str) -> Tuple[float, ...]:
return tuple(map(unit_float, x.split()))
o('tab_fade', '0.25 0.5 0.75 1', option_type=tab_fade, long_text=_('''
Control how each tab fades into the background when using :code:`fade` for the
:opt:`tab_bar_style`. Each number is an alpha (between zero and one) that controls
@ -1006,31 +743,10 @@ as the :opt:`tab_bar_style`, can be one of: :code:`angled`, :code:`slanted`, or
'''))
def tab_activity_symbol(x: str) -> Optional[str]:
if x == 'none':
return None
return x or None
o('tab_activity_symbol', 'none', option_type=tab_activity_symbol, long_text=_('''
Some text or a unicode symbol to show on the tab if a window in the tab that does
not have focus has some activity.'''))
def tab_title_template(x: str) -> str:
if x:
for q in '\'"':
if x.startswith(q) and x.endswith(q):
x = x[1:-1]
break
return x
def active_tab_title_template(x: str) -> Optional[str]:
x = tab_title_template(x)
return None if x == 'none' else x
o('tab_title_template', '"{title}"', option_type=tab_title_template, long_text=_('''
A template to render the tab title. The default just renders
the title. If you wish to include the tab-index as well,
@ -1086,16 +802,6 @@ default as it has a performance cost)
'''))
def config_or_absolute_path(x: str) -> Optional[str]:
if x.lower() == 'none':
return None
x = os.path.expanduser(x)
x = os.path.expandvars(x)
if not os.path.isabs(x):
x = os.path.join(config_dir, x)
return x
o('background_image', 'none', option_type=config_or_absolute_path, long_text=_('''
Path to a background image. Must be in PNG format.'''))
@ -1189,12 +895,6 @@ terminal can fail silently because their stdout/stderr/stdin no longer work.
'''))
def allow_remote_control(x: str) -> str:
if x != 'socket-only':
x = 'y' if to_bool(x) else 'n'
return x
o('allow_remote_control', 'no', option_type=allow_remote_control, long_text=_('''
Allow other programs to control kitty. If you turn this on other programs can
control all aspects of kitty, including sending text to kitty windows, opening
@ -1249,10 +949,6 @@ Environment variables in the path are expanded.
'''))
def clipboard_control(x: str) -> FrozenSet[str]:
return frozenset(x.lower().split())
o('clipboard_control', 'write-clipboard write-primary', option_type=clipboard_control, long_text=_('''
Allow programs running in kitty to read and write from the clipboard. You can
control exactly which actions are allowed. The set of possible actions is:
@ -1265,12 +961,6 @@ program, even one running on a remote server via SSH can read your clipboard.
'''))
def allow_hyperlinks(x: str) -> int:
if x == 'ask':
return 0b11
return 1 if to_bool(x) else 0
o('allow_hyperlinks', 'yes', option_type=allow_hyperlinks, long_text=_('''
Process hyperlink (OSC 8) escape sequences. If disabled OSC 8 escape
sequences are ignored. Otherwise they become clickable links, that you
@ -1293,15 +983,6 @@ key-presses, to colors, to various advanced features may not work.
g('os') # {{{
def macos_titlebar_color(x: str) -> int:
x = x.strip('"')
if x == 'system':
return 0
if x == 'background':
return 1
return (color_as_int(to_color(x)) << 8) | 2
o('wayland_titlebar_color', 'system', option_type=macos_titlebar_color, long_text=_('''
Change the color of the kitty window's titlebar on Wayland systems with client side window decorations such as GNOME.
A value of :code:`system` means to use the default system color,
@ -1322,19 +1003,6 @@ probably better off just hiding the titlebar with :opt:`hide_window_decorations`
'''))
def macos_option_as_alt(x: str) -> int:
x = x.lower()
if x == 'both':
return 0b11
if x == 'left':
return 0b10
if x == 'right':
return 0b01
if to_bool(x):
return 0b11
return 0
o('macos_option_as_alt', 'no', option_type=macos_option_as_alt, long_text=_('''
Use the option key as an alt key. With this set to :code:`no`, kitty will use
the macOS native :kbd:`Option+Key` = unicode character behavior. This will

View File

@ -4,11 +4,10 @@
import sys
from contextlib import suppress
from typing import Callable, Optional
from typing import Callable, Dict, Optional
from .constants import is_macos
functional_key_name_aliases = {
'ESC': 'ESCAPE',
'PGUP': 'PAGE_UP',
@ -26,7 +25,7 @@ functional_key_name_aliases = {
}
character_key_name_aliases = {
character_key_name_aliases: Dict[str, str] = {
'SPC': ' ',
'SPACE': ' ',
'STAR': '*',

347
kitty/options_types.py Normal file
View File

@ -0,0 +1,347 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
import os
from typing import (
Callable, Dict, FrozenSet, Iterable, List, Optional, Tuple, Union
)
import kitty.fast_data_types as defines
from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .conf.utils import (
positive_float, positive_int, to_bool, to_color, uniq, unit_float
)
from .constants import config_dir
from .key_names import (
character_key_name_aliases, functional_key_name_aliases,
get_key_name_lookup
)
from .layout.interface import all_layouts
from .rgb import Color, color_as_int
from .types import FloatEdges, SingleKey
from .utils import log_error
MINIMUM_FONT_SIZE = 4
default_tab_separator = ''
mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '': 'SUPER',
'': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'}
character_key_name_aliases_with_ascii_lowercase: Dict[str, str] = character_key_name_aliases.copy()
for x in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
character_key_name_aliases_with_ascii_lowercase[x] = x.lower()
class InvalidMods(ValueError):
pass
def parse_mods(parts: Iterable[str], sc: str) -> Optional[int]:
def map_mod(m: str) -> str:
return mod_map.get(m, m)
mods = 0
for m in parts:
try:
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
except AttributeError:
if m.upper() != 'NONE':
log_error('Shortcut: {} has unknown modifier, ignoring'.format(sc))
return None
return mods
def to_modifiers(val: str) -> int:
return parse_mods(val.split('+'), val) or 0
def parse_shortcut(sc: str) -> SingleKey:
if sc.endswith('+') and len(sc) > 1:
sc = sc[:-1] + 'plus'
parts = sc.split('+')
mods = 0
if len(parts) > 1:
mods = parse_mods(parts[:-1], sc) or 0
if not mods:
raise InvalidMods('Invalid shortcut')
q = parts[-1]
q = character_key_name_aliases_with_ascii_lowercase.get(q.upper(), q)
is_native = False
if q.startswith('0x'):
try:
key = int(q, 16)
except Exception:
key = 0
else:
is_native = True
else:
try:
key = ord(q)
except Exception:
uq = q.upper()
uq = functional_key_name_aliases.get(uq, uq)
x: Optional[int] = getattr(defines, f'GLFW_FKEY_{uq}', None)
if x is None:
lf = get_key_name_lookup()
key = lf(q, False) or 0
is_native = key > 0
else:
key = x
return SingleKey(mods, is_native, key or 0)
def adjust_line_height(x: str) -> Union[int, float]:
if x.endswith('%'):
ans = float(x[:-1].strip()) / 100.0
if ans < 0:
log_error('Percentage adjustments of cell sizes must be positive numbers')
return 0
return ans
return int(x)
def to_font_size(x: str) -> float:
return max(MINIMUM_FONT_SIZE, float(x))
def disable_ligatures(x: str) -> int:
cmap = {'never': 0, 'cursor': 1, 'always': 2}
return cmap.get(x.lower(), 0)
def box_drawing_scale(x: str) -> Tuple[float, float, float, float]:
ans = tuple(float(q.strip()) for q in x.split(','))
if len(ans) != 4:
raise ValueError('Invalid box_drawing scale, must have four entries')
return ans[0], ans[1], ans[2], ans[3]
def cursor_text_color(x: str) -> Optional[Color]:
if x.lower() == 'background':
return None
return to_color(x)
cshapes = {
'block': CURSOR_BLOCK,
'beam': CURSOR_BEAM,
'underline': CURSOR_UNDERLINE
}
def to_cursor_shape(x: str) -> int:
try:
return cshapes[x.lower()]
except KeyError:
raise ValueError(
'Invalid cursor shape: {} allowed values are {}'.format(
x, ', '.join(cshapes)
)
)
def scrollback_lines(x: str) -> int:
ans = int(x)
if ans < 0:
ans = 2 ** 32 - 1
return ans
def scrollback_pager_history_size(x: str) -> int:
ans = int(max(0, float(x)) * 1024 * 1024)
return min(ans, 4096 * 1024 * 1024 - 1)
def url_style(x: str) -> int:
return url_style_map.get(x, url_style_map['curly'])
url_style_map = dict(
((v, i) for i, v in enumerate('none single double curly'.split()))
)
def url_prefixes(x: str) -> Tuple[str, ...]:
return tuple(a.lower() for a in x.replace(',', ' ').split())
def copy_on_select(raw: str) -> str:
q = raw.lower()
# boolean values special cased for backwards compat
if q in ('y', 'yes', 'true', 'clipboard'):
return 'clipboard'
if q in ('n', 'no', 'false', ''):
return ''
return raw
def window_size(val: str) -> Tuple[int, str]:
val = val.lower()
unit = 'cells' if val.endswith('c') else 'px'
return positive_int(val.rstrip('c')), unit
def to_layout_names(raw: str) -> List[str]:
parts = [x.strip().lower() for x in raw.split(',')]
ans: List[str] = []
for p in parts:
if p in ('*', 'all'):
ans.extend(sorted(all_layouts))
continue
name = p.partition(':')[0]
if name not in all_layouts:
raise ValueError('The window layout {} is unknown'.format(p))
ans.append(p)
return uniq(ans)
def window_border_width(x: Union[str, int, float]) -> Tuple[float, str]:
unit = 'pt'
if isinstance(x, str):
trailer = x[-2:]
if trailer in ('px', 'pt'):
unit = trailer
val = float(x[:-2])
else:
val = float(x)
else:
val = float(x)
return max(0, val), unit
def edge_width(x: str, converter: Callable[[str], float] = positive_float) -> FloatEdges:
parts = str(x).split()
num = len(parts)
if num == 1:
val = converter(parts[0])
return FloatEdges(val, val, val, val)
if num == 2:
v = converter(parts[0])
h = converter(parts[1])
return FloatEdges(h, v, h, v)
if num == 3:
top, h, bottom = map(converter, parts)
return FloatEdges(h, top, h, bottom)
top, right, bottom, left = map(converter, parts)
return FloatEdges(left, top, right, bottom)
def optional_edge_width(x: str) -> FloatEdges:
return edge_width(x, float)
def hide_window_decorations(x: str) -> int:
if x == 'titlebar-only':
return 0b10
if to_bool(x):
return 0b01
return 0b00
def resize_draw_strategy(x: str) -> int:
cmap = {'static': 0, 'scale': 1, 'blank': 2, 'size': 3}
return cmap.get(x.lower(), 0)
def tab_separator(x: str) -> str:
for q in '\'"':
if x.startswith(q) and x.endswith(q):
x = x[1:-1]
if not x:
return ''
break
if not x.strip():
x = ('\xa0' * len(x)) if x else default_tab_separator
return x
def tab_bar_edge(x: str) -> int:
return {'top': 1, 'bottom': 3}.get(x.lower(), 3)
def tab_font_style(x: str) -> Tuple[bool, bool]:
return {
'bold-italic': (True, True),
'bold': (True, False),
'italic': (False, True)
}.get(x.lower().replace('_', '-'), (False, False))
def tab_bar_min_tabs(x: str) -> int:
return max(1, positive_int(x))
def tab_fade(x: str) -> Tuple[float, ...]:
return tuple(map(unit_float, x.split()))
def tab_activity_symbol(x: str) -> Optional[str]:
if x == 'none':
return None
return x or None
def tab_title_template(x: str) -> str:
if x:
for q in '\'"':
if x.startswith(q) and x.endswith(q):
x = x[1:-1]
break
return x
def active_tab_title_template(x: str) -> Optional[str]:
x = tab_title_template(x)
return None if x == 'none' else x
def config_or_absolute_path(x: str) -> Optional[str]:
if x.lower() == 'none':
return None
x = os.path.expanduser(x)
x = os.path.expandvars(x)
if not os.path.isabs(x):
x = os.path.join(config_dir, x)
return x
def allow_remote_control(x: str) -> str:
if x != 'socket-only':
x = 'y' if to_bool(x) else 'n'
return x
def clipboard_control(x: str) -> FrozenSet[str]:
return frozenset(x.lower().split())
def allow_hyperlinks(x: str) -> int:
if x == 'ask':
return 0b11
return 1 if to_bool(x) else 0
def macos_titlebar_color(x: str) -> int:
x = x.strip('"')
if x == 'system':
return 0
if x == 'background':
return 1
return (color_as_int(to_color(x)) << 8) | 2
def macos_option_as_alt(x: str) -> int:
x = x.lower()
if x == 'both':
return 0b11
if x == 'left':
return 0b10
if x == 'right':
return 0b01
if to_bool(x):
return 0b11
return 0

View File

@ -7,7 +7,7 @@ import sys
from typing import Generator, List, Optional, Sequence, Union
from .cli_stub import CLIOptions
from .config_data import to_layout_names
from .options_types import to_layout_names, window_size
from .constants import kitty_exe
from .layout.interface import all_layouts
from .options_stub import Options
@ -127,7 +127,6 @@ def parse_session(raw: str, opts: Options, default_title: Optional[str] = None)
elif cmd == 'title':
ans.set_next_title(rest)
elif cmd == 'os_window_size':
from kitty.config_data import window_size
w, h = map(window_size, rest.split(maxsplit=1))
ans.os_window_size = WindowSizes(WindowSize(*w), WindowSize(*h))
elif cmd == 'os_window_class':

View File

@ -680,11 +680,3 @@ class SSHConnectionData(NamedTuple):
binary: str
hostname: str
port: Optional[int] = None
def positive_int(x: ConvertibleToNumbers) -> int:
return max(0, int(x))
def positive_float(x: ConvertibleToNumbers) -> float:
return max(0, float(x))