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>
|
||||
|
||||
import builtins
|
||||
import re
|
||||
import typing
|
||||
from importlib import import_module
|
||||
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
|
||||
@ -25,6 +27,64 @@ class 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:
|
||||
|
||||
def __init__(
|
||||
@ -40,6 +100,28 @@ class Option:
|
||||
self.parser_func = parser_func
|
||||
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:
|
||||
|
||||
@ -65,8 +147,43 @@ class MultiOption:
|
||||
def __iter__(self) -> Iterator[MultiVal]:
|
||||
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__(
|
||||
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}'
|
||||
|
||||
|
||||
class MouseMapping:
|
||||
class MouseMapping(Mapping):
|
||||
setting_name: str = 'mouse_map'
|
||||
|
||||
def __init__(
|
||||
self, name: str, button: str, event: str, modes: str, action_def: str,
|
||||
@ -139,6 +257,80 @@ class Group:
|
||||
else:
|
||||
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:
|
||||
ans = None
|
||||
@ -268,5 +460,5 @@ class Definition:
|
||||
def add_deprecation(self, parser_name: str, *aliases: str) -> None:
|
||||
self.deprecations[self.parser_func(parser_name)] = aliases
|
||||
|
||||
def as_conf(self, commented: bool = False) -> str:
|
||||
raise NotImplementedError('TODO:')
|
||||
def as_conf(self, commented: bool = False) -> List[str]:
|
||||
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:
|
||||
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:
|
||||
|
||||
@ -539,8 +539,8 @@ agr('performance', 'Performance tuning')
|
||||
opt('repaint_delay', '10',
|
||||
option_type='positive_int',
|
||||
long_text='''
|
||||
Delay (in milliseconds) between screen updates. Decreasing it, increases frames-
|
||||
per-second (FPS) at the cost of more CPU usage. The default value yields ~100
|
||||
Delay (in milliseconds) between screen updates. Decreasing it, increases frames-per-second
|
||||
(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
|
||||
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
|
||||
@ -1127,7 +1127,7 @@ opt('mark3_foreground', 'black',
|
||||
|
||||
opt('mark3_background', '#f274bc',
|
||||
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',
|
||||
@ -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
|
||||
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
|
||||
different the terminal you are changing it to is, various things from key-
|
||||
presses, to colors, to various advanced features may not work.
|
||||
different the terminal you are changing it to is, various things from key-presses,
|
||||
to colors, to various advanced features may not work.
|
||||
'''
|
||||
)
|
||||
egr() # }}}
|
||||
@ -3092,26 +3092,26 @@ map('Increase font size',
|
||||
)
|
||||
map('Increase font size',
|
||||
'increase_font_size kitty_mod+plus change_font_size all +2.0',
|
||||
documented=False,
|
||||
documented=True,
|
||||
)
|
||||
map('Increase font size',
|
||||
'increase_font_size kitty_mod+kp_add change_font_size all +2.0',
|
||||
documented=False,
|
||||
documented=True,
|
||||
)
|
||||
map('Increase font size',
|
||||
'increase_font_size cmd+plus change_font_size all +2.0',
|
||||
only="macos",
|
||||
documented=False,
|
||||
documented=True,
|
||||
)
|
||||
map('Increase font size',
|
||||
'increase_font_size cmd+equal change_font_size all +2.0',
|
||||
only="macos",
|
||||
documented=False,
|
||||
documented=True,
|
||||
)
|
||||
map('Increase font size',
|
||||
'increase_font_size cmd+shift+equal change_font_size all +2.0',
|
||||
only="macos",
|
||||
documented=False,
|
||||
documented=True,
|
||||
)
|
||||
|
||||
map('Decrease font size',
|
||||
@ -3123,12 +3123,12 @@ map('Decrease font size',
|
||||
map('Decrease font size',
|
||||
'decrease_font_size cmd+minus change_font_size all -2.0',
|
||||
only="macos",
|
||||
documented=False,
|
||||
documented=True,
|
||||
)
|
||||
map('Decrease font size',
|
||||
'decrease_font_size cmd+shift+minus change_font_size all -2.0',
|
||||
only="macos",
|
||||
documented=False,
|
||||
documented=True,
|
||||
)
|
||||
|
||||
map('Reset font size',
|
||||
@ -3137,7 +3137,7 @@ map('Reset font size',
|
||||
map('Reset font size',
|
||||
'reset_font_size cmd+0 change_font_size all 0',
|
||||
only="macos",
|
||||
documented=False,
|
||||
documented=True,
|
||||
)
|
||||
egr('''
|
||||
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
|
||||
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
|
||||
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
|
||||
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`
|
||||
@ -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
|
||||
the line (same as pressing the Home key)::
|
||||
|
||||
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 normal Word\\x1b[H
|
||||
map ctrl+alt+a send_text application Word\\x1bOH
|
||||
'''
|
||||
)
|
||||
egr() # }}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user