diff --git a/docs/source/conf.py b/docs/source/conf.py index fb2e603f1..a043f0b49 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -63,10 +63,13 @@ latex_elements = { extensions.append('sphinx.ext.todo') todo_include_todos = True +# custom cmd-ref parsing/linking +sys.path += [os.path.dirname(__file__) + "/../"] +extensions.append('util.cmdref') + def setup(sphinx): - sys.path += [os.path.dirname(__file__) + "/../util"] - from RtlilLexer import RtlilLexer + from util.RtlilLexer import RtlilLexer sphinx.add_lexer("RTLIL", RtlilLexer) - from YoscryptLexer import YoscryptLexer + from util.YoscryptLexer import YoscryptLexer sphinx.add_lexer("yoscrypt", YoscryptLexer) \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index df335444d..0a776393a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -13,6 +13,12 @@ Yosys Open SYnthesis Suite appendix +Indices +------- + +- :ref:`commandindex` +- :ref:`tagindex` + TODOs ----- diff --git a/docs/static/yosyshq.css b/docs/static/yosyshq.css index 0be7f7728..91a15c129 100644 --- a/docs/static/yosyshq.css +++ b/docs/static/yosyshq.css @@ -63,12 +63,12 @@ p { color: #6ecbd7 !important; } -.cmdref .highlight-yoscrypt .highlight pre { +.cmd.def .highlight-yoscrypt, .cmd.def .highlight pre { padding: 0%; margin: 0%; } -.cmdref .highlight-none .highlight pre { +.cmd.def .highlight-none, .cmd.def .highlight pre { padding-top: 0%; margin-top: 0%; } diff --git a/docs/util/__init__.py b/docs/util/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/util/cmdref.py b/docs/util/cmdref.py new file mode 100644 index 000000000..175bd20f0 --- /dev/null +++ b/docs/util/cmdref.py @@ -0,0 +1,240 @@ +# based on https://github.com/ofosos/sphinxrecipes/blob/master/sphinxrecipes/sphinxrecipes.py +# license: +# Copyright 2019 Mark Meyer +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import docutils +from docutils import nodes +import sphinx +from docutils.parsers import rst +from docutils.parsers.rst import directives +from sphinx.domains import Domain, Index +from sphinx.domains.std import StandardDomain +from sphinx.roles import XRefRole +from sphinx.directives import ObjectDescription +from sphinx.util.nodes import make_refnode +from sphinx import addnodes + +class CommandNode(ObjectDescription): + """A custom node that describes a command.""" + + required_arguments = 1 + + option_spec = { + 'title': directives.unchanged_required, + 'tags': directives.unchanged + } + + def handle_signature(self, sig, signode: addnodes.desc_signature): + signode += addnodes.desc_addname(text="yosys> help ") + signode += addnodes.desc_name(text=sig) + return sig + + def add_target_and_index(self, name_cls, sig, signode): + signode['ids'].append('cmd' + '-' + sig) + if 'noindex' not in self.options: + name = "{}.{}.{}".format('cmd', type(self).__name__, sig) + tmap = self.env.domaindata['cmd']['obj2tag'] + tmap[name] = list(self.options.get('tags', '').split(' ')) + objs = self.env.domaindata['cmd']['objects'] + objs.append((name, + sig, + self.options.get('title'), + self.env.docname, + 'cmd' + '-' + sig, + 0)) + +class TagIndex(Index): + """A custom directive that creates an 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['obj2tag'] + 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 + )) + re = [(k, v) for k, v in sorted(content.items())] + + return (re, 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()) + items = sorted(items, key=lambda item: item[0]) + for name, dispname, typ, docname, anchor in items: + lis = content.setdefault('Command', []) + lis.append(( + dispname, 0, docname, + anchor, + '', '', typ + )) + re = [(k, v) for k, v in sorted(content.items())] + + return (re, True) + + +class CommandDomain(Domain): + name = 'cmd' + label = 'Command Sample' + + roles = { + 'ref': XRefRole() + } + + directives = { + 'def': CommandNode, + } + + indices = { + CommandIndex, + TagIndex + } + + initial_data = { + 'objects': [], # object list + 'obj2tag': {}, # name -> object + } + + def get_full_qualified_name(self, node): + """Return full qualified name for a given node""" + return "{}.{}.{}".format('cmd', + 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) + for name, sig, typ, docname, anchor, prio + in self.get_objects() if sig == target] + + if len(match) > 0: + todocname = match[0][0] + targ = match[0][1] + + return make_refnode(builder,fromdocname,todocname, + targ, contnode, targ) + else: + print("Awww, found nothing") + return None + +def setup(app): + app.add_domain(CommandDomain) + + StandardDomain.initial_data['labels']['commandindex'] =\ + ('cmd-cmd', '', 'Command Reference') + StandardDomain.initial_data['labels']['tagindex'] =\ + ('cmd-tag', '', 'Tag Index') + + StandardDomain.initial_data['anonlabels']['commandindex'] =\ + ('cmd-cmd', '') + StandardDomain.initial_data['anonlabels']['tagindex'] =\ + ('cmd-tag', '') + + return {'version': '0.1'} diff --git a/kernel/register.cc b/kernel/register.cc index 9ffb17c1a..c9b9f238a 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -786,9 +786,8 @@ struct HelpPass : public Pass { fprintf(f, ".. raw:: latex\n\n \\begin{comment}\n\n"); // render html - fprintf(f, ":code:`yosys> help %s`\n", cmd.c_str()); - fprintf(f, "--------------------------------------------------------------------------------\n\n"); - fprintf(f, ".. container:: cmdref\n"); + fprintf(f, ".. cmd:def:: %s\n", cmd.c_str()); + fprintf(f, " :title: %s\n\n", title.c_str()); std::stringstream ss; std::string textcp = text; ss << text;