Skip to content

Commit

Permalink
Add shell directive
Browse files Browse the repository at this point in the history
Standarize how we provide shell snippets,
supports multiple shells, including PowerShell (ps1) for windows
snippets.

Signed-off-by: Jorge Marques <[email protected]>
  • Loading branch information
gastmaier committed Oct 1, 2024
1 parent ab0b4b6 commit 8d4347f
Show file tree
Hide file tree
Showing 8 changed files with 433 additions and 76 deletions.
153 changes: 144 additions & 9 deletions adi_doctools/directive/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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}"]
Expand All @@ -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')
6 changes: 5 additions & 1 deletion adi_doctools/directive/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
45 changes: 44 additions & 1 deletion adi_doctools/theme/cosmic/style/code.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

2 changes: 1 addition & 1 deletion adi_doctools/theme/cosmic/style/element.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
9 changes: 9 additions & 0 deletions adi_doctools/theme/cosmic/style/print.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
25 changes: 19 additions & 6 deletions adi_doctools/theme/cosmic/style/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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};
Expand All @@ -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};
Expand Down Expand Up @@ -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 {
Expand All @@ -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%;
}
}

Expand Down
2 changes: 2 additions & 0 deletions adi_doctools/theme/cosmic/style/variable.scss
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading

0 comments on commit 8d4347f

Please sign in to comment.