Restore conf file generation in the new framework
This commit is contained in:
parent
6d7df1c5e8
commit
a059e49579
@ -3,10 +3,12 @@
|
|||||||
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import builtins
|
import builtins
|
||||||
|
import re
|
||||||
import typing
|
import typing
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union, cast
|
Any, Callable, Dict, Iterable, Iterator, List, Match, Optional, Tuple,
|
||||||
|
Union, cast
|
||||||
)
|
)
|
||||||
|
|
||||||
import kitty.conf.utils as generic_parsers
|
import kitty.conf.utils as generic_parsers
|
||||||
@ -25,6 +27,64 @@ class Unset:
|
|||||||
unset = Unset()
|
unset = Unset()
|
||||||
|
|
||||||
|
|
||||||
|
def remove_markup(text: str) -> str:
|
||||||
|
|
||||||
|
def sub(m: Match) -> str:
|
||||||
|
if m.group(1) == 'ref':
|
||||||
|
return {
|
||||||
|
'layouts': 'https://sw.kovidgoyal.net/kitty/index.html#layouts',
|
||||||
|
'sessions': 'https://sw.kovidgoyal.net/kitty/index.html#sessions',
|
||||||
|
'functional': 'https://sw.kovidgoyal.net/kitty/keyboard-protocol.html#functional-key-definitions',
|
||||||
|
}[m.group(2)]
|
||||||
|
return str(m.group(2))
|
||||||
|
|
||||||
|
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_blocks(lines: Iterable[str]) -> Iterator[Tuple[List[str], int]]:
|
||||||
|
current_block: List[str] = []
|
||||||
|
prev_indent = 0
|
||||||
|
for line in lines:
|
||||||
|
indent_size = len(line) - len(line.lstrip())
|
||||||
|
if indent_size != prev_indent or not line:
|
||||||
|
if current_block:
|
||||||
|
yield current_block, prev_indent
|
||||||
|
current_block = []
|
||||||
|
prev_indent = indent_size
|
||||||
|
if not line:
|
||||||
|
yield [''], 100
|
||||||
|
else:
|
||||||
|
current_block.append(line)
|
||||||
|
if current_block:
|
||||||
|
yield current_block, indent_size
|
||||||
|
|
||||||
|
|
||||||
|
def wrapped_block(lines: Iterable[str]) -> Iterator[str]:
|
||||||
|
wrapper = getattr(wrapped_block, 'wrapper', None)
|
||||||
|
if wrapper is None:
|
||||||
|
import textwrap
|
||||||
|
wrapper = textwrap.TextWrapper(
|
||||||
|
initial_indent='#: ', subsequent_indent='#: ', width=70, break_long_words=False
|
||||||
|
)
|
||||||
|
setattr(wrapped_block, 'wrapper', wrapper)
|
||||||
|
for block, indent_size in iter_blocks(lines):
|
||||||
|
if indent_size > 0:
|
||||||
|
for line in block:
|
||||||
|
if not line:
|
||||||
|
yield line
|
||||||
|
else:
|
||||||
|
yield '#: ' + line
|
||||||
|
else:
|
||||||
|
for line in wrapper.wrap('\n'.join(block)):
|
||||||
|
yield line
|
||||||
|
|
||||||
|
|
||||||
|
def render_block(text: str) -> str:
|
||||||
|
text = remove_markup(text)
|
||||||
|
lines = text.splitlines()
|
||||||
|
return '\n'.join(wrapped_block(lines))
|
||||||
|
|
||||||
|
|
||||||
class Option:
|
class Option:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -40,6 +100,28 @@ class Option:
|
|||||||
self.parser_func = parser_func
|
self.parser_func = parser_func
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
|
|
||||||
|
@property
|
||||||
|
def needs_coalescing(self) -> bool:
|
||||||
|
return self.documented and not self.long_text
|
||||||
|
|
||||||
|
def as_conf(self, commented: bool = False, level: int = 0, option_group: List['Option'] = []) -> List[str]:
|
||||||
|
ans: List[str] = []
|
||||||
|
a = ans.append
|
||||||
|
if not self.documented:
|
||||||
|
return ans
|
||||||
|
if option_group:
|
||||||
|
sz = max(len(self.name), max(len(o.name) for o in option_group))
|
||||||
|
a(f'{self.name.ljust(sz)} {self.defval_as_string}')
|
||||||
|
for o in option_group:
|
||||||
|
a(f'{o.name.ljust(sz)} {o.defval_as_string}')
|
||||||
|
else:
|
||||||
|
a(f'{self.name} {self.defval_as_string}')
|
||||||
|
if self.long_text:
|
||||||
|
a('')
|
||||||
|
a(render_block(self.long_text))
|
||||||
|
a('')
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
class MultiVal:
|
class MultiVal:
|
||||||
|
|
||||||
@ -65,8 +147,43 @@ class MultiOption:
|
|||||||
def __iter__(self) -> Iterator[MultiVal]:
|
def __iter__(self) -> Iterator[MultiVal]:
|
||||||
yield from self.items
|
yield from self.items
|
||||||
|
|
||||||
|
def as_conf(self, commented: bool = False, level: int = 0) -> List[str]:
|
||||||
|
ans: List[str] = []
|
||||||
|
a = ans.append
|
||||||
|
for k in self.items:
|
||||||
|
if k.documented:
|
||||||
|
prefix = '' if k.add_to_default else '# '
|
||||||
|
a(f'{prefix}{self.name} {k.defval_as_str}')
|
||||||
|
if self.long_text:
|
||||||
|
a('')
|
||||||
|
a(render_block(self.long_text))
|
||||||
|
a('')
|
||||||
|
return ans
|
||||||
|
|
||||||
class ShortcutMapping:
|
|
||||||
|
class Mapping:
|
||||||
|
add_to_default: bool
|
||||||
|
long_text: str
|
||||||
|
documented: bool
|
||||||
|
setting_name: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parseable_text(self) -> str:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def as_conf(self, commented: bool = False, level: int = 0) -> List[str]:
|
||||||
|
ans: List[str] = []
|
||||||
|
if self.documented:
|
||||||
|
a = ans.append
|
||||||
|
if self.add_to_default:
|
||||||
|
a(self.setting_name + ' ' + self.parseable_text)
|
||||||
|
if self.long_text:
|
||||||
|
a(''), a(render_block(self.long_text.strip())), a('')
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
class ShortcutMapping(Mapping):
|
||||||
|
setting_name: str = 'map'
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, name: str, key: str, action_def: str, short_text: str, long_text: str, add_to_default: bool, documented: bool, group: 'Group', only: Only
|
self, name: str, key: str, action_def: str, short_text: str, long_text: str, add_to_default: bool, documented: bool, group: 'Group', only: Only
|
||||||
@ -86,7 +203,8 @@ class ShortcutMapping:
|
|||||||
return f'{self.key} {self.action_def}'
|
return f'{self.key} {self.action_def}'
|
||||||
|
|
||||||
|
|
||||||
class MouseMapping:
|
class MouseMapping(Mapping):
|
||||||
|
setting_name: str = 'mouse_map'
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, name: str, button: str, event: str, modes: str, action_def: str,
|
self, name: str, button: str, event: str, modes: str, action_def: str,
|
||||||
@ -139,6 +257,80 @@ class Group:
|
|||||||
else:
|
else:
|
||||||
yield x
|
yield x
|
||||||
|
|
||||||
|
def as_conf(self, commented: bool = False, level: int = 0) -> List[str]:
|
||||||
|
ans: List[str] = []
|
||||||
|
a = ans.append
|
||||||
|
if level:
|
||||||
|
a('#: ' + self.title + ' {{''{')
|
||||||
|
a('')
|
||||||
|
if self.start_text:
|
||||||
|
a(render_block(self.start_text))
|
||||||
|
a('')
|
||||||
|
else:
|
||||||
|
ans.extend(('# vim:fileencoding=utf-8:ft=conf:foldmethod=marker', ''))
|
||||||
|
|
||||||
|
option_groups = {}
|
||||||
|
current_group: List[Option] = []
|
||||||
|
coalesced = set()
|
||||||
|
for item in self:
|
||||||
|
if isinstance(item, Option):
|
||||||
|
if current_group:
|
||||||
|
if item.needs_coalescing:
|
||||||
|
current_group.append(item)
|
||||||
|
coalesced.add(id(item))
|
||||||
|
continue
|
||||||
|
option_groups[id(current_group[0])] = current_group[1:]
|
||||||
|
current_group = [item]
|
||||||
|
else:
|
||||||
|
current_group.append(item)
|
||||||
|
if current_group:
|
||||||
|
option_groups[id(current_group[0])] = current_group[1:]
|
||||||
|
|
||||||
|
for item in self:
|
||||||
|
if isinstance(item, Option):
|
||||||
|
if id(item) in coalesced:
|
||||||
|
continue
|
||||||
|
lines = item.as_conf(option_group=option_groups[id(item)])
|
||||||
|
else:
|
||||||
|
lines = item.as_conf(commented, level + 1)
|
||||||
|
ans.extend(lines)
|
||||||
|
|
||||||
|
if level:
|
||||||
|
if self.end_text:
|
||||||
|
a('')
|
||||||
|
a(render_block(self.end_text))
|
||||||
|
a('#: }}''}')
|
||||||
|
a('')
|
||||||
|
else:
|
||||||
|
map_groups = []
|
||||||
|
start: Optional[int] = None
|
||||||
|
count: Optional[int] = None
|
||||||
|
for i, line in enumerate(ans):
|
||||||
|
if line.startswith('map ') or line.startswith('mouse_map '):
|
||||||
|
if start is None:
|
||||||
|
start = i
|
||||||
|
count = 1
|
||||||
|
else:
|
||||||
|
if count is not None:
|
||||||
|
count += 1
|
||||||
|
else:
|
||||||
|
if start is not None and count is not None:
|
||||||
|
map_groups.append((start, count))
|
||||||
|
start = count = None
|
||||||
|
for start, count in map_groups:
|
||||||
|
r = range(start, start + count)
|
||||||
|
sz = max(len(ans[i].split(' ', 3)[1]) for i in r)
|
||||||
|
for i in r:
|
||||||
|
line = ans[i]
|
||||||
|
parts = line.split(' ', 3)
|
||||||
|
parts[1] = parts[1].ljust(sz)
|
||||||
|
ans[i] = ' '.join(parts)
|
||||||
|
|
||||||
|
if commented:
|
||||||
|
ans = [x if x.startswith('#') or not x.strip() else ('# ' + x) for x in ans]
|
||||||
|
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def resolve_import(name: str, module: Any = None) -> Callable:
|
def resolve_import(name: str, module: Any = None) -> Callable:
|
||||||
ans = None
|
ans = None
|
||||||
@ -268,5 +460,5 @@ class Definition:
|
|||||||
def add_deprecation(self, parser_name: str, *aliases: str) -> None:
|
def add_deprecation(self, parser_name: str, *aliases: str) -> None:
|
||||||
self.deprecations[self.parser_func(parser_name)] = aliases
|
self.deprecations[self.parser_func(parser_name)] = aliases
|
||||||
|
|
||||||
def as_conf(self, commented: bool = False) -> str:
|
def as_conf(self, commented: bool = False) -> List[str]:
|
||||||
raise NotImplementedError('TODO:')
|
return self.root_group.as_conf(commented)
|
||||||
|
|||||||
@ -80,7 +80,7 @@ def cached_values_for(name: str) -> Generator[Dict, None, None]:
|
|||||||
|
|
||||||
def commented_out_default_config() -> str:
|
def commented_out_default_config() -> str:
|
||||||
from .options.definition import definition
|
from .options.definition import definition
|
||||||
return definition.as_conf(commented=True)
|
return '\n'.join(definition.as_conf(commented=True))
|
||||||
|
|
||||||
|
|
||||||
def prepare_config_file_for_editing() -> str:
|
def prepare_config_file_for_editing() -> str:
|
||||||
|
|||||||
@ -539,8 +539,8 @@ agr('performance', 'Performance tuning')
|
|||||||
opt('repaint_delay', '10',
|
opt('repaint_delay', '10',
|
||||||
option_type='positive_int',
|
option_type='positive_int',
|
||||||
long_text='''
|
long_text='''
|
||||||
Delay (in milliseconds) between screen updates. Decreasing it, increases frames-
|
Delay (in milliseconds) between screen updates. Decreasing it, increases frames-per-second
|
||||||
per-second (FPS) at the cost of more CPU usage. The default value yields ~100
|
(FPS) at the cost of more CPU usage. The default value yields ~100
|
||||||
FPS which is more than sufficient for most uses. Note that to actually achieve
|
FPS which is more than sufficient for most uses. Note that to actually achieve
|
||||||
100 FPS you have to either set :opt:`sync_to_monitor` to no or use a monitor
|
100 FPS you have to either set :opt:`sync_to_monitor` to no or use a monitor
|
||||||
with a high refresh rate. Also, to minimize latency when there is pending input
|
with a high refresh rate. Also, to minimize latency when there is pending input
|
||||||
@ -1127,7 +1127,7 @@ opt('mark3_foreground', 'black',
|
|||||||
|
|
||||||
opt('mark3_background', '#f274bc',
|
opt('mark3_background', '#f274bc',
|
||||||
option_type='to_color',
|
option_type='to_color',
|
||||||
long_text='Color for marks of type 1 (violet)'
|
long_text='Color for marks of type 3 (violet)'
|
||||||
)
|
)
|
||||||
|
|
||||||
opt('color16', '#000000',
|
opt('color16', '#000000',
|
||||||
@ -2459,8 +2459,8 @@ terminal programs, only change it if you know what you are doing, not because
|
|||||||
you read some advice on Stack Overflow to change it. The TERM variable is used
|
you read some advice on Stack Overflow to change it. The TERM variable is used
|
||||||
by various programs to get information about the capabilities and behavior of
|
by various programs to get information about the capabilities and behavior of
|
||||||
the terminal. If you change it, depending on what programs you run, and how
|
the terminal. If you change it, depending on what programs you run, and how
|
||||||
different the terminal you are changing it to is, various things from key-
|
different the terminal you are changing it to is, various things from key-presses,
|
||||||
presses, to colors, to various advanced features may not work.
|
to colors, to various advanced features may not work.
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
egr() # }}}
|
egr() # }}}
|
||||||
@ -3092,26 +3092,26 @@ map('Increase font size',
|
|||||||
)
|
)
|
||||||
map('Increase font size',
|
map('Increase font size',
|
||||||
'increase_font_size kitty_mod+plus change_font_size all +2.0',
|
'increase_font_size kitty_mod+plus change_font_size all +2.0',
|
||||||
documented=False,
|
documented=True,
|
||||||
)
|
)
|
||||||
map('Increase font size',
|
map('Increase font size',
|
||||||
'increase_font_size kitty_mod+kp_add change_font_size all +2.0',
|
'increase_font_size kitty_mod+kp_add change_font_size all +2.0',
|
||||||
documented=False,
|
documented=True,
|
||||||
)
|
)
|
||||||
map('Increase font size',
|
map('Increase font size',
|
||||||
'increase_font_size cmd+plus change_font_size all +2.0',
|
'increase_font_size cmd+plus change_font_size all +2.0',
|
||||||
only="macos",
|
only="macos",
|
||||||
documented=False,
|
documented=True,
|
||||||
)
|
)
|
||||||
map('Increase font size',
|
map('Increase font size',
|
||||||
'increase_font_size cmd+equal change_font_size all +2.0',
|
'increase_font_size cmd+equal change_font_size all +2.0',
|
||||||
only="macos",
|
only="macos",
|
||||||
documented=False,
|
documented=True,
|
||||||
)
|
)
|
||||||
map('Increase font size',
|
map('Increase font size',
|
||||||
'increase_font_size cmd+shift+equal change_font_size all +2.0',
|
'increase_font_size cmd+shift+equal change_font_size all +2.0',
|
||||||
only="macos",
|
only="macos",
|
||||||
documented=False,
|
documented=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
map('Decrease font size',
|
map('Decrease font size',
|
||||||
@ -3123,12 +3123,12 @@ map('Decrease font size',
|
|||||||
map('Decrease font size',
|
map('Decrease font size',
|
||||||
'decrease_font_size cmd+minus change_font_size all -2.0',
|
'decrease_font_size cmd+minus change_font_size all -2.0',
|
||||||
only="macos",
|
only="macos",
|
||||||
documented=False,
|
documented=True,
|
||||||
)
|
)
|
||||||
map('Decrease font size',
|
map('Decrease font size',
|
||||||
'decrease_font_size cmd+shift+minus change_font_size all -2.0',
|
'decrease_font_size cmd+shift+minus change_font_size all -2.0',
|
||||||
only="macos",
|
only="macos",
|
||||||
documented=False,
|
documented=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
map('Reset font size',
|
map('Reset font size',
|
||||||
@ -3137,7 +3137,7 @@ map('Reset font size',
|
|||||||
map('Reset font size',
|
map('Reset font size',
|
||||||
'reset_font_size cmd+0 change_font_size all 0',
|
'reset_font_size cmd+0 change_font_size all 0',
|
||||||
only="macos",
|
only="macos",
|
||||||
documented=False,
|
documented=True,
|
||||||
)
|
)
|
||||||
egr('''
|
egr('''
|
||||||
To setup shortcuts for specific font sizes::
|
To setup shortcuts for specific font sizes::
|
||||||
@ -3291,7 +3291,7 @@ If you want to operate on all windows instead of just the current one, use :ital
|
|||||||
It is also possible to remap Ctrl+L to both scroll the current screen contents into the scrollback buffer
|
It is also possible to remap Ctrl+L to both scroll the current screen contents into the scrollback buffer
|
||||||
and clear the screen, instead of just clearing the screen::
|
and clear the screen, instead of just clearing the screen::
|
||||||
|
|
||||||
map ctrl+l combine : clear_terminal scroll active : send_text normal,application \x0c
|
map ctrl+l combine : clear_terminal scroll active : send_text normal,application \\x0c
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -3306,7 +3306,7 @@ the client program when pressing specified shortcut keys. For example::
|
|||||||
|
|
||||||
This will send "Special text" when you press the :kbd:`ctrl+alt+a` key
|
This will send "Special text" when you press the :kbd:`ctrl+alt+a` key
|
||||||
combination. The text to be sent is a python string literal so you can use
|
combination. The text to be sent is a python string literal so you can use
|
||||||
escapes like :code:`\x1b` to send control codes or :code:`\u21fb` to send
|
escapes like :code:`\\x1b` to send control codes or :code:`\\u21fb` to send
|
||||||
unicode characters (or you can just input the unicode characters directly as
|
unicode characters (or you can just input the unicode characters directly as
|
||||||
UTF-8 text). The first argument to :code:`send_text` is the keyboard modes in which to
|
UTF-8 text). The first argument to :code:`send_text` is the keyboard modes in which to
|
||||||
activate the shortcut. The possible values are :code:`normal` or :code:`application` or :code:`kitty`
|
activate the shortcut. The possible values are :code:`normal` or :code:`application` or :code:`kitty`
|
||||||
@ -3317,8 +3317,8 @@ terminals, and :code:`kitty` refers to the special kitty extended keyboard proto
|
|||||||
Another example, that outputs a word and then moves the cursor to the start of
|
Another example, that outputs a word and then moves the cursor to the start of
|
||||||
the line (same as pressing the Home key)::
|
the line (same as pressing the Home key)::
|
||||||
|
|
||||||
map ctrl+alt+a send_text normal Word\x1b[H
|
map ctrl+alt+a send_text normal Word\\x1b[H
|
||||||
map ctrl+alt+a send_text application Word\x1bOH
|
map ctrl+alt+a send_text application Word\\x1bOH
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
egr() # }}}
|
egr() # }}}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user