Coverage for src/pyTRLCConverter/marko/gfm2rst_renderer.py: 93%
57 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-26 12:41 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-26 12:41 +0000
1"""reStructuredText renderer for Marko.
2 It is used to convert GitHub Flavored Markdown AST to reStructuredText format.
4 Author: Andreas Merkle (andreas.merkle@newtec.de)
5"""
7# pyTRLCConverter - A tool to convert TRLC files to specific formats.
8# Copyright (c) 2024 - 2026 NewTec GmbH
9#
10# This file is part of pyTRLCConverter program.
11#
12# The pyTRLCConverter program is free software: you can redistribute it and/or modify it under
13# the terms of the GNU General Public License as published by the Free Software Foundation,
14# either version 3 of the License, or (at your option) any later version.
15#
16# The pyTRLCConverter program is distributed in the hope that it will be useful, but
17# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License along with pyTRLCConverter.
21# If not, see <https://www.gnu.org/licenses/>.
23# Imports **********************************************************************
25from __future__ import annotations
26from typing import TYPE_CHECKING, Any, cast
27from pyTRLCConverter.marko.md2rst_renderer import Md2RstRenderer
29if TYPE_CHECKING:
30 from . import block, inline
32# Variables ********************************************************************
34# Classes **********************************************************************
36# pylint: disable-next=too-many-public-methods
37class Gfm2RstRenderer(Md2RstRenderer):
38 # lobster-trace: SwRequirements.sw_req_rst_render_gfm
39 # lobster-trace: SwRequirements.sw_req_rst_render_plantuml
40 # lobster-trace: SwRequirements.sw_req_plantuml
41 """Renderer for reStructuredText output.
43 Converts GitHub Flavored Markdown to reStructuredText format.
44 Inherits inline PlantUML handling from :class:`Md2RstRenderer`.
45 """
47 # Inherit all CommonMark rendering behavior from Md2RstRenderer and override only GFM additions.
49 def render_list_item(self, element: block.ListItem, marker="-") -> str:
50 """
51 Renders a list item element.
53 Args:
54 element (block.ListItem): The list item element to render.
55 marker (str, optional): The marker to use for the list item. Defaults to "*".
57 Returns:
58 str: The rendered list item as a string.
59 """
60 indent = 2
61 content = self.render_children(element)
63 # GitHub Flavored Markdown task-list items expose a "checked" flag in the paragraph node.
64 # Prefix the rendered item text to preserve the task state in reStructuredText output.
65 if 0 < len(element.children):
66 first_child = element.children[0]
67 checked = getattr(first_child, "checked", None)
68 if checked is True:
69 content = "[x] " + content.lstrip()
70 elif checked is False:
71 content = "[ ] " + content.lstrip()
73 return f"{' ' * indent * (self._list_indent_level - 1)}{marker} {content}"
75 def render_table(self, element: Any) -> str:
76 """Renders a GitHub Flavored Markdown table as a reStructuredText grid table.
78 Args:
79 element (Any): The GFM table element.
81 Returns:
82 str: The rendered grid table as a string.
83 """
84 if not element.children:
85 return ""
87 header_row = element.children[0]
88 body_rows = element.children[1:]
90 header_values = [self.render_children(cell).strip() for cell in header_row.children]
91 row_values = []
93 for row in body_rows:
94 row_values.append([self.render_children(cell).strip() for cell in row.children])
96 return self._rst_create_grid_table(header_values, row_values)
98 def render_table_row(self, element: Any) -> str:
99 """Renders a GitHub Flavored Markdown table row.
101 Args:
102 element (Any): The table row element.
104 Returns:
105 str: The rendered row as a string.
106 """
107 return " | ".join(self.render_children(cell).strip() for cell in element.children)
109 def render_table_cell(self, element: Any) -> str:
110 """Renders a GitHub Flavored Markdown table cell.
112 Args:
113 element (Any): The table cell element.
115 Returns:
116 str: The rendered table cell as a string.
117 """
118 return self.render_children(element)
120 def render_strikethrough(self, element: Any) -> str:
121 """Renders a GitHub Flavored Markdown strikethrough element.
123 Args:
124 element (Any): The strikethrough element.
126 Returns:
127 str: The rendered strikethrough text.
128 """
129 return f"~~{self.render_children(element)}~~"
131 def render_url(self, element: Any) -> str:
132 """Renders a GitHub Flavored Markdown URL element.
134 Args:
135 element (Any): The URL element.
137 Returns:
138 str: The rendered URL as an RST link.
139 """
140 return self.render_link(cast("inline.Link", element))
142 @staticmethod
143 def _rst_table_append_border(char: str, max_widths: list[int]) -> str:
144 """
145 Appends a table border line for the grid table.
147 Args:
148 char (str): The character to use for the border line.
149 max_widths (list[int]): The maximum widths of the table columns.
151 Returns:
152 str: The rendered border line as a string.
153 """
154 return "+" + "+".join(char * (width + 2) for width in max_widths) + "+\n"
156 @staticmethod
157 def _rst_table_append_row(values: list[str], max_widths: list[int]) -> str:
158 """
159 Appends a table row for the grid table.
161 Args:
162 values (list[str]): The values for the row.
163 max_widths (list[int]): The maximum widths of the table columns.
165 Returns:
166 str: The rendered row as a string.
167 """
168 padded = [f" {value.ljust(max_widths[index])} " for index, value in enumerate(values)]
170 return "|" + "|".join(padded) + "|\n"
172 @staticmethod
173 def _rst_create_grid_table(headers: list[str], rows: list[list[str]]) -> str:
174 """Create a reStructuredText grid table.
176 Args:
177 headers (list[str]): Header row values.
178 rows (list[list[str]]): Body row values.
180 Returns:
181 str: The rendered grid table including trailing newline.
182 """
183 if len(headers) == 0:
184 return ""
186 max_widths = [len(value) for value in headers]
188 for row in rows:
189 for index, value in enumerate(row):
190 max_widths[index] = max(max_widths[index], len(value))
192 table = ""
193 table += Gfm2RstRenderer._rst_table_append_border("-", max_widths)
194 table += Gfm2RstRenderer._rst_table_append_row(headers, max_widths)
195 table += Gfm2RstRenderer._rst_table_append_border("=", max_widths)
197 for row in rows:
198 table += Gfm2RstRenderer._rst_table_append_row(row, max_widths)
199 table += Gfm2RstRenderer._rst_table_append_border("-", max_widths)
201 table += "\n"
203 return table
205# Functions ********************************************************************
207# Main *************************************************************************