diff --git a/adi_doctools/directive/common.py b/adi_doctools/directive/common.py index c3b9bc8..1613b8b 100644 --- a/adi_doctools/directive/common.py +++ b/adi_doctools/directive/common.py @@ -4,13 +4,16 @@ from docutils.statemachine import ViewList from docutils.parsers.rst import Directive, directives from sphinx.util import logging +from sphinx.util.docutils import SphinxDirective +from sphinx.directives.code import container_wrapper import re +from os import path from uuid import uuid4 from hashlib import sha1 from typing import Tuple -from .node import node_div, node_input, node_label, node_icon, node_source, node_a +from .node import node_div, node_input, node_label, node_icon, node_source, node_a, node_pre from .node import node_iframe, node_video logger = logging.getLogger(__name__) @@ -115,18 +118,18 @@ def generic_table(self, description, uid: Optional[str]=None, media_print=False) rows = [] for key in description: row = nodes.row() - + entry = nodes.entry() if not media_print: # Check if not in PDF mode entry += nodes.literal(text="{:s}".format(key)) else: entry += nodes.paragraph(text="{:s}".format(key)) # Use paragraph for PDF mode row += entry - + entry = nodes.entry() entry += parse_rst(self.state, description[key], uid=uid) row += entry - + rows.append(row) tbody = nodes.tbody() @@ -314,12 +317,13 @@ class directive_clear_content(Directive): def run(self): side = self.options.get('side') - side = self.options.get('break') - if side not in ['left', 'right', 'both']: + values = ['left', 'right', 'both'] + if side not in values: if side is not None: - docname = self.state.document.current_source - logger.warning("clear directive option '%s' is invalid", - side, location=(docname, self.lineno)) + location = self.state_machine.get_source_and_line(self.lineno) + logger.warning(("clear-content option '%s' is invalid, valid ", + f"values are [{', '.join(values)}]."), + side, location=location) side = 'both' classes = [f"clear-{side}"] @@ -332,9 +336,140 @@ def run(self): return [node] +class directive_shell(SphinxDirective): + option_spec = { + 'user': directives.unchanged_required, + 'group': directives.unchanged_required, + 'caption': directives.unchanged_required, + 'showuser': directives.flag + } + has_content = True + final_argument_whitespace = True + + required_arguments = 0 + optional_arguments = 1 + + def run(self): + self.assert_has_content() + + shells = ['bash', 'sh', 'zsh', 'ps1'] + types = ['$', '/', '~', '#', ' '] + + user = self.options.get('user') + group = self.options.get('group') + caption = self.options.get('caption') + language = self.arguments[0].strip() if len(self.arguments) > 0 else None + if user is None: + user = "user" + if group is None: + group = "analog" + if language not in shells: + if language is not None: + location = self.state_machine.get_source_and_line(self.lineno) + logger.warning(("shell '%s' is invalid, valid values are " + f"[{', '.join(shells)}]."), + language, location=location) + language = "bash" + if 'showuser' in self.options: + if language != 'ps1': + wd_p = f"{user}@{group}:" + else: + wd_p = f"[{group}:{user}] " + else: + wd_p = "" + + def resolve_block(l_, l, wd, lang): + if l in ['/', '~']: + return None + + sep = '$' if lang != 'ps1' else '>' + lit = node_div() + if l in ['$', '#']: + if wd is not None: + sep = wd + sep + lit += nodes.literal_block(sep, sep, + classes=['no-select', 'float-left']) + + if l == '$': + lit_ = nodes.literal_block(l_, l_, classes=['bold']) + lit_['language'] = language + lit += lit_ + elif l == '#': + lit_ = nodes.literal_block('#'+l_,'#'+l_) + lit_['language'] = language + lit += lit_ + elif l == ' ': + lit += nodes.literal_block(l_, l_, classes=['no-select']) + else: + lit += nodes.literal_block(l_, l_, classes=['no-select']) + + lit += node_div( + classes = [f"clear-left"] + ) + return lit + + literals = node_div( + classes=['code-shell'] + ) + ll = None + if language == 'ps1': + wd = wd_ = '/C:/' + else: + wd = wd_ = '~' + block = [] + cd_flush = False + l__ = '' + for line in self.content: + if l__.startswith("cd "): + if language != 'ps1': + wd = path.abspath(path.join(wd, l__[3:])) + else: + l__ = l__.replace('\\', '/') + if l__[4] == ':': + wd = '/'+l__[3:] + else: + wd = path.abspath(path.join(path.sep, wd, l__[3:])) + if len(wd) > 1 and wd[-1] == "/": + wd = wd[:-1] + + l = line[0] if len(line) > 0 else ' ' + l_ = line[1:] if l in types else line + l__ = l_.strip() + + if language != 'ps1': + if l == '/': + wd = line + elif l == '~': + wd = line + if ((ll != l and ll is not None) or (ll == '$' and block[-1][-1] != '\\') or + ll == '#' and len(block) > 0): + wd__ = wd_ if language != 'ps1' else wd_.replace('/', '\\')[1:] + literals += resolve_block('\n'.join(block), ll, + wd_p+wd__, language) + if l not in ['/', '~']: + block = [l_] + wd_ = wd + else: + block.append(l_) + ll = l + literals += resolve_block('\n'.join(block), ll, wd, language) + + caption = self.options.get('caption') + if caption: + try: + literals = container_wrapper(self, literals, caption) + except ValueError as exc: + return [document.reporter.warning(exc, line=self.lineno)] + + self.add_name(literals) + + return [literals] + + def common_setup(app): app.add_directive('collapsible', directive_collapsible) app.add_directive('video', directive_video) app.add_directive('clear-content', directive_clear_content) + app.add_directive('shell', directive_shell) app.add_config_value('hide_collapsible_content', dft_hide_collapsible_content, 'env') diff --git a/adi_doctools/directive/node.py b/adi_doctools/directive/node.py index 18ec54c..11bf54b 100644 --- a/adi_doctools/directive/node.py +++ b/adi_doctools/directive/node.py @@ -61,8 +61,12 @@ class node_a(node_base): tagname = 'a' endtag = 'true' +class node_pre(node_base): + tagname = 'pre' + endtag = 'true' + def node_setup(app): - for node in [node_div, node_input, node_label, node_icon, node_video, node_source, node_iframe, node_a]: + for node in [node_div, node_input, node_label, node_icon, node_video, node_source, node_iframe, node_a, node_pre]: app.add_node(node, html =(node.visit, node.depart), latex=(node.visit, node.depart), diff --git a/adi_doctools/theme/cosmic/style/code.scss b/adi_doctools/theme/cosmic/style/code.scss index 3258d62..00776fc 100644 --- a/adi_doctools/theme/cosmic/style/code.scss +++ b/adi_doctools/theme/cosmic/style/code.scss @@ -33,6 +33,49 @@ em.sig-param, em.property { background-color: rgba(125, 125, 125, 0.1); } -.sig-object:hover .headerlink { +.sig-object:hover .headerlink, .literal-block-wrapper:hover .headerlink { opacity: 1; } + +.code-block-caption { + text-align: center; + padding-bottom: .25em; +} + +// TODO Port to third-party themes +.code-shell { + background-color: var(--bg-color3); + border-radius: $border-radius; + padding: .75em; + margin-bottom: .5em; + user-select: text; + + .highlight { + padding: 0; + white-space: collapse; + margin: 0; + background: none !important; + overflow: visible; + } + + .float-left { + float: left; + clear: left; + display: inline-block; + margin-right: .25em; + } + + .bold { + font-weight: bold; + } + + pre { + margin: 0; + } + + .no-select { + opacity: 0.8; + user-select: none; + } +} + diff --git a/adi_doctools/theme/cosmic/style/element.scss b/adi_doctools/theme/cosmic/style/element.scss index a973dbc..9c63ce2 100644 --- a/adi_doctools/theme/cosmic/style/element.scss +++ b/adi_doctools/theme/cosmic/style/element.scss @@ -13,7 +13,7 @@ video { max-width: 100%; } -section, #top-anchor, aside, a { +section, #top-anchor, aside, a, .literal-block-wrapper { scroll-margin-top: 3.5rem; } diff --git a/adi_doctools/theme/cosmic/style/print.scss b/adi_doctools/theme/cosmic/style/print.scss index 9265ed8..42779dc 100644 --- a/adi_doctools/theme/cosmic/style/print.scss +++ b/adi_doctools/theme/cosmic/style/print.scss @@ -22,6 +22,15 @@ border: 1px solid var(--text-color3); } + .code-shell { + background: none; + border: 1px solid var(--text-color3); + + .highlight { + border: none; + } + } + .body { padding: 0; } diff --git a/adi_doctools/theme/cosmic/style/style.scss b/adi_doctools/theme/cosmic/style/style.scss index ce1ee69..b20e49c 100644 --- a/adi_doctools/theme/cosmic/style/style.scss +++ b/adi_doctools/theme/cosmic/style/style.scss @@ -30,6 +30,7 @@ body { --display-light: inline-block; --bg-color1: #{$bg-light1}; --bg-color2: #{$bg-light2}; + --bg-color3: #{$bg-light3}; --bg-color1-faded1: #{$bg-light1}99; --bg-color1-faded2: #{$bg-light1}aa; --text-color1: #{$text-color-light1}; @@ -48,6 +49,7 @@ body.dark { --display-light: none; --bg-color1: #{$bg-dark1}; --bg-color2: #{$bg-dark2}; + --bg-color3: #{$bg-dark3}; --bg-color1-faded1: #{$bg-dark1}aa; --bg-color1-faded2: #{$bg-dark1}cc; --text-color1: #{$text-color-dark1}; @@ -67,6 +69,7 @@ body.dark { --display-light: none; --bg-color1: #{$bg-dark1}; --bg-color2: #{$bg-dark2}; + --bg-color3: #{$bg-dark3}; --bg-color1-faded1: #{$bg-dark1}99; --bg-color1-faded2: #{$bg-dark1}aa; --text-color1: #{$text-color-dark1}; @@ -159,10 +162,20 @@ body { } } -.body p { - line-height: 1.5em; - text-align: justify; - margin: .25em 0 .75em 0; +.body { + p, div.line { + line-height: 1.5em; + text-align: justify; + margin: .25em 0 .75em 0; + } + + p { + margin: .25em 0 .75em 0; + } + + div.line{ + margin: .25em 0; + } } .body ol p, .body ul p { @@ -179,11 +192,11 @@ svg { line-height: 1.5em; } -@media (min-width: $screen-1) { +@media print, (min-width: $screen-1) { #hdl-component-diagram svg { float: right; margin: -3em 0 0 1em; - max-width: 30rem; + max-width: 45%; } } diff --git a/adi_doctools/theme/cosmic/style/variable.scss b/adi_doctools/theme/cosmic/style/variable.scss index d5aee1a..bc4ef53 100644 --- a/adi_doctools/theme/cosmic/style/variable.scss +++ b/adi_doctools/theme/cosmic/style/variable.scss @@ -1,7 +1,9 @@ $bg-light1: #f9f9f9; $bg-light2: #f4f4f4; +$bg-light3: #f0f0f0; $bg-dark1: #1a1a1a; $bg-dark2: #111; +$bg-dark3: #202020; $bg-banner-dark: #0088ff; $bg-banner-light: #00305b; $text-color-dark1: #e5e5e5; diff --git a/docs/docs_guidelines.rst b/docs/docs_guidelines.rst index 5e3d411..b3142a8 100644 --- a/docs/docs_guidelines.rst +++ b/docs/docs_guidelines.rst @@ -150,16 +150,16 @@ if the target directory is: Exporting to PDF -------------------------------------------------------------------------------- -The whole documentation can be exported to a PDF document for a more compact -format. This is done by setting the enviroment variable called -``ADOC_MEDIA_PRINT`` to 1 (default it's 0) and building the documentation using +The whole documentation can be exported to a PDF document for a more compact +format. This is done by setting the environment variable called +``ADOC_MEDIA_PRINT`` to 1 (default it's 0) and building the documentation using this command: .. code-block:: user@analog:~/doctools/docs$ sphinx-build -b pdf . _build/pdfbuild -In the output folder, you’ll find a PDF document named after the repository +In the output folder, you’ll find a PDF document named after the repository (e.g. Doctools.pdf). This document includes an auto-generated cover, followed by the remaining pages. Note that an HTML build of the documentation is not required for the PDF build. @@ -168,53 +168,75 @@ required for the PDF build. The enviroment variable ``ADOC_MEDIA_PRINT`` should be set to 0 when building the HTML pages of documentation. If not set, some components of the pages - may not render properly. + may not render properly. -References +.. _local_refs: + +Local references -------------------------------------------------------------------------------- -References have the format ``library/project context``, e.g. -:code:`:ref:\`vivado block-diagrams\`` renders as :ref:`vivado block-diagrams`. -Notice how neither *library* nor *project* are present in the label, since there is no -naming collision between libraries or projects (no project will ever be named -*axi_dmac*). +References to labels have the format :code:`:ref:\`context topic\``, e.g. +:code:`:ref:\`role git\`` renders as :ref:`role git`. + +Labels are created for any content with the syntax +(dot-dot underscore