diff --git a/.gitignore b/.gitignore
index 676a98024..e4706db04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ dev
__pycache__
glfw/wayland-*-client-protocol.[ch]
docs/_build
+docs/generated
diff --git a/docs/_static/custom.css b/docs/_static/custom.css
index 3053eb1c7..e2c170288 100644
--- a/docs/_static/custom.css
+++ b/docs/_static/custom.css
@@ -82,3 +82,25 @@ div.sphinxsidebar {
#sidebartoc a[href]:hover {
color: red;
}
+
+
+.green {
+ color: green;
+}
+
+.cyan {
+ color: blue;
+}
+
+.italic {
+ font-style: italic;
+}
+
+.bold {
+ font-weight: bold;
+}
+
+.title {
+ font-size: larger;
+ font-weight: bold
+}
diff --git a/docs/conf.py b/docs/conf.py
index dcbdf1a76..8f6931dc6 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -78,7 +78,7 @@ def create_shortcut_defs():
# 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']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'generated/cli-*']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
@@ -86,6 +86,11 @@ def create_shortcut_defs():
rst_prolog = '''
.. |kitty.conf| replace:: :doc:`kitty.conf `
.. |kitty| replace:: *kitty*
+.. role:: green
+.. role:: italic
+.. role:: bold
+.. role:: cyan
+.. role:: title
''' + create_shortcut_defs()
@@ -258,7 +263,17 @@ def add_html_context(app, pagename, templatename, context, *args):
# }}}
+# CLI docs {{{
+def write_cli_docs():
+ from kitty.cli import option_spec_as_rst
+ with open('generated/cli-kitty.rst', 'w') as f:
+ f.write(option_spec_as_rst(appname='kitty'))
+
+# }}}
+
+
def setup(app):
+ write_cli_docs()
app.add_role('iss', issue_role)
app.add_role('commit', commit_role)
app.connect('html-page-context', add_html_context)
diff --git a/docs/index.rst b/docs/index.rst
index 078e3fcfc..6741cb5dc 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -60,6 +60,10 @@ packages are available for:
`Fedora `_.
+See :doc:`Configuring kitty ` for help on configuring kitty and
+:doc:`Invocation ` for the command line arguments kitty supports.
+
+
.. contents::
diff --git a/docs/invocation.rst b/docs/invocation.rst
new file mode 100644
index 000000000..98d932b25
--- /dev/null
+++ b/docs/invocation.rst
@@ -0,0 +1,4 @@
+The kitty command line interface
+====================================
+
+.. include:: generated/cli-kitty.rst
diff --git a/kitty/cli.py b/kitty/cli.py
index 696e5e3c1..261160453 100644
--- a/kitty/cli.py
+++ b/kitty/cli.py
@@ -103,6 +103,7 @@
# Debugging options
--version -v
+type=bool-set
The current {appname} version
@@ -246,6 +247,25 @@ def sub(m):
return text
+def prettify_rst(text):
+
+ def roled(role):
+ def f(x):
+ if x.startswith(':'):
+ return x
+ return ':{}:`{}`'.format(role, x.replace('`', r'\`'))
+ return f
+
+ def sub(m):
+ t = m.group(2)
+ for ch in m.group(1):
+ t = {'C': roled('cyan'), '_': roled('italic'), '*': roled('bold'), 'G': roled('green'), 'T': roled('title')}[ch](t)
+ return t
+
+ text = re.sub(r'[|]([a-zA-Z_*]+?) (.+?)[|]', sub, text)
+ return text
+
+
def version(add_rev=False):
rev = ''
from . import fast_data_types
@@ -354,6 +374,61 @@ def wa(text, indent=0, leading_indent=None):
print_help_for_seq.allow_pager = True
+def seq_as_rst(seq, usage, message, appname):
+ import textwrap
+ blocks = []
+ a = blocks.append
+
+ usage = '[program-to-run ...]' if usage is None else usage
+ optstring = '[options] ' if seq else ''
+ a('.. highlight:: sh')
+ a('.. code-block:: sh')
+ a('')
+ a(' {} {}{}'.format(appname, optstring, usage))
+ a('')
+ message = message or (
+ 'Run the |G {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
+ ' arguments following the |_ options|. For example: {appname} /bin/sh'
+ ).format(appname=appname)
+ a(prettify_rst(message))
+ a('')
+ if seq:
+ a('Options')
+ a('----------------')
+ for opt in seq:
+ if isinstance(opt, str):
+ a(opt)
+ a('~' * (len(opt) + 10))
+ continue
+ help_text = opt['help']
+ if help_text == '!':
+ continue # hidden option
+ defn = '.. option:: '
+ if not opt.get('type', '').startswith('bool-'):
+ val_name = ' <{}>'.format(opt['dest'].upper())
+ else:
+ val_name = ''
+ a(defn + ', '.join(o + val_name for o in sorted(opt['aliases'])))
+ if opt.get('help'):
+ defval = opt.get('default')
+ t = help_text.replace('%default', str(defval)).strip()
+ a('')
+ t = re.sub(r':$', '::', t, flags=re.MULTILINE)
+ t = t.replace('::\n\n\t ', '::\n \n ')
+ t = t.replace('\n\n\t ', '\n \n')
+ t = re.sub(r'(--[a-zA-Z0-9-]+)', r':option:`\1` ', t)
+ t = re.sub('"(.+?)"', r'``\1``', t)
+ a(textwrap.indent(prettify_rst(t), ' ' * 4))
+ if defval is not None:
+ a(textwrap.indent('Default: {}'.format(defval), ' ' * 4))
+ if 'choices' in opt:
+ a(textwrap.indent('Choices: {}'.format(', '.join(opt['choices'])), ' ' * 4))
+ a('')
+
+ text = '\n'.join(blocks)
+ return text
+
+
def defval_for_opt(opt):
dv = opt.get('default')
typ = opt.get('type', '')
@@ -489,6 +564,13 @@ def options_spec():
return options_spec.ans
+def option_spec_as_rst(ospec=options_spec, usage=None, message=None, appname=None):
+ options = parse_option_spec(ospec())
+ seq, disabled = options
+ oc = Options(seq, usage, message, appname)
+ return seq_as_rst(oc.seq, oc.usage, oc.message, oc.appname)
+
+
def parse_args(args=None, ospec=options_spec, usage=None, message=None, appname=None):
options = parse_option_spec(ospec())
seq, disabled = options