mirror of
https://github.com/YosysHQ/yosys
synced 2025-06-23 22:33:41 +00:00
Docs: Add :cmd:title: directive
Calling :cmd:title:`<cmd>` will generate a cross reference to `<cmd>`, but unlike :cmd:ref: which displays a literal block and puts the title (short_help) in the hovertext (the title field of an a-ref), :cmd:title: will display "<cmd> - <short_help>" as plain text. Thus replacing the previous use case of referring to :doc:`cmd/<cmd>`. Also refactor util py scripts to have more descriptive names.
This commit is contained in:
parent
a898ade473
commit
024bfcdc53
10 changed files with 52 additions and 40 deletions
738
docs/util/custom_directives.py
Normal file
738
docs/util/custom_directives.py
Normal file
|
@ -0,0 +1,738 @@
|
|||
# based on https://github.com/ofosos/sphinxrecipes/blob/master/sphinxrecipes/sphinxrecipes.py
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import cast
|
||||
import warnings
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Node, Element, Text
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst.states import Inliner
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.domains import Domain, Index
|
||||
from sphinx.domains.std import StandardDomain
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.directives.code import container_wrapper
|
||||
from sphinx.util.nodes import make_refnode
|
||||
from sphinx.util.docfields import Field, GroupedField
|
||||
from sphinx import addnodes
|
||||
|
||||
class TocNode(ObjectDescription):
|
||||
def add_target_and_index(
|
||||
self,
|
||||
name: str,
|
||||
sig: str,
|
||||
signode: addnodes.desc_signature
|
||||
) -> None:
|
||||
idx = ".".join(name.split("::"))
|
||||
signode['ids'].append(idx)
|
||||
|
||||
def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature) -> tuple[str, ...]:
|
||||
if 'tocname' not in sig_node:
|
||||
return ()
|
||||
|
||||
modname = sig_node.get('module')
|
||||
fullname = sig_node['fullname']
|
||||
|
||||
if modname:
|
||||
return (modname, *fullname.split('::'))
|
||||
else:
|
||||
return tuple(fullname.split('::'))
|
||||
|
||||
def _toc_entry_name(self, sig_node: addnodes.desc_signature) -> str:
|
||||
if not sig_node.get('_toc_parts'):
|
||||
return ''
|
||||
|
||||
config = self.env.app.config
|
||||
objtype = sig_node.parent.get('objtype')
|
||||
*parents, name = sig_node['_toc_parts']
|
||||
if config.toc_object_entries_show_parents == 'domain':
|
||||
return sig_node.get('tocname', name)
|
||||
if config.toc_object_entries_show_parents == 'hide':
|
||||
return name
|
||||
if config.toc_object_entries_show_parents == 'all':
|
||||
return '.'.join(parents + [name])
|
||||
return ''
|
||||
|
||||
class CommandNode(TocNode):
|
||||
"""A custom node that describes a command."""
|
||||
|
||||
name = 'cmd'
|
||||
required_arguments = 1
|
||||
|
||||
option_spec = TocNode.option_spec.copy()
|
||||
option_spec.update({
|
||||
'title': directives.unchanged,
|
||||
'tags': directives.unchanged
|
||||
})
|
||||
|
||||
doc_field_types = [
|
||||
GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')),
|
||||
]
|
||||
|
||||
def handle_signature(self, sig, signode: addnodes.desc_signature):
|
||||
signode['fullname'] = sig
|
||||
signode += addnodes.desc_addname(text="yosys> help ")
|
||||
signode += addnodes.desc_name(text=sig)
|
||||
return signode['fullname']
|
||||
|
||||
def add_target_and_index(self, name_cls, sig, signode):
|
||||
idx = type(self).name + '-' + sig
|
||||
signode['ids'].append(idx)
|
||||
if 'noindex' not in self.options:
|
||||
name = "{}.{}.{}".format(self.name, type(self).__name__, sig)
|
||||
tagmap = self.env.domaindata[type(self).name]['obj2tag']
|
||||
tagmap[name] = list(self.options.get('tags', '').split(' '))
|
||||
title = self.options.get('title', sig)
|
||||
titlemap = self.env.domaindata[type(self).name]['obj2title']
|
||||
titlemap[name] = title
|
||||
objs = self.env.domaindata[type(self).name]['objects']
|
||||
# (name, sig, typ, docname, anchor, prio)
|
||||
objs.append((name,
|
||||
sig,
|
||||
type(self).name,
|
||||
self.env.docname,
|
||||
idx,
|
||||
0))
|
||||
|
||||
class CommandUsageNode(TocNode):
|
||||
"""A custom node that describes command usages"""
|
||||
|
||||
name = 'cmdusage'
|
||||
|
||||
option_spec = TocNode.option_spec
|
||||
option_spec.update({
|
||||
'usage': directives.unchanged,
|
||||
})
|
||||
|
||||
doc_field_types = [
|
||||
GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')),
|
||||
]
|
||||
|
||||
def handle_signature(self, sig: str, signode: addnodes.desc_signature):
|
||||
parts = sig.split('::')
|
||||
if len(parts) > 2: parts.pop(0)
|
||||
use = parts[-1]
|
||||
signode['fullname'] = '::'.join(parts)
|
||||
usage = self.options.get('usage', use)
|
||||
if usage:
|
||||
signode['tocname'] = usage
|
||||
signode += addnodes.desc_name(text=usage)
|
||||
return signode['fullname']
|
||||
|
||||
def add_target_and_index(
|
||||
self,
|
||||
name: str,
|
||||
sig: str,
|
||||
signode: addnodes.desc_signature
|
||||
) -> None:
|
||||
idx = ".".join(name.split("::"))
|
||||
signode['ids'].append(idx)
|
||||
if 'noindex' not in self.options:
|
||||
tocname: str = signode.get('tocname', name)
|
||||
objs = self.env.domaindata[self.domain]['objects']
|
||||
# (name, sig, typ, docname, anchor, prio)
|
||||
objs.append((name,
|
||||
tocname,
|
||||
type(self).name,
|
||||
self.env.docname,
|
||||
idx,
|
||||
1))
|
||||
|
||||
class CommandOptionGroupNode(CommandUsageNode):
|
||||
"""A custom node that describes a group of related options"""
|
||||
|
||||
name = 'cmdoptiongroup'
|
||||
|
||||
option_spec = CommandUsageNode.option_spec
|
||||
|
||||
doc_field_types = [
|
||||
Field('opt', ('option',), label='', rolename='option')
|
||||
]
|
||||
|
||||
def transform_content(self, contentnode: addnodes.desc_content) -> None:
|
||||
"""hack `:option -thing: desc` into a proper option list"""
|
||||
newchildren = []
|
||||
for node in contentnode:
|
||||
newnode = node
|
||||
if isinstance(node, nodes.field_list):
|
||||
newnode = nodes.option_list()
|
||||
for field in node:
|
||||
is_option = False
|
||||
option_list_item = nodes.option_list_item()
|
||||
for child in field:
|
||||
if isinstance(child, nodes.field_name):
|
||||
option_group = nodes.option_group()
|
||||
option_list_item += option_group
|
||||
option = nodes.option()
|
||||
option_group += option
|
||||
name, text = child.rawsource.split(' ', 1)
|
||||
is_option = name == 'option'
|
||||
option += nodes.option_string(text=text)
|
||||
if not is_option: warnings.warn(f'unexpected option \'{name}\' in {field.source}')
|
||||
elif isinstance(child, nodes.field_body):
|
||||
description = nodes.description()
|
||||
description += child.children
|
||||
option_list_item += description
|
||||
if is_option:
|
||||
newnode += option_list_item
|
||||
newchildren.append(newnode)
|
||||
contentnode.children = newchildren
|
||||
|
||||
class PropNode(TocNode):
|
||||
name = 'prop'
|
||||
fieldname = 'props'
|
||||
|
||||
def handle_signature(self, sig: str, signode: addnodes.desc_signature):
|
||||
signode['fullname'] = sig
|
||||
signode['tocname'] = tocname = sig.split('::')[-1]
|
||||
signode += addnodes.desc_name(text=tocname)
|
||||
return signode['fullname']
|
||||
|
||||
def add_target_and_index(
|
||||
self,
|
||||
name: str,
|
||||
sig: str,
|
||||
signode: addnodes.desc_signature
|
||||
) -> None:
|
||||
idx = ".".join(name.split("::"))
|
||||
signode['ids'].append(idx)
|
||||
if 'noindex' not in self.options:
|
||||
tocname: str = signode.get('tocname', name)
|
||||
objs = self.env.domaindata[self.domain]['objects']
|
||||
# (name, sig, typ, docname, anchor, prio)
|
||||
objs.append((name,
|
||||
tocname,
|
||||
type(self).name,
|
||||
self.env.docname,
|
||||
idx,
|
||||
1))
|
||||
|
||||
class CellGroupedField(Field):
|
||||
"""Custom version of GroupedField which doesn't require content."""
|
||||
is_grouped = True
|
||||
list_type = nodes.bullet_list
|
||||
|
||||
def __init__(self, name: str, names: tuple[str, ...] = (), label: str = None,
|
||||
rolename: str = None, can_collapse: bool = False) -> None:
|
||||
super().__init__(name, names, label, True, rolename)
|
||||
self.can_collapse = can_collapse
|
||||
|
||||
def make_field(self, types: dict[str, list[Node]], domain: str,
|
||||
items: tuple, env: BuildEnvironment = None,
|
||||
inliner: Inliner = None, location: Node = None) -> nodes.field:
|
||||
fieldname = nodes.field_name('', self.label)
|
||||
listnode = self.list_type()
|
||||
for fieldarg, content in items:
|
||||
par = nodes.paragraph()
|
||||
if fieldarg:
|
||||
par.extend(self.make_xrefs(self.rolename, domain,
|
||||
fieldarg, nodes.Text,
|
||||
env=env, inliner=inliner, location=location))
|
||||
|
||||
if len(content) == 1 and (
|
||||
isinstance(content[0], nodes.Text) or
|
||||
(isinstance(content[0], nodes.inline) and len(content[0]) == 1 and
|
||||
isinstance(content[0][0], nodes.Text))):
|
||||
par += nodes.Text(' -- ')
|
||||
par += content
|
||||
listnode += nodes.list_item('', par)
|
||||
|
||||
if len(items) == 1 and self.can_collapse:
|
||||
list_item = cast(nodes.list_item, listnode[0])
|
||||
fieldbody = nodes.field_body('', list_item[0])
|
||||
return nodes.field('', fieldname, fieldbody)
|
||||
|
||||
fieldbody = nodes.field_body('', listnode)
|
||||
return nodes.field('', fieldname, fieldbody)
|
||||
|
||||
class CellNode(TocNode):
|
||||
"""A custom node that describes an internal cell."""
|
||||
|
||||
name = 'cell'
|
||||
|
||||
option_spec = {
|
||||
'title': directives.unchanged,
|
||||
'ports': directives.unchanged,
|
||||
'properties': directives.unchanged,
|
||||
}
|
||||
|
||||
doc_field_types = [
|
||||
CellGroupedField('props', label='Properties', rolename='prop',
|
||||
names=('properties', 'property', 'tag', 'tags'),
|
||||
can_collapse=True),
|
||||
]
|
||||
|
||||
def handle_signature(self, sig: str, signode: addnodes.desc_signature):
|
||||
signode['fullname'] = sig
|
||||
signode['tocname'] = tocname = sig.split('::')[-1]
|
||||
signode += addnodes.desc_addname(text="yosys> help ")
|
||||
signode += addnodes.desc_name(text=tocname)
|
||||
return signode['fullname']
|
||||
|
||||
def add_target_and_index(
|
||||
self,
|
||||
name: str,
|
||||
sig: str,
|
||||
signode: addnodes.desc_signature
|
||||
) -> None:
|
||||
idx = ".".join(name.split("::"))
|
||||
signode['ids'].append(idx)
|
||||
if 'noindex' not in self.options:
|
||||
tocname: str = signode.get('tocname', name)
|
||||
title: str = self.options.get('title', sig)
|
||||
titlemap = self.env.domaindata[self.domain]['obj2title']
|
||||
titlemap[name] = title
|
||||
props = self.options.get('properties', '')
|
||||
if props:
|
||||
propmap = self.env.domaindata[self.domain]['obj2prop']
|
||||
propmap[name] = props.split(' ')
|
||||
objs = self.env.domaindata[self.domain]['objects']
|
||||
# (name, sig, typ, docname, anchor, prio)
|
||||
objs.append((name,
|
||||
tocname,
|
||||
type(self).name,
|
||||
self.env.docname,
|
||||
idx,
|
||||
0))
|
||||
|
||||
def transform_content(self, contentnode: addnodes.desc_content) -> None:
|
||||
# Add the cell title to the body
|
||||
if 'title' in self.options:
|
||||
titlenode = nodes.paragraph()
|
||||
titlenode += nodes.strong()
|
||||
titlenode[-1] += nodes.Text(self.options['title'])
|
||||
contentnode.insert(0, titlenode)
|
||||
|
||||
class CellSourceNode(TocNode):
|
||||
"""A custom code block for including cell source."""
|
||||
|
||||
name = 'cellsource'
|
||||
|
||||
option_spec = {
|
||||
"source": directives.unchanged_required,
|
||||
"language": directives.unchanged_required,
|
||||
'lineno-start': int,
|
||||
}
|
||||
|
||||
def handle_signature(
|
||||
self,
|
||||
sig,
|
||||
signode: addnodes.desc_signature
|
||||
) -> str:
|
||||
language = self.options.get('language')
|
||||
signode['fullname'] = sig
|
||||
signode['tocname'] = f"{sig.split('::')[-2]} {language}"
|
||||
signode += addnodes.desc_name(text="Simulation model")
|
||||
signode += addnodes.desc_sig_space()
|
||||
signode += addnodes.desc_addname(text=f'({language})')
|
||||
return signode['fullname']
|
||||
|
||||
def run(self) -> list[Node]:
|
||||
"""Override run to parse content as a code block"""
|
||||
if ':' in self.name:
|
||||
self.domain, self.objtype = self.name.split(':', 1)
|
||||
else:
|
||||
self.domain, self.objtype = '', self.name
|
||||
self.indexnode = addnodes.index(entries=[])
|
||||
|
||||
node = addnodes.desc()
|
||||
node.document = self.state.document
|
||||
source, line = self.get_source_info()
|
||||
if line is not None:
|
||||
line -= 1
|
||||
self.state.document.note_source(source, line)
|
||||
node['domain'] = self.domain
|
||||
# 'desctype' is a backwards compatible attribute
|
||||
node['objtype'] = node['desctype'] = self.objtype
|
||||
node['noindex'] = noindex = ('noindex' in self.options)
|
||||
node['noindexentry'] = ('noindexentry' in self.options)
|
||||
node['nocontentsentry'] = ('nocontentsentry' in self.options)
|
||||
if self.domain:
|
||||
node['classes'].append(self.domain)
|
||||
node['classes'].append(node['objtype'])
|
||||
|
||||
self.names = []
|
||||
signatures = self.get_signatures()
|
||||
for sig in signatures:
|
||||
# add a signature node for each signature in the current unit
|
||||
# and add a reference target for it
|
||||
signode = addnodes.desc_signature(sig, '')
|
||||
self.set_source_info(signode)
|
||||
node.append(signode)
|
||||
try:
|
||||
# name can also be a tuple, e.g. (classname, objname);
|
||||
# this is strictly domain-specific (i.e. no assumptions may
|
||||
# be made in this base class)
|
||||
name = self.handle_signature(sig, signode)
|
||||
except ValueError:
|
||||
# signature parsing failed
|
||||
signode.clear()
|
||||
signode += addnodes.desc_name(sig, sig)
|
||||
continue # we don't want an index entry here
|
||||
finally:
|
||||
# Private attributes for ToC generation. Will be modified or removed
|
||||
# without notice.
|
||||
if self.env.app.config.toc_object_entries:
|
||||
signode['_toc_parts'] = self._object_hierarchy_parts(signode)
|
||||
signode['_toc_name'] = self._toc_entry_name(signode)
|
||||
else:
|
||||
signode['_toc_parts'] = ()
|
||||
signode['_toc_name'] = ''
|
||||
if name not in self.names:
|
||||
self.names.append(name)
|
||||
if not noindex:
|
||||
# only add target and index entry if this is the first
|
||||
# description of the object with this name in this desc block
|
||||
self.add_target_and_index(name, sig, signode)
|
||||
|
||||
# handle code
|
||||
code = '\n'.join(self.content)
|
||||
literal: Element = nodes.literal_block(code, code)
|
||||
if 'lineno-start' in self.options:
|
||||
literal['linenos'] = True
|
||||
literal['highlight_args'] = {
|
||||
'linenostart': self.options['lineno-start']
|
||||
}
|
||||
literal['classes'] += self.options.get('class', [])
|
||||
literal['language'] = self.options.get('language')
|
||||
literal = container_wrapper(self, literal, self.options.get('source'))
|
||||
|
||||
return [self.indexnode, node, literal]
|
||||
|
||||
class CellGroupNode(TocNode):
|
||||
name = 'cellgroup'
|
||||
|
||||
option_spec = {
|
||||
'caption': directives.unchanged,
|
||||
}
|
||||
|
||||
def add_target_and_index(self, name: str, sig: str, signode: addnodes.desc_signature) -> None:
|
||||
if self.options.get('caption', ''):
|
||||
super().add_target_and_index(name, sig, signode)
|
||||
|
||||
def handle_signature(
|
||||
self,
|
||||
sig,
|
||||
signode: addnodes.desc_signature
|
||||
) -> str:
|
||||
signode['fullname'] = fullname = sig
|
||||
caption = self.options.get("caption", fullname)
|
||||
if caption:
|
||||
signode['tocname'] = caption
|
||||
signode += addnodes.desc_name(text=caption)
|
||||
return fullname
|
||||
|
||||
class TagIndex(Index):
|
||||
"""A custom directive that creates a tag matrix."""
|
||||
|
||||
name = 'tag'
|
||||
localname = 'Tag Index'
|
||||
shortname = 'Tag'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TagIndex, self).__init__(*args, **kwargs)
|
||||
|
||||
def generate(self, docnames=None):
|
||||
"""Return entries for the index given by *name*. If *docnames* is
|
||||
given, restrict to entries referring to these docnames.
|
||||
The return value is a tuple of ``(content, collapse)``, where
|
||||
* collapse* is a boolean that determines if sub-entries should
|
||||
start collapsed (for output formats that support collapsing
|
||||
sub-entries).
|
||||
*content* is a sequence of ``(letter, entries)`` tuples, where *letter*
|
||||
is the "heading" for the given *entries*, usually the starting letter.
|
||||
*entries* is a sequence of single entries, where a single entry is a
|
||||
sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``.
|
||||
The items in this sequence have the following meaning:
|
||||
- `name` -- the name of the index entry to be displayed
|
||||
- `subtype` -- sub-entry related type:
|
||||
0 -- normal entry
|
||||
1 -- entry with sub-entries
|
||||
2 -- sub-entry
|
||||
- `docname` -- docname where the entry is located
|
||||
- `anchor` -- anchor for the entry within `docname`
|
||||
- `extra` -- extra info for the entry
|
||||
- `qualifier` -- qualifier for the description
|
||||
- `descr` -- description for the entry
|
||||
Qualifier and description are not rendered e.g. in LaTeX output.
|
||||
"""
|
||||
|
||||
content = {}
|
||||
|
||||
objs = {name: (dispname, typ, docname, anchor)
|
||||
for name, dispname, typ, docname, anchor, prio
|
||||
in self.domain.get_objects()}
|
||||
|
||||
tmap = {}
|
||||
tags = self.domain.data[f'obj2{self.name}']
|
||||
for name, tags in tags.items():
|
||||
for tag in tags:
|
||||
tmap.setdefault(tag,[])
|
||||
tmap[tag].append(name)
|
||||
|
||||
for tag in tmap.keys():
|
||||
lis = content.setdefault(tag, [])
|
||||
objlis = tmap[tag]
|
||||
for objname in objlis:
|
||||
dispname, typ, docname, anchor = objs[objname]
|
||||
lis.append((
|
||||
dispname, 0, docname,
|
||||
anchor,
|
||||
docname, '', typ
|
||||
))
|
||||
ret = [(k, v) for k, v in sorted(content.items())]
|
||||
|
||||
return (ret, True)
|
||||
|
||||
class CommandIndex(Index):
|
||||
name = 'cmd'
|
||||
localname = 'Command Reference'
|
||||
shortname = 'Command'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CommandIndex, self).__init__(*args, **kwargs)
|
||||
|
||||
def generate(self, docnames=None):
|
||||
"""Return entries for the index given by *name*. If *docnames* is
|
||||
given, restrict to entries referring to these docnames.
|
||||
The return value is a tuple of ``(content, collapse)``, where
|
||||
* collapse* is a boolean that determines if sub-entries should
|
||||
start collapsed (for output formats that support collapsing
|
||||
sub-entries).
|
||||
*content* is a sequence of ``(letter, entries)`` tuples, where *letter*
|
||||
is the "heading" for the given *entries*, usually the starting letter.
|
||||
*entries* is a sequence of single entries, where a single entry is a
|
||||
sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``.
|
||||
The items in this sequence have the following meaning:
|
||||
- `name` -- the name of the index entry to be displayed
|
||||
- `subtype` -- sub-entry related type:
|
||||
0 -- normal entry
|
||||
1 -- entry with sub-entries
|
||||
2 -- sub-entry
|
||||
- `docname` -- docname where the entry is located
|
||||
- `anchor` -- anchor for the entry within `docname`
|
||||
- `extra` -- extra info for the entry
|
||||
- `qualifier` -- qualifier for the description
|
||||
- `descr` -- description for the entry
|
||||
Qualifier and description are not rendered e.g. in LaTeX output.
|
||||
"""
|
||||
|
||||
content = {}
|
||||
items = ((name, dispname, typ, docname, anchor)
|
||||
for name, dispname, typ, docname, anchor, prio
|
||||
in self.domain.get_objects()
|
||||
if typ == self.name)
|
||||
items = sorted(items, key=lambda item: item[0])
|
||||
for name, dispname, typ, docname, anchor in items:
|
||||
lis = content.setdefault(self.shortname, [])
|
||||
lis.append((
|
||||
dispname, 0, docname,
|
||||
anchor,
|
||||
'', '', typ
|
||||
))
|
||||
ret = [(k, v) for k, v in sorted(content.items())]
|
||||
|
||||
return (ret, True)
|
||||
|
||||
class CellIndex(CommandIndex):
|
||||
name = 'cell'
|
||||
localname = 'Internal cell reference'
|
||||
shortname = 'Internal cell'
|
||||
|
||||
class PropIndex(TagIndex):
|
||||
"""A custom directive that creates a properties matrix."""
|
||||
|
||||
name = 'prop'
|
||||
localname = 'Property Index'
|
||||
shortname = 'Prop'
|
||||
fieldname = 'props'
|
||||
|
||||
def generate(self, docnames=None):
|
||||
content = {}
|
||||
|
||||
cells = {name: (dispname, docname, anchor)
|
||||
for name, dispname, typ, docname, anchor, _
|
||||
in self.domain.get_objects()
|
||||
if typ == 'cell'}
|
||||
props = {name: (dispname, docname, anchor)
|
||||
for name, dispname, typ, docname, anchor, _
|
||||
in self.domain.get_objects()
|
||||
if typ == 'prop'}
|
||||
|
||||
tmap: dict[str, list[str]] = {}
|
||||
tags: dict[str, list[str]] = self.domain.data[f'obj2{self.name}']
|
||||
for name, tags in tags.items():
|
||||
for tag in tags:
|
||||
tmap.setdefault(tag,[])
|
||||
tmap[tag].append(name)
|
||||
|
||||
for tag in sorted(tmap.keys()):
|
||||
test = re.match(r'^(\w+[_-])', tag)
|
||||
tag_prefix = test.group(1)
|
||||
lis = content.setdefault(tag_prefix, [])
|
||||
try:
|
||||
dispname, docname, anchor = props[tag]
|
||||
except KeyError:
|
||||
dispname = tag
|
||||
docname = anchor = ''
|
||||
lis.append((
|
||||
dispname, 1, docname,
|
||||
anchor,
|
||||
'', '', docname or 'unavailable'
|
||||
))
|
||||
objlis = tmap[tag]
|
||||
for objname in sorted(objlis):
|
||||
dispname, docname, anchor = cells[objname]
|
||||
lis.append((
|
||||
dispname, 2, docname,
|
||||
anchor,
|
||||
'', '', docname
|
||||
))
|
||||
ret = [(k, v) for k, v in sorted(content.items())]
|
||||
|
||||
return (ret, True)
|
||||
|
||||
class TitleRefRole(XRefRole):
|
||||
"""XRefRole used which has the cmd title as the displayed text."""
|
||||
pass
|
||||
|
||||
class CommandDomain(Domain):
|
||||
name = 'cmd'
|
||||
label = 'Yosys commands'
|
||||
|
||||
roles = {
|
||||
'ref': XRefRole(),
|
||||
'title': TitleRefRole(),
|
||||
}
|
||||
|
||||
directives = {
|
||||
'def': CommandNode,
|
||||
'usage': CommandUsageNode,
|
||||
'optiongroup': CommandOptionGroupNode,
|
||||
}
|
||||
|
||||
indices = {
|
||||
CommandIndex,
|
||||
TagIndex
|
||||
}
|
||||
|
||||
initial_data = {
|
||||
'objects': [], # object list
|
||||
'obj2tag': {}, # name -> tags
|
||||
'obj2title': {}, # name -> title
|
||||
}
|
||||
|
||||
def get_full_qualified_name(self, node):
|
||||
"""Return full qualified name for a given node"""
|
||||
return "{}.{}.{}".format(type(self).name,
|
||||
type(node).__name__,
|
||||
node.arguments[0])
|
||||
|
||||
def get_objects(self):
|
||||
for obj in self.data['objects']:
|
||||
yield(obj)
|
||||
|
||||
def resolve_xref(self, env, fromdocname, builder, typ,
|
||||
target, node, contnode):
|
||||
|
||||
match = [(docname, anchor, name)
|
||||
for name, sig, typ, docname, anchor, prio
|
||||
in self.get_objects() if sig == target]
|
||||
|
||||
if match:
|
||||
todocname = match[0][0]
|
||||
targ = match[0][1]
|
||||
qual_name = match[0][2]
|
||||
title = self.data['obj2title'].get(qual_name, targ)
|
||||
|
||||
if typ == 'title':
|
||||
# caller wants the title in the content of the node
|
||||
cmd = contnode.astext()
|
||||
contnode = Text(f'{cmd} - {title}')
|
||||
return make_refnode(builder, fromdocname, todocname,
|
||||
targ, contnode)
|
||||
else:
|
||||
# cmd title as hover text
|
||||
return make_refnode(builder, fromdocname, todocname,
|
||||
targ, contnode, title)
|
||||
else:
|
||||
print(f"Missing ref for {target} in {fromdocname} ")
|
||||
return None
|
||||
|
||||
class CellDomain(CommandDomain):
|
||||
name = 'cell'
|
||||
label = 'Yosys internal cells'
|
||||
|
||||
roles = CommandDomain.roles.copy()
|
||||
roles.update({
|
||||
'prop': XRefRole()
|
||||
})
|
||||
|
||||
directives = {
|
||||
'def': CellNode,
|
||||
'defprop': PropNode,
|
||||
'source': CellSourceNode,
|
||||
'group': CellGroupNode,
|
||||
}
|
||||
|
||||
indices = {
|
||||
CellIndex,
|
||||
PropIndex
|
||||
}
|
||||
|
||||
initial_data = {
|
||||
'objects': [], # object list
|
||||
'obj2prop': {}, # name -> properties
|
||||
'obj2title': {}, # name -> title
|
||||
}
|
||||
|
||||
def get_objects(self):
|
||||
for obj in self.data['objects']:
|
||||
yield(obj)
|
||||
|
||||
def autoref(name, rawtext: str, text: str, lineno, inliner: Inliner,
|
||||
options=None, content=None):
|
||||
words = text.split(' ')
|
||||
if len(words) == 2 and words[0] == "help":
|
||||
IsLinkable = True
|
||||
thing = words[1]
|
||||
else:
|
||||
IsLinkable = len(words) == 1 and words[0][0] != '-'
|
||||
thing = words[0]
|
||||
if IsLinkable:
|
||||
role = 'cell:ref' if thing[0] == '$' else 'cmd:ref'
|
||||
text = f'{text} <{thing}>'
|
||||
else:
|
||||
role = 'yoscrypt'
|
||||
return inliner.interpreted(rawtext, text, role, lineno)
|
||||
|
||||
def setup(app: Sphinx):
|
||||
app.add_domain(CommandDomain)
|
||||
app.add_domain(CellDomain)
|
||||
|
||||
StandardDomain.initial_data['labels']['commandindex'] =\
|
||||
('cmd-cmd', '', 'Command Reference')
|
||||
StandardDomain.initial_data['labels']['tagindex'] =\
|
||||
('cmd-tag', '', 'Tag Index')
|
||||
StandardDomain.initial_data['labels']['cellindex'] =\
|
||||
('cell-cell', '', 'Internal cell reference')
|
||||
StandardDomain.initial_data['labels']['propindex'] =\
|
||||
('cell-prop', '', 'Property Index')
|
||||
|
||||
StandardDomain.initial_data['anonlabels']['commandindex'] =\
|
||||
('cmd-cmd', '')
|
||||
StandardDomain.initial_data['anonlabels']['tagindex'] =\
|
||||
('cmd-tag', '')
|
||||
StandardDomain.initial_data['anonlabels']['cellindex'] =\
|
||||
('cell-cell', '')
|
||||
StandardDomain.initial_data['anonlabels']['propindex'] =\
|
||||
('cell-prop', '')
|
||||
|
||||
app.add_role('autoref', autoref)
|
||||
|
||||
return {'version': '0.2'}
|
Loading…
Add table
Add a link
Reference in a new issue