Skip to content

Commit

Permalink
Add support for verbatim inline elements
Browse files Browse the repository at this point in the history
This extends the current verbatim rST support with a new option
"embed:rst:inline", which can be used to generate an inline node,
instead of the paragraph node used by other verbatim blocks.

TODO: tests, error handling (what if there are multiple lines?)

Signed-off-by: Fabio Utzig <fabio.utzig@nordicsemi.no>
  • Loading branch information
utzig committed Aug 31, 2020
1 parent 67a3c4c commit 14631f8
Showing 1 changed file with 71 additions and 7 deletions.
78 changes: 71 additions & 7 deletions breathe/renderer/sphinxrenderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

from docutils import nodes
from docutils.nodes import Element, Node, TextElement # noqa
from docutils.statemachine import StringList
from docutils.statemachine import StringList, UnexpectedIndentationError
from docutils.parsers.rst.states import Text

try:
from sphinxcontrib import phpdomain as php # type: ignore
Expand Down Expand Up @@ -396,6 +397,49 @@ def get_definition_without_template_args(data_object):
return definition


class InlineText(Text):
"""
Add a custom docutils class to allow parsing inline text. This is to be
used inside a @verbatim/@endverbatim block but only the first line is
consumed and a inline element is generated as the parent, instead of the
paragraph used by Text.
"""
patterns = {'inlinetext': r''}
initial_transitions = [('inlinetext', )]

def indent(self, match, context, next_state):
"""
Avoid Text's indent from detecting space prefixed text and
doing "funny" stuff; always rely on inlinetext for parsing.
"""
return self.inlinetext(match, context, next_state)

def eof(self, context):
"""
Text.eof() inserts a paragraph, so override it to skip adding elements.
"""
return []

def inlinetext(self, match, context, next_state):
"""
Called by the StateMachine when an inline element is found (which is
any text when this class is added as the single transition.
"""
startline = self.state_machine.abs_line_number() - 1
msg = None
try:
block = self.state_machine.get_text_block()
except UnexpectedIndentationError as err:
block, src, srcline = err.args
msg = self.reporter.error('Unexpected indentation.',
source=src, line=srcline)
lines = context + list(block)
text, _ = self.inline_text(lines[0], startline)
self.parent += text
self.parent += msg
return [], next_state, []


class SphinxRenderer:
"""
Doxygen node visitor that converts input into Sphinx/RST representation.
Expand Down Expand Up @@ -1315,6 +1359,8 @@ def visit_verbatim(self, node) -> List[Node]:
# Handle has a preformatted text
return [nodes.literal_block(text, text)]

is_inline = False

# do we need to strip leading asterisks?
# NOTE: We could choose to guess this based on every line starting with '*'.
# However This would have a side-effect for any users who have an rst-block
Expand All @@ -1333,23 +1379,41 @@ def visit_verbatim(self, node) -> List[Node]:
lines = map(lambda text: text.replace("///", " ", 1), lines)
node.text = "\n".join(lines)

# Remove the first line which is "embed:rst[:leading-asterisk]"
text = "\n".join(node.text.split(u"\n")[1:])
elif node.text.strip().startswith("embed:rst:inline"):
node.text = node.text.replace("embed:rst:inline", "").splitlines()
is_inline = True

# Remove starting whitespace
text = textwrap.dedent(text)
if is_inline:
# Ignore all lines after the first
text = node.text[0]
else:
# Remove the first line which is "embed:rst[:leading-asterisk]"
text = "\n".join(node.text.split(u"\n")[1:])

# Remove starting whitespace
text = textwrap.dedent(text)

# Inspired by autodoc.py in Sphinx
rst = StringList()
for line in text.split("\n"):
rst.append(line, "<breathe>")

# Parent node for the generated node subtree
rst_node = nodes.paragraph()
if is_inline:
rst_node = nodes.inline()
else:
rst_node = nodes.paragraph()
rst_node.document = self.state.document

# Generate node subtree
nested_parse_with_titles(self.state, rst, rst_node)
if is_inline:
self.state.nested_parse(rst, 0, rst_node,
state_machine_kwargs={
'state_classes': (InlineText,),
'initial_state': 'InlineText'
})
else:
nested_parse_with_titles(self.state, rst, rst_node)
# TODO: hmm, what is supposed to be returned? something is strange with the types
return rst_node # type: ignore

Expand Down

0 comments on commit 14631f8

Please sign in to comment.