Start work on documenting the conf file

This commit is contained in:
Kovid Goyal 2018-06-03 16:07:40 +05:30
parent 3ce9e1932c
commit 9dc9fb2012
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 296 additions and 35 deletions

View File

@ -6,6 +6,7 @@
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
import os
import subprocess
from collections import defaultdict
from functools import partial
@ -80,7 +81,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'generated/cli-*']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'generated/cli-*', 'generated/conf-*']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
@ -298,8 +299,66 @@ def write_cli_docs():
# }}}
# config file docs {{{
def render_group(a, group):
a(group.short_text)
a('^' * (len(group.short_text) + 20))
a('')
if group.start_text:
a(group.start_text)
a('')
def render_conf(ref_prefix, all_options):
from kitty.conf.definition import merged_opts
ans = []
a = ans.append
current_group = None
all_options = list(all_options)
for i, opt in enumerate(all_options):
if not opt.short_text:
continue
if opt.group is not current_group:
if current_group and current_group.end_text:
a(''), a(current_group.end_text)
current_group = opt.group
render_group(a, current_group)
mopts = list(merged_opts(all_options, opt, i))
for mo in mopts:
a('.. _conf_{}_{}:'.format(ref_prefix, mo.name))
a('')
a(opt.short_text)
a('_' * (len(opt.short_text) + 20))
a('')
a('.. code-block:: ini')
a('')
sz = max(len(x.name) for x in mopts)
for mo in mopts:
a((' {:%ds} {}' % sz).format(mo.name, mo.defval_as_string))
a('')
if opt.long_text:
a(opt.long_text)
a('')
return '\n'.join(ans)
def write_conf_docs():
from kitty.config_data import all_options
with open('generated/conf-kitty.rst', 'w', encoding='utf-8') as f:
print('.. highlight:: ini\n', file=f)
f.write(render_conf('kitty', all_options.values()))
# }}}
def setup(app):
try:
os.mkdir('generated')
except FileExistsError:
pass
write_cli_docs()
write_conf_docs()
app.add_role('iss', partial(num_role, 'issues'))
app.add_role('pull', partial(num_role, 'pull'))
app.add_role('commit', commit_role)

View File

@ -1,25 +1,32 @@
:tocdepth: 2
Configuring kitty
===============================
.. highlight:: ini
|kitty| is highly customizable, everything from keyboard shortcuts, to painting
frames-per-second. See the heavily commented default config file below for an
overview of all customization possibilities.
frames-per-second. See below for an overview of all customization
possibilities.
You can open the config file within kitty by pressing |sc_edit_config_file|.
You can also display the current configuration by running ``kitty
--debug-config``.
.. literalinclude:: ../kitty/kitty.conf
:language: ini
.. _confloc:
|kitty| looks for a config file in the OS config directories (usually
:file:`~/.config/kitty/kitty.conf` and additionally
:file:`~/Library/Preferences/kitty/kitty.conf` on macOS) but you can pass a
specific path via the :option:`kitty --config` option or use the ``KITTY_CONFIG_DIRECTORY``
environment variable. See the :option:`kitty --config` option
for full details.
:file:`~/.config/kitty/kitty.conf`) but you can pass a specific path via the
:option:`kitty --config` option or use the ``KITTY_CONFIG_DIRECTORY``
environment variable. See the :option:`kitty --config` option for full details.
You can include secondary config files via the :code:`include` directive. If
you use a relative path for include, it is resolved with respect to the
location of the current config file. Note that environment variables are
expanded, so :code:`${USER}.conf` becomes :file:`name.conf` if
:code:`USER=name`. For example::
include other.conf
.. include:: /generated/conf-kitty.rst

View File

@ -12,10 +12,8 @@ from weakref import WeakValueDictionary
from .cli import create_opts, parse_args
from .conf.utils import to_cmdline
from .config import (
MINIMUM_FONT_SIZE, initial_window_size_func,
prepare_config_file_for_editing
)
from .config import initial_window_size_func, prepare_config_file_for_editing
from .config_data import MINIMUM_FONT_SIZE
from .constants import (
appname, config_dir, set_boss, supports_primary_selection
)

137
kitty/conf/definition.py Normal file
View File

