datalad-handbook/dataladhandbook_support/roles.py
Michael Hanke 9115f13a79 New roles :dlcmd:, :gitcmd:, :gitannexcmd:, and :shcmd:
These should be used instead of ':command:`datalad ...`' etc. It will
insert a link to the online command manpage on first use for a specific
command in any source document (except for `shcmd`).

Purpose:

- `dlcmd`: any datalad subcommand
- `gitcmd`: any git subcommand
- `gitannexcmd`: any git-annex subcommand
- `shcmd`: any (UNIX-like) command

This makes the manual handling of that insertion (which is presently
different between online and print version) obsolete.

For now, the manpage links for `dlcmd` are only valid for datalad-core
commands (also true for the established `:manpage:` approach).
Additional support for specific extension can be added in code in the
future.
2023-08-05 20:22:34 +02:00

142 lines
3.8 KiB
Python

# -*- coding: utf-8 -*-
from docutils.parsers.rst import roles
from .directives import (
findoutmoreref,
windowswitref,
)
from sphinx.addnodes import manpage
from sphinx.roles import (
XRefRole,
)
class HandbookRefRole(XRefRole):
"""Custom :ref: roles base class"""
refnodecls = None
refnodeclsname = None
def run(self):
# must be 'ref' or sphinx will refuse to resolve the reference
self.refdomain, self.reftype = 'std', 'ref'
# values don't matter, sphinx will overwrite them when resolving
# the pending ref
self.classes = ['handbookref']
return self.create_xref_node()
def result_nodes(self, document, env, node, is_ref):
nodes, messages = super().result_nodes(document, env, node, is_ref)
# we wrap the generated reference into an inline (container)
# to enable consistent markup etc.
r = self.refnodecls(
'handbookrawsrc', '', *nodes, classes=[self.refnodeclsname])
return ([r], messages)
class FindOutMoreRole(HandbookRefRole):
""":find-out-more: ref"""
refnodecls = findoutmoreref
refnodeclsname = 'findoutmoreref'
class WindowsWitRole(HandbookRefRole):
""":windows-wit: ref"""
refnodecls = windowswitref
refnodeclsname = 'windowswitref'
class anycmd(manpage):
visited = set()
cmdname = ''
manual_url_tmpl = None
@classmethod
def visit_html(cls, self, node):
assert len(node.children) == 1, \
f"`{cls.cmdname}` must not have more than one child node"
self.visit_inline(node)
self.body.append(f'<code>{cls.cmdname} ')
@classmethod
def depart_html(cls, self, node):
self.depart_inline(node)
self.body.append('</code>')
# get the name of the subcommand
subcmdname = str(node.children[0]).split()[0]
# reference ID (source doc + command + subcommand)
man_id = (self.document.attributes['source'], cls.cmdname, subcmdname)
# only for the first reference, include a manual link
if cls.manual_url_tmpl and man_id not in cls.visited:
self.body.append(' (<a href="{url}">manual</a>)'.format(
url=cls.manual_url_tmpl.format(
cmdname=subcmdname,
)
))
cls.visited.add(man_id)
@classmethod
def visit_latex(cls, self, node):
self.visit_literal(node)
self.body.append(f'{cls.cmdname} ')
@classmethod
def depart_latex(cls, self, node):
self.depart_literal(node)
class shcmd(anycmd):
cmdname = ''
manual_url_tmpl = None
class dlcmd(anycmd):
cmdname = 'datalad'
manual_url_tmpl = \
'https://docs.datalad.org/generated/man/datalad-{cmdname}.html'
class gitcmd(anycmd):
cmdname = 'git'
manual_url_tmpl = "https://git-scm.com/docs/git-{cmdname}"
class gitannexcmd(anycmd):
cmdname = 'git annex'
manual_url_tmpl = "https://git-annex.branchable.com/git-annex-{cmdname}"
hb_roles = {
'find-out-more': FindOutMoreRole(),
'windows-wit': WindowsWitRole(),
}
for rolename, cls in (
('dlcmd', dlcmd),
('gitcmd', gitcmd),
('gitannexcmd', gitannexcmd),
('shcmd', shcmd),
):
hb_roles[rolename] = roles.CustomRole(
rolename,
roles.GenericRole(rolename, cls),
{'classes': [rolename]},
)
def setup(app):
for rolename, func in hb_roles.items():
roles.register_local_role(rolename, func)
for cls in (dlcmd, gitcmd, gitannexcmd, shcmd):
app.add_node(
cls,
html=(cls.visit_html, cls.depart_html),
latex=(cls.visit_latex, cls.depart_latex),
)
return {
'parallel_read_safe': True,
'parallel_write_safe': True,
}
# vim: set expandtab shiftwidth=4 softtabstop=4 :