Dont maintain ref_map manually

This commit is contained in:
Kovid Goyal 2022-08-19 11:20:50 +05:30
parent 6f6e23bf61
commit 5deed81737
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 100 additions and 28 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
*.pyo *.pyo
*_stub.pyi *_stub.pyi
*_generated.go *_generated.go
*_generated.h
/.dmypy.json /.dmypy.json
/tags /tags
/build/ /build/

35
docs/extract-rst-targets.py Executable file
View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
import os
import re
from typing import Dict, Iterator
tgt_pat = re.compile(r'^.. _(\S+?):$', re.MULTILINE)
def find_explicit_targets(text: str) -> Iterator[str]:
for m in tgt_pat.finditer(text):
yield m.group(1)
def main() -> Dict[str, str]:
refs = {'github_discussions': 'https://github.com/kovidgoyal/kitty/discussions'}
base = os.path.dirname(os.path.abspath(__file__))
for dirpath, dirnames, filenames in os.walk(base):
if 'generated' in dirnames:
dirnames.remove('generated')
for f in filenames:
if f.endswith('.rst'):
with open(os.path.join(dirpath, f)) as stream:
raw = stream.read()
href = os.path.relpath(stream.name, base).replace(os.sep, '/')
href = href.rpartition('.')[0] + '/'
for explicit_target in find_explicit_targets(raw):
refs[explicit_target] = href + f'#{explicit_target}'
return {'ref': refs}
if __name__ == '__main__':
import json
print(json.dumps(main(), indent=2))

View File

@ -11,7 +11,7 @@ from typing import (
) )
from .cli_stub import CLIOptions from .cli_stub import CLIOptions
from .conf.types import ref_map from .conf.types import resolve_ref
from .conf.utils import resolve_config from .conf.utils import resolve_config
from .constants import ( from .constants import (
appname, clear_handled_signals, defconf, is_macos, str_version, website_url appname, clear_handled_signals, defconf, is_macos, str_version, website_url
@ -131,9 +131,15 @@ def code(x: str) -> str:
return cyan(x) return cyan(x)
def text_and_target(x: str) -> Tuple[str, str]:
parts = x.split('<')
return parts[0].strip(), parts[-1].rstrip('>')
@role @role
def term(x: str) -> str: def term(x: str) -> str:
return italic(x.split('<', 1)[0]) t, q = text_and_target(x)
return italic(t)
@role @role
@ -165,11 +171,10 @@ def doc(x: str) -> str:
@role @role
def ref(x: str) -> str: def ref(x: str) -> str:
parts = x.split('<') t, q = text_and_target(x)
t = parts[0].strip() url = resolve_ref(q)
q = parts[-1].rstrip('>') if url:
if q in ref_map(): return hyperlink_for_url(url, t)
return hyperlink_for_url(ref_map()[q], t)
return t return t

View File

@ -45,24 +45,29 @@ def expand_opt_references(conf_name: str, text: str) -> str:
@run_once @run_once
def ref_map() -> Dict[str, str]: def ref_map() -> Dict[str, Dict[str, str]]:
from kitty.actions import get_all_actions import json
ref_map = { from ..fast_data_types import get_docs_ref_map
'layouts': f'{website_url("overview")}#layouts', ans: Dict[str, Dict[str, str]] = json.loads(get_docs_ref_map())
'include': f'{website_url("conf")}#include', return ans
'watchers': f'{website_url("launch")}#watchers',
'sessions': f'{website_url("overview")}#startup-sessions',
'functional': f'{website_url("keyboard-protocol")}#functional-key-definitions', def resolve_ref(ref: str) -> str:
'ssh_copy_command': f'{website_url("kittens/ssh")}#ssh-copy-command', m = ref_map()
'shell_integration': website_url("shell-integration"), href = m['ref'].get(ref, '')
'rc_custom_auth': f'{website_url("remote-control")}#rc-custom-auth', if href:
'clone_shell': f'{website_url("shell-integration")}#clone-shell', return href
'github_discussions': 'https://github.com/kovidgoyal/kitty/discussions', if ref.startswith('conf-'):
} base = 'generated/' + ref.rpartition('-')[0]
for actions in get_all_actions().values(): href = f'{website_url(base)}#{ref}'
for ac in actions: elif ref.startswith('at_'):
ref_map[f'action-{ac.name}'] = f'{website_url("actions")}#' + ac.name.replace('_', '-') href = f'{website_url("generated/cli-kitty-at")}#{ref}'
return ref_map elif ref.startswith('action-group-'):
href = f'{website_url("generated/actions")}#{ref}'
elif ref.startswith('action-'):
frag = ref.partition('-')[-1]
href = f'{website_url("generated/actions")}#{frag}'
return href
def remove_markup(text: str) -> str: def remove_markup(text: str) -> str:
@ -76,7 +81,10 @@ def remove_markup(text: str) -> str:
def sub(m: 'Match[str]') -> str: def sub(m: 'Match[str]') -> str:
if m.group(1) == 'ref': if m.group(1) == 'ref':
t, q = extract(m) t, q = extract(m)
return f'{t} <{ref_map()[q]}>' url = resolve_ref(q)
if not url:
raise KeyError(f'Failed to resolve :ref: {q}')
return f'{t} <{url}>'
if m.group(1) == 'doc': if m.group(1) == 'doc':
t, q = extract(m) t, q = extract(m)
return f'{t} <{website_url(q.lstrip("/"))}>' return f'{t} <{website_url(q.lstrip("/"))}>'
@ -91,7 +99,7 @@ def remove_markup(text: str) -> str:
return t return t
if m.group(1) == 'disc': if m.group(1) == 'disc':
t, q = extract(m) t, q = extract(m)
return f'{t} {ref_map()["github_discussions"]}/{q}' return f'{t} {resolve_ref("github_discussions")}/{q}'
return str(m.group(2)) return str(m.group(2))
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL) return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)