@ -0,0 +1,137 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import re
from functools import partial
from .utils import to_bool
def to_string(x):
return x
class Group:
__slots__ = 'name', 'short_text', 'start_text', 'end_text'
def __init__(self, name, short_text, start_text='', end_text=''):
self.name, self.short_text = name, short_text.strip()
self.start_text, self.end_text = start_text.strip(), end_text.strip()
class Option:
__slots__ = 'name', 'group', 'short_text', 'long_text', 'option_type', 'defval_as_string', 'defval', 'add_to_default'
def __init__(self, name, group, defval, option_type, short_text, long_text, add_to_default):
self.name, self.group, self.short_text = name, group, short_text
self.long_text, self.option_type = long_text.strip(), option_type
self.defval_as_string, self.defval = defval, option_type(defval)
self.add_to_default = add_to_default
def option(
all_options,
group,
name,
defval,
short_text='',
long_text='',
option_type=to_string,
add_to_default=True
):
is_multiple = name.startswith('+')
if is_multiple:
name = name[1:]
defval_type = type(defval)
if defval_type is not str:
if option_type is to_string:
if defval_type is bool:
option_type = to_bool
else:
option_type = defval_type
if defval_type is bool:
defval = 'yes' if defval else 'no'
else:
defval = str(defval)
key = name
if is_multiple:
key = name + ' ' + defval.partition(' ')[0]
ans = Option(name, group[0], defval, option_type, short_text, long_text, add_to_default)
all_options[key] = ans
return ans
def option_func(all_options, all_groups):
all_groups = {k: Group(k, *v) for k, v in all_groups.items()}
group = [None]
def change_group(name):
group[0] = all_groups[name]
return partial(option, all_options, group), change_group, all_groups
def merged_opts(all_options, opt, i):
yield opt
for k in range(i + 1, len(all_options)):
q = all_options[k]
if not q.short_text:
yield q
else:
break
def remove_markup(text):
return re.sub(r':(.+?):`(.+?)`', r'\2', text, flags=re.DOTALL)
def render_block(text):
text = remove_markup(text)
lines = text.splitlines()
return '\n'.join('#: ' + line for line in lines)
def render_group(a, group):
a('# ' + group.short_text + ' {{{')
a('')
if group.start_text:
a(render_block(group.start_text))
a('')
def as_conf_file(all_options):
ans = []
a = ans.append
current_group = None
all_options = list(all_options)
for i, opt in enumerate(all_options):
if not opt.short_text:
continue
if opt.group is not current_group:
if current_group:
if current_group.end_text:
a(''), a(current_group.end_text)
a('# }}}')
current_group = opt.group
render_group(a, current_group)
mopts = list(merged_opts(all_options, opt, i))
a(render_block(opt.short_text))
a('')
if opt.long_text:
a(render_block(opt.long_text))
a('')
sz = max(len(x.name) for x in mopts)
for mo in mopts:
prefix = '' if mo.add_to_default else '# '
a(('{}{:%ds} {}' % sz).format(prefix, mo.name, mo.defval_as_string))
a('')
if current_group:
if current_group.end_text:
a(''), a(current_group.end_text)
a('# }}}')
return ans

View File

@ -10,24 +10,19 @@ from collections import namedtuple
from contextlib import contextmanager
from . import fast_data_types as defines
from .conf.definition import as_conf_file
from .conf.utils import (
init_config, key_func, load_config as _load_config, merge_dicts,
parse_config_base, positive_float, positive_int, python_string, to_bool,
to_cmdline, to_color, unit_float
)
from .config_data import all_options
from .constants import cache_dir, defconf
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts
from .rgb import color_as_int, color_from_int
from .utils import log_error
MINIMUM_FONT_SIZE = 4
def to_font_size(x):
return max(MINIMUM_FONT_SIZE, float(x))
cshapes = {
'block': CURSOR_BLOCK,
'beam': CURSOR_BEAM,
@ -378,7 +373,6 @@ type_map = {
'scrollback_lines': positive_int,
'scrollback_pager': to_cmdline,
'open_url_with': to_cmdline,
'font_size': to_font_size,
'focus_follows_mouse': to_bool,
'cursor_shape': to_cursor_shape,
'open_url_modifiers': to_modifiers,
@ -587,15 +581,12 @@ def initial_window_size_func(opts, cached_values):
def commented_out_default_config():
with open(default_config_path, encoding='utf-8', errors='replace') as f:
config = f.read()
lines = []
for line in config.splitlines():
if line.strip() and not line.startswith('#'):
ans = []
for line in as_conf_file(all_options.values()):
if line and line[0] != '#':
line = '# ' + line
lines.append(line)
config = '\n'.join(lines)
return config
ans.append(line)
return '\n'.join(ans)
def prepare_config_file_for_editing():
@ -605,7 +596,7 @@ def prepare_config_file_for_editing():
os.makedirs(d)
except FileExistsError:
pass
with open(defconf, 'w') as f:
with open(defconf, 'w', encoding='utf-8') as f:
f.write(commented_out_default_config())
return defconf

69
kitty/config_data.py Normal file
View File

@ -0,0 +1,69 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from gettext import gettext as _
from .conf.definition import option_func
MINIMUM_FONT_SIZE = 4
def to_font_size(x):
return max(MINIMUM_FONT_SIZE, float(x))
all_options = {}
o, g, all_groups = option_func(all_options, {
'fonts': [
_('Fonts'),
_('kitty has very powerful font management. You can configure individual\n'
'font faces and even specify special fonts for particular characters.')
],
})
type_map = {o.name: o.option_type for o in all_options.values()}
g('fonts') # {{{
o(
'font_family',
'monospace',
_('Font family'),
long_text=_('''
You can specify different fonts for the bold/italic/bold-italic variants.
By default they are derived automatically, by the OSes font system. Setting
them manually is useful for font families that have many weight variants like
Book, Medium, Thick, etc. For example::
font_family Operator Mono Book
bold_font Operator Mono Medium
italic_font Operator Mono Book Italic
bold_italic_font Operator Mono Medium Italic
''')
)
o('bold_font', 'auto')
o('italic_font', 'auto')
o('bold_italic_font', 'auto')
o('font_size', 11.0, _('Font size (in pts)'), option_type=to_font_size)
o(
'+symbol_map',
'U+E0A0-U+E0A2,U+E0B0-U+E0B3 PowerlineSymbols',
_('Font character mapping'),
add_to_default=False,
long_text=_('''
Map the specified unicode codepoints to a particular font. Useful if you need
special rendering for some symbols, such as for Powerline. Avoids the need for
patched fonts. Each unicode code point is specified in the form :code:`U+<code point
in hexadecimal>`. You can specify multiple code points, separated by commas and
ranges separated by hyphens. :code:`symbol_map` itself can be specified multiple times.
Syntax is::
symbol_map codepoints Font Family Name
'''))
# }}}