|
|
|
@ -15,15 +15,70 @@ serve to show the default.
|
|
|
|
|
|
|
|
|
|
# pylint:disable=redefined-builtin
|
|
|
|
|
|
|
|
|
|
import fnmatch
|
|
|
|
|
from io import open
|
|
|
|
|
from itertools import starmap
|
|
|
|
|
import os
|
|
|
|
|
import re
|
|
|
|
|
import shlex # pylint:disable=unused-import
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -- Support :dfhack-keybind:`command` ------------------------------------
|
|
|
|
|
# this is a custom directive that pulls info from dfhack.init-example
|
|
|
|
|
|
|
|
|
|
from docutils import nodes
|
|
|
|
|
from docutils.parsers.rst import roles
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_keybinds():
|
|
|
|
|
"""Get the implemented keybinds, and return a dict of
|
|
|
|
|
{tool: [(full_command, keybinding, context), ...]}.
|
|
|
|
|
"""
|
|
|
|
|
with open('dfhack.init-example') as f:
|
|
|
|
|
lines = [l.replace('keybinding add', '').strip() for l in f.readlines()
|
|
|
|
|
if l.startswith('keybinding add')]
|
|
|
|
|
keybindings = dict()
|
|
|
|
|
for k in lines:
|
|
|
|
|
first, command = k.split(' ', maxsplit=1)
|
|
|
|
|
bind, context = (first.split('@') + [''])[:2]
|
|
|
|
|
if ' ' not in command:
|
|
|
|
|
command = command.replace('"', '')
|
|
|
|
|
tool = command.split(' ')[0].replace('"', '')
|
|
|
|
|
keybindings[tool] = keybindings.get(tool, []) + [
|
|
|
|
|
(command, bind.split('-'), context)]
|
|
|
|
|
return keybindings
|
|
|
|
|
|
|
|
|
|
KEYBINDS = get_keybinds()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# pylint:disable=unused-argument,dangerous-default-value,too-many-arguments
|
|
|
|
|
def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner,
|
|
|
|
|
options={}, content=[]):
|
|
|
|
|
"""Custom role parser for DFHack default keybinds."""
|
|
|
|
|
roles.set_classes(options)
|
|
|
|
|
if text not in KEYBINDS:
|
|
|
|
|
msg = inliner.reporter.error(
|
|
|
|
|
'no keybinding for {} in dfhack.init-example'.format(text),
|
|
|
|
|
line=lineno)
|
|
|
|
|
prb = inliner.problematic(rawtext, rawtext, msg)
|
|
|
|
|
return [prb], [msg]
|
|
|
|
|
newnode = nodes.paragraph()
|
|
|
|
|
for cmd, key, ctx in KEYBINDS[text]:
|
|
|
|
|
n = nodes.paragraph()
|
|
|
|
|
newnode += n
|
|
|
|
|
n += nodes.strong('Keybinding: ', 'Keybinding: ')
|
|
|
|
|
for k in key:
|
|
|
|
|
n += nodes.inline(k, k, classes=['kbd'])
|
|
|
|
|
if cmd != text:
|
|
|
|
|
n += nodes.inline(' -> ', ' -> ')
|
|
|
|
|
n += nodes.literal(cmd, cmd, classes=['guilabel'])
|
|
|
|
|
if ctx:
|
|
|
|
|
n += nodes.inline(' in ', ' in ')
|
|
|
|
|
n += nodes.literal(ctx, ctx)
|
|
|
|
|
return [newnode], []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
roles.register_canonical_role('dfhack-keybind', dfhack_keybind_role_func)
|
|
|
|
|
|
|
|
|
|
# -- Autodoc for DFhack scripts -------------------------------------------
|
|
|
|
|
|
|
|
|
|
def doc_dir(dirname, files):
|
|
|
|
@ -46,28 +101,41 @@ def doc_dir(dirname, files):
|
|
|
|
|
command = line
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def doc_all_dirs():
|
|
|
|
|
"""Collect the commands and paths to include in our docs."""
|
|
|
|
|
scripts = []
|
|
|
|
|
for root, _, files in os.walk('scripts'):
|
|
|
|
|
scripts.extend(doc_dir(root, files))
|
|
|
|
|
return tuple(scripts)
|
|
|
|
|
|
|
|
|
|
DOC_ALL_DIRS = doc_all_dirs()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def document_scripts():
|
|
|
|
|
"""Autodoc for files with the magic script documentation marker strings.
|
|
|
|
|
|
|
|
|
|
Returns a dict of script-kinds to lists of .rst include directives.
|
|
|
|
|
"""
|
|
|
|
|
# First, we collect the commands and paths to include in our docs
|
|
|
|
|
scripts = []
|
|
|
|
|
for root, _, files in os.walk('scripts'):
|
|
|
|
|
scripts.extend(doc_dir(root, files))
|
|
|
|
|
# Next we split by type and create include directives sorted by command
|
|
|
|
|
kinds = {'base': [], 'devel': [], 'fix': [], 'gui': [], 'modtools': []}
|
|
|
|
|
for s in scripts:
|
|
|
|
|
for s in DOC_ALL_DIRS:
|
|
|
|
|
k_fname = s[0].split('/', 1)
|
|
|
|
|
if len(k_fname) == 1:
|
|
|
|
|
kinds['base'].append(s)
|
|
|
|
|
else:
|
|
|
|
|
kinds[k_fname[0]].append(s)
|
|
|
|
|
template = '.. _{}:\n\n.. include:: /{}\n' +\
|
|
|
|
|
' :start-after: {}\n :end-before: {}\n'
|
|
|
|
|
return {key: '\n\n'.join(starmap(template.format, sorted(value)))
|
|
|
|
|
|
|
|
|
|
def template(arg):
|
|
|
|
|
tmp = '.. _{}:\n\n.. include:: /{}\n' +\
|
|
|
|
|
' :start-after: {}\n :end-before: {}\n'
|
|
|
|
|
if arg[0] in KEYBINDS:
|
|
|
|
|
tmp += '\n:dfhack-keybind:`{}`\n'.format(arg[0])
|
|
|
|
|
return tmp.format(*arg)
|
|
|
|
|
|
|
|
|
|
return {key: '\n\n'.join(map(template, sorted(value)))
|
|
|
|
|
for key, value in kinds.items()}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_script_docs():
|
|
|
|
|
"""
|
|
|
|
|
Creates a file for eack kind of script (base/devel/fix/gui/modtools)
|
|
|
|
@ -97,10 +165,23 @@ def write_script_docs():
|
|
|
|
|
outfile.write(kinds[k])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Actually call the docs generator
|
|
|
|
|
write_script_docs()
|
|
|
|
|
def all_keybinds_documented():
|
|
|
|
|
"""Check that all keybindings are documented with the :dfhack-keybind:
|
|
|
|
|
directive somewhere."""
|
|
|
|
|
configured_binds = set(KEYBINDS)
|
|
|
|
|
script_commands = set(i[0] for i in DOC_ALL_DIRS)
|
|
|
|
|
with open('./docs/Plugins.rst') as f:
|
|
|
|
|
plugin_binds = set(re.findall(':dfhack-keybind:`(.*?)`', f.read()))
|
|
|
|
|
undocumented_binds = configured_binds - script_commands - plugin_binds
|
|
|
|
|
if undocumented_binds:
|
|
|
|
|
raise ValueError('The following DFHack commands have undocumented'
|
|
|
|
|
'keybindings: {}'.format(sorted(undocumented_binds)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Actually call the docs generator and run test
|
|
|
|
|
write_script_docs()
|
|
|
|
|
all_keybinds_documented()
|
|
|
|
|
|
|
|
|
|
# -- General configuration ------------------------------------------------
|
|
|
|
|
|
|
|
|
|
# If your documentation needs a minimal Sphinx version, state it here.
|
|
|
|
|