View File

@ -209,10 +209,16 @@ py_getpeereid(PyObject *self UNUSED, PyObject *args) {
return Py_BuildValue("ii", u, g); return Py_BuildValue("ii", u, g);
} }
#include "docs_ref_map_generated.h"
static PyObject*
get_docs_ref_map(PyObject *self UNUSED, PyObject *args UNUSED) {
return PyBytes_FromStringAndSize(docs_ref_map, sizeof(docs_ref_map));
}
static PyMethodDef module_methods[] = { static PyMethodDef module_methods[] = {
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""}, {"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
{"get_docs_ref_map", (PyCFunction)get_docs_ref_map, METH_NOARGS, ""},
{"getpeereid", (PyCFunction)py_getpeereid, METH_VARARGS, ""}, {"getpeereid", (PyCFunction)py_getpeereid, METH_VARARGS, ""},
{"wcswidth", (PyCFunction)wcswidth_std, METH_O, ""}, {"wcswidth", (PyCFunction)wcswidth_std, METH_O, ""},
{"open_tty", open_tty, METH_VARARGS, ""}, {"open_tty", open_tty, METH_VARARGS, ""},

View File

@ -1486,3 +1486,4 @@ class SingleKey:
def set_use_os_log(yes: bool) -> None: ... def set_use_os_log(yes: bool) -> None: ...
def get_docs_ref_map() -> bytes: ...

View File

@ -14,6 +14,7 @@ import subprocess
import sys import sys
import sysconfig import sysconfig
import tempfile import tempfile
import textwrap
import time import time
from contextlib import suppress from contextlib import suppress
from functools import lru_cache, partial from functools import lru_cache, partial
@ -839,10 +840,25 @@ def init_env_from_args(args: Options, native_optimizations: bool = False) -> Non
) )
def build_ref_map() -> str:
m = runpy.run_path('docs/extract-rst-targets.py')
d = m['main']()
h = 'static const char docs_ref_map[] = {\n' + textwrap.fill(', '.join(map(str, bytearray(json.dumps(d).encode('utf-8'))))) + '\n};'
dest = 'kitty/docs_ref_map_generated.h'
q = ''
with suppress(FileNotFoundError), open(dest) as f:
q = f.read()
if q != h:
with open(dest, 'w') as f:
f.write(h)
return dest
def build(args: Options, native_optimizations: bool = True, call_init: bool = True) -> None: def build(args: Options, native_optimizations: bool = True, call_init: bool = True) -> None:
if call_init: if call_init:
init_env_from_args(args, native_optimizations) init_env_from_args(args, native_optimizations)
sources, headers = find_c_files() sources, headers = find_c_files()
headers.append(build_ref_map())
compile_c_extension( compile_c_extension(
kitty_env(), 'kitty/fast_data_types', args.compilation_database, sources, headers kitty_env(), 'kitty/fast_data_types', args.compilation_database, sources, headers
) )
@ -1375,7 +1391,7 @@ def clean() -> None:
safe_remove( safe_remove(
'build', 'compile_commands.json', 'link_commands.json', 'build', 'compile_commands.json', 'link_commands.json',
'linux-package', 'kitty.app', 'asan-launcher', 'linux-package', 'kitty.app', 'asan-launcher',
'kitty-profile') 'kitty-profile', 'docs/generated')
clean_launcher_dir('kitty/launcher') clean_launcher_dir('kitty/launcher')
def excluded(root: str, d: str) -> bool: def excluded(root: str, d: str) -> bool: