diff options
author | thing1 <thing1@seacrossedlovers.xyz> | 2025-04-01 18:10:15 +0000 |
---|---|---|
committer | thing1 <thing1@seacrossedlovers.xyz> | 2025-04-01 18:10:15 +0000 |
commit | dabaff03992c102c395314629f63ce93a2c1bd3a (patch) | |
tree | 990472507186637085165b7cbbf7abf15c10889a /elpa/evil-1.15.0/doc/source/_ext/elisp.py |
init commit
Diffstat (limited to 'elpa/evil-1.15.0/doc/source/_ext/elisp.py')
-rw-r--r-- | elpa/evil-1.15.0/doc/source/_ext/elisp.py | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/elpa/evil-1.15.0/doc/source/_ext/elisp.py b/elpa/evil-1.15.0/doc/source/_ext/elisp.py new file mode 100644 index 0000000..924d026 --- /dev/null +++ b/elpa/evil-1.15.0/doc/source/_ext/elisp.py @@ -0,0 +1,309 @@ +import re +from os import path +import json + +from docutils import nodes +from docutils.parsers.rst import Directive + +from sphinx import addnodes +from sphinx.domains import Domain, ObjType, Index +from sphinx.domains.std import StandardDomain +from sphinx.directives import ObjectDescription +from sphinx.roles import XRefRole +from sphinx.util.docfields import Field +from sphinx.util.nodes import make_refnode + + +with open(path.join(path.dirname(__file__), '..', '..', 'docstringdb.json')) as f: + DATA = json.load(f) + + +re_evilcode = re.compile(r"`(evil-[^']*)'") +re_code = re.compile(r"`([^:][^ ']*)'") +re_kwd = re.compile(r"`(:[^']*)'") +re_item = re.compile(r"([^\n])\n- ") +re_sexp = re.compile(r"\([A-Z \-\.'`\[\]]+\)|[A-Z\-]+") +re_capitals = re.compile(r"[A-Z\-]+") +re_nonspace = re.compile(r"[^ ]") +re_signature = re.compile(r'\(fn (.*)\)') +re_keymap_or_kbd = re.compile(r"\\[\[<]([^\]>]*)[\]>]") + +emphasis = [ + 'thing-at-point', +] + +def emacs_is_local(var): + return DATA[var]['local'] + +def emacs_default_value(var): + default = DATA[var]['default'] + tp = DATA[var]['default-type'] + if tp == 'string': + rep = repr(default)[1:-1] + return f'"{rep}"' + return str(default) + +def process_docstring(docstring, capitals=None): + # Remove explicit signature + docstring = re_signature.sub('', docstring) + + # Add code blocks to indented sections + def blockified_lines(lines): + in_block = False + for line in lines: + try: + indented = next(re_nonspace.finditer(line)).start(0) > 3 + except StopIteration: + indented = None + if indented is True and not in_block: + yield '.. code-block:: elisp' + yield '' + in_block = True + elif indented is False: + in_block = False + yield line + docstring = '\n'.join(blockified_lines(docstring.split('\n'))) + + # Substitute `evil-alpha' with :elisp:ref:`evil-alpha` + docstring = re_evilcode.sub(r':elisp:ref:`\1`', docstring) + + # Substitute `alpha' with ``alpha`` + docstring = re_code.sub(r'``\1``', docstring) + + # Substitute `:alpha' with ``alpha`` + docstring = re_kwd.sub(r'``\1``', docstring) + + # Translate key bindings + keymap = None + def substitute_binding(match): + nonlocal keymap + if match.group(0)[1] == '<': + keymap = match.group(1) + return '' + if keymap is None: + print(docstring) + assert False + return '???' + key = DATA[keymap]['keymap-inv'][match.group(1)] + return f':kbd:`{key}`' + docstring = re_keymap_or_kbd.sub(substitute_binding, docstring) + + # Add empty line between list items + docstring = re_item.sub(r'\1\n\n- ', docstring) + + if capitals is None: + capitals = [] + else: + capitals = list(capitals) + + # Find things that look like sexps + def substitute_sexp(match): + s = match.group(0) + if re_capitals.match(s): + if s in capitals: + return f'*{s}*' + return s + else: + capitals.extend(re_capitals.findall(s)) + return f'``{s}``' + docstring = re_sexp.sub(substitute_sexp, docstring) + + # Italicize some words + for s in emphasis: + docstring = docstring.replace(s, f'*{s}*') + + return docstring + +def emacs_variable_docstring(var): + docstring = DATA[var]['var-docstring'] + return process_docstring(docstring) + +def emacs_function_docstring(var): + docstring = DATA[var]['fn-docstring'] + return process_docstring(docstring, capitals=emacs_argnames(var)) + +def emacs_argnames(var): + arglist = emacs_arglist(var) + return re_capitals.findall(arglist) + +def emacs_arglist(var): + docstring = DATA[var]['fn-docstring'] + match = re_signature.search(docstring) + if match: + return match.group(1) + + arglist = [arg.upper() for arg in DATA[var]['arglist']] + state = None + ret = '' + for arg in arglist: + if arg in ('&REST', '&OPTIONAL'): + if state == '&OPTIONAL': + ret += ']' + state = arg + ret += ' [' + continue + ret += ('' if state in ('&REST', '&OPTIONAL') else ' ') + arg + if state == '&OPTIONAL': + state += '-CONT' + if state is not None and state.startswith('&OPTIONAL'): + ret += ']' + if state == '&REST': + ret += '...]' + return ret + + +class AbstractElisp(ObjectDescription): + + def add_target_and_index(self, name, sig, signode): + anchor = f'elispobj-{sig}' + signode['ids'].append(anchor) + + objs = self.env.domaindata['elisp']['objects'] + objs[sig] = { + 'docname': self.env.docname, + 'anchor': f'elispobj-{sig}', + 'type': self.object_type, + } + + +class AbstractVariable(AbstractElisp): + object_type = 'variable' + + def handle_signature(self, sig, signode): + signode += addnodes.desc_annotation(sig, sig) + return sig + + def run(self): + extra = [] + + default = self.default_value() + if default: + extra.append(f'Default: ``{default}``') + if self.is_buffer_local(): + extra.append('buffer-local') + + self.content.data.extend(['', ', '.join(extra)]) + retval = super().run() + return retval + + +class Variable(AbstractVariable): + required_arguments = 1 + optional_arguments = 2 + + def default_value(self): + try: + return self.arguments[1] + except IndexError: + return None + + def is_buffer_local(self): + return 'bufloc' in self.arguments[1:] + + +class AutoVariable(AbstractVariable): + required_arguments = 1 + + def is_buffer_local(self): + return emacs_is_local(self.arguments[0]) + + def default_value(self): + return emacs_default_value(self.arguments[0]) + + def run(self): + docstring = emacs_variable_docstring(self.arguments[0]) + self.content.data.extend(docstring.split('\n')) + return super().run() + + +class AutoFunction(AbstractElisp): + required_arguments = 1 + + @property + def object_type(self): + return 'macro' if DATA[self.arguments[0]]['macrop'] else 'function' + + def handle_signature(self, sig, signode): + args = emacs_arglist(sig) + signode += addnodes.desc_annotation(sig, f'({sig} {args})') + return sig + + def run(self): + docstring = emacs_function_docstring(self.arguments[0]) + self.content.data.extend(docstring.split('\n')) + return super().run() + + +class ElispIndex(Index): + name = 'index' + localname = 'Emacs lisp functions and variables' + shortname = 'Elisp' + + def generate(self, docnames=None): + index = {} + for name, item in self.domain.data['objects'].items(): + if name.startswith('evil-'): + letter = name[5].upper() + else: + letter = name[0].upper() + index.setdefault(letter, []).append(( + name, + 0, + item['docname'], + item['anchor'], + item['type'], + '', + '', + )) + + index = {k: sorted(v, key=lambda k: k[0].lower()) for k, v in index.items()} + index = list(index.items()) + index = sorted(index, key=lambda k: k[0]) + return index, True + + +class Elisp(Domain): + name = 'elisp' + label = 'Emacs lisp' + + object_types = { + 'variable': ObjType('variable', 'variable', 'obj'), + 'autovariable': ObjType('autovariable', 'autovariable', 'obj'), + 'autofunction': ObjType('autofunction', 'autofunction', 'obj'), + } + + directives = { + 'variable': Variable, + 'autovariable': AutoVariable, + 'autofunction': AutoFunction, + } + + roles = { + 'ref': XRefRole(), + } + + initial_data = { + 'objects': {}, + } + + indices = { + ElispIndex, + } + + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): + obj = self.data['objects'].get(target, None) + if obj is None: + return None + return make_refnode(builder, fromdocname, obj['docname'], obj['anchor'], contnode, obj['anchor']) + + +def setup(app): + app.add_domain(Elisp) + StandardDomain.initial_data['labels']['elispindex'] = ('elisp-index', '', 'Emacs lisp functions and variables') + StandardDomain.initial_data['anonlabels']['elispindex'] = ('elisp-index', '') + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } |