Coverage for src / pyTRLCConverter / marko / rst_renderer.py: 75%
79 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-21 12:06 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-21 12:06 +0000
1"""reStructuredText renderer for Marko.
3 Author: Andreas Merkle (andreas.merkle@newtec.de)
4"""
6# pyTRLCConverter - A tool to convert TRLC files to specific formats.
7# Copyright (c) 2024 - 2025 NewTec GmbH
8#
9# This file is part of pyTRLCConverter program.
10#
11# The pyTRLCConverter program is free software: you can redistribute it and/or modify it under
12# the terms of the GNU General Public License as published by the Free Software Foundation,
13# either version 3 of the License, or (at your option) any later version.
14#
15# The pyTRLCConverter program is distributed in the hope that it will be useful, but
16# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License along with pyTRLCConverter.
20# If not, see <https://www.gnu.org/licenses/>.
22# Imports **********************************************************************
24from __future__ import annotations
25from typing import TYPE_CHECKING, Any, cast
26from marko import Renderer
28if TYPE_CHECKING:
29 from . import block, inline
31# Variables ********************************************************************
33# Classes **********************************************************************
35# pylint: disable-next=too-many-public-methods
36class RSTRenderer(Renderer):
37 # lobster-trace: SwRequirements.sw_req_rst_render_md
38 """Renderer for reStructuredText output."""
40 def __init__(self) -> None:
41 """
42 Initializes the RSTRenderer.
43 """
44 super().__init__()
45 self._list_indent_level = 0
47 def render_paragraph(self, element: block.Paragraph) -> str:
48 """
49 Renders a paragraph element.
51 Args:
52 element (block.Paragraph): The paragraph element to render.
54 Returns:
55 str: The rendered paragraph as a string.
56 """
57 return self.render_children(element) + "\n\n"
59 def render_list(self, element: block.List) -> str:
60 """
61 Renders a list (ordered or unordered) element.
63 Args:
64 element (block.List): The list element to render.
66 Returns:
67 str: The rendered list as a string.
68 """
69 items = []
71 self._list_indent_level += 1
73 for index, child in enumerate(element.children):
74 marker = f"{index + 1}." if element.ordered else "-"
75 item = self.render_list_item(child, marker)
76 items.append(item)
78 self._list_indent_level -= 1
80 return "\n".join(items) + "\n"
82 def render_list_item(self, element: block.ListItem, marker="-") -> str:
83 """
84 Renders a list item element.
86 Args:
87 element (block.ListItem): The list item element to render.
88 marker (str, optional): The marker to use for the list item. Defaults to "*".
90 Returns:
91 str: The rendered list item as a string.
92 """
93 indent = 2
94 content = self.render_children(element)
96 return f"{' ' * indent * (self._list_indent_level - 1)}{marker} {content}"
98 def render_quote(self, element: block.Quote) -> str:
99 """
100 Renders a blockquote element.
102 Args:
103 element (block.Quote): The blockquote element to render.
105 Returns:
106 str: The rendered blockquote as a string.
107 """
108 quote = self.render_children(element)
109 quoted = "\n".join([f" {line}" if line.strip() else "" for line in quote.splitlines()])
111 return quoted + "\n\n"
113 def render_fenced_code(self, element: block.FencedCode) -> str:
114 """
115 Renders a fenced code block element.
117 Args:
118 element (block.FencedCode): The fenced code block element to render.
120 Returns:
121 str: The rendered fenced code block as a string.
122 """
123 lang = element.lang or ""
124 code = element.children[0].children # type: ignore
126 return f".. code-block:: {lang}\n\n " + "\n ".join(code.splitlines()) + "\n\n"
128 def render_code_block(self, element: block.CodeBlock) -> str:
129 """
130 Renders a code block element.
132 Args:
133 element (block.CodeBlock): The code block element to render.
135 Returns:
136 str: The rendered code block as a string.
137 """
138 return self.render_fenced_code(cast("block.FencedCode", element))
140 def render_html_block(self, element: block.HTMLBlock) -> str:
141 """
142 Renders a raw HTML block element.
144 Args:
145 element (block.HTMLBlock): The HTML block element to render.
147 Returns:
148 str: The rendered HTML block as a string.
149 """
150 # reStructuredText does not support raw HTML, so output as a literal block.
151 body = element.body
153 return "::\n\n " + "\n ".join(body.splitlines()) + "\n\n"
155 # pylint: disable-next=unused-argument
156 def render_thematic_break(self, element: block.ThematicBreak) -> str:
157 """
158 Renders a thematic break (horizontal rule) element.
160 Args:
161 element (block.ThematicBreak): The thematic break element to render.
163 Returns:
164 str: The rendered thematic break as a string.
165 """
166 return "\n----\n\n"
168 def render_heading(self, element: block.Heading) -> str:
169 """
170 Renders a heading element.
172 Args:
173 element (block.Heading): The heading element to render.
175 Returns:
176 str: The rendered heading as a string.
177 """
178 text = self.render_children(element)
179 underline = {
180 1: "=",
181 2: "-",
182 3: "~",
183 4: "^",
184 5: '"',
185 6: "'"
186 }.get(element.level, "-")
188 return f"{text}\n{underline * len(text)}\n\n"
190 def render_setext_heading(self, element: block.SetextHeading) -> str:
191 """
192 Renders a setext heading element.
194 Args:
195 element (block.SetextHeading): The setext heading element to render.
197 Returns:
198 str: The rendered setext heading as a string.
199 """
200 return self.render_heading(cast("block.Heading", element))
202 # pylint: disable-next=unused-argument
203 def render_blank_line(self, element: block.BlankLine) -> str:
204 """
205 Renders a blank line element.
207 Args:
208 element (block.BlankLine): The blank line element to render.
210 Returns:
211 str: The rendered blank line as a string.
212 """
213 return "\n"
215 # pylint: disable-next=unused-argument
216 def render_link_ref_def(self, element: block.LinkRefDef) -> str:
217 """
218 Renders a link reference definition element.
220 Args:
221 element (block.LinkRefDef): The link reference definition element to render.
223 Returns:
224 str: The rendered link reference definition as a string.
225 """
226 # reStructuredText uses reference links differently than Markdown.
227 # It shall not be rendered in the document.
228 return ""
230 def render_emphasis(self, element: inline.Emphasis) -> str:
231 """
232 Renders an emphasis (italic) element.
234 Args:
235 element (inline.Emphasis): The emphasis element to render.
237 Returns:
238 str: The rendered emphasis as a string.
239 """
240 return f"*{self.render_children(element)}*"
242 def render_strong_emphasis(self, element: inline.StrongEmphasis) -> str:
243 """
244 Renders a strong emphasis (bold) element.
246 Args:
247 element (inline.StrongEmphasis): The strong emphasis element to render.
249 Returns:
250 str: The rendered strong emphasis as a string.
251 """
252 return f"**{self.render_children(element)}**"
254 def render_inline_html(self, element: inline.InlineHTML) -> str:
255 """
256 Renders an inline HTML element.
258 Args:
259 element (inline.InlineHTML): The inline HTML element to render.
261 Returns:
262 str: The rendered inline HTML as a string.
263 """
264 # Output as literal block.
265 html_content = cast(str, element.children)
266 return f"``{html_content}``"
268 def render_plain_text(self, element: Any) -> str:
269 """
270 Renders plain text or any element with string children.
272 Args:
273 element (Any): The element to render.
275 Returns:
276 str: The rendered plain text as a string.
277 """
278 if isinstance(element.children, str):
279 return element.children
281 return self.render_children(element)
283 def render_link(self, element: inline.Link) -> str:
284 """
285 Renders a link element.
287 Args:
288 element (inline.Link): The link element to render.
290 Returns:
291 str: The rendered link as a string.
292 """
293 body = self.render_children(element)
294 url = element.dest
295 title = f" ({element.title})" if element.title else ""
297 return f"`{body} <{url}>`_{title}"
299 def render_auto_link(self, element: inline.AutoLink) -> str:
300 """
301 Renders an auto link element.
303 Args:
304 element (inline.AutoLink): The auto link element to render.
306 Returns:
307 str: The rendered auto link as a string.
308 """
309 return self.render_link(cast("inline.Link", element))
311 def render_image(self, element: inline.Image) -> str:
312 """
313 Renders an image element.
315 Args:
316 element (inline.Image): The image element to render.
318 Returns:
319 str: The rendered image as a string.
320 """
321 url = element.dest
322 alt = self.render_children(element)
323 title = f" :alt: {alt}" if alt else ""
324 extra_title = f" :title: {element.title}" if element.title else ""
326 return f".. image:: {url}\n{title}\n{extra_title}\n"
328 def render_literal(self, element: inline.Literal) -> str:
329 """
330 Renders a literal (inline code) element.
332 Args:
333 element (inline.Literal): The literal element to render.
335 Returns:
336 str: The rendered literal as a string.
337 """
338 return self.render_raw_text(cast("inline.RawText", element))
340 def render_raw_text(self, element: inline.RawText) -> str:
341 """
342 Renders a raw text element.
344 Args:
345 element (inline.RawText): The raw text element to render.
347 Returns:
348 str: The rendered raw text as a string.
349 """
350 return f"{element.children}"
352 # pylint: disable-next=unused-argument
353 def render_line_break(self, element: inline.LineBreak) -> str:
354 """
355 Renders a line break element.
357 Args:
358 element (inline.LineBreak): The line break element to render.
360 Returns:
361 str: The rendered line break as a string.
362 """
363 return "\n"
365 def render_code_span(self, element: inline.CodeSpan) -> str:
366 """
367 Renders a code span (inline code) element.
369 Args:
370 element (inline.CodeSpan): The code span element to render.
372 Returns:
373 str: The rendered code span as a string.
374 """
375 return f"``{cast(str, element.children)}``"
377# Functions ********************************************************************
379# Main *************************************************************************