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

1"""reStructuredText renderer for Marko. 

2 It is used to convert GitHub Flavored Markdown AST to reStructuredText format. 

3 

4 Author: Andreas Merkle (andreas.merkle@newtec.de) 

5""" 

6 

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/>. 

22 

23# Imports ********************************************************************** 

24 

25from __future__ import annotations 

26from typing import TYPE_CHECKING, Any, cast 

27from pyTRLCConverter.marko.md2rst_renderer import Md2RstRenderer 

28 

29if TYPE_CHECKING: 

30 from . import block, inline 

31 

32# Variables ******************************************************************** 

33 

34# Classes ********************************************************************** 

35 

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. 

42 

43 Converts GitHub Flavored Markdown to reStructuredText format. 

44 Inherits inline PlantUML handling from :class:`Md2RstRenderer`. 

45 """ 

46 

47 # Inherit all CommonMark rendering behavior from Md2RstRenderer and override only GFM additions. 

48 

49 def render_list_item(self, element: block.ListItem, marker="-") -> str: 

50 """ 

51 Renders a list item element. 

52 

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 "*". 

56 

57 Returns: 

58 str: The rendered list item as a string. 

59 """ 

60 indent = 2 

61 content = self.render_children(element) 

62 

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() 

72 

73 return f"{' ' * indent * (self._list_indent_level - 1)}{marker} {content}" 

74 

75 def render_table(self, element: Any) -> str: 

76 """Renders a GitHub Flavored Markdown table as a reStructuredText grid table. 

77 

78 Args: 

79 element (Any): The GFM table element. 

80 

81 Returns: 

82 str: The rendered grid table as a string. 

83 """ 

84 if not element.children: 

85 return "" 

86 

87 header_row = element.children[0] 

88 body_rows = element.children[1:] 

89 

90 header_values = [self.render_children(cell).strip() for cell in header_row.children] 

91 row_values = [] 

92 

93 for row in body_rows: 

94 row_values.append([self.render_children(cell).strip() for cell in row.children]) 

95 

96 return self._rst_create_grid_table(header_values, row_values) 

97 

98 def render_table_row(self, element: Any) -> str: 

99 """Renders a GitHub Flavored Markdown table row. 

100 

101 Args: 

102 element (Any): The table row element. 

103 

104 Returns: 

105 str: The rendered row as a string. 

106 """ 

107 return " | ".join(self.render_children(cell).strip() for cell in element.children) 

108 

109 def render_table_cell(self, element: Any) -> str: 

110 """Renders a GitHub Flavored Markdown table cell. 

111 

112 Args: 

113 element (Any): The table cell element. 

114 

115 Returns: 

116 str: The rendered table cell as a string. 

117 """ 

118 return self.render_children(element) 

119 

120 def render_strikethrough(self, element: Any) -> str: 

121 """Renders a GitHub Flavored Markdown strikethrough element. 

122 

123 Args: 

124 element (Any): The strikethrough element. 

125 

126 Returns: 

127 str: The rendered strikethrough text. 

128 """ 

129 return f"~~{self.render_children(element)}~~" 

130 

131 def render_url(self, element: Any) -> str: 

132 """Renders a GitHub Flavored Markdown URL element. 

133 

134 Args: 

135 element (Any): The URL element. 

136 

137 Returns: 

138 str: The rendered URL as an RST link. 

139 """ 

140 return self.render_link(cast("inline.Link", element)) 

141 

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. 

146 

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. 

150 

151 Returns: 

152 str: The rendered border line as a string. 

153 """ 

154 return "+" + "+".join(char * (width + 2) for width in max_widths) + "+\n" 

155 

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. 

160 

161 Args: 

162 values (list[str]): The values for the row. 

163 max_widths (list[int]): The maximum widths of the table columns. 

164 

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)] 

169 

170 return "|" + "|".join(padded) + "|\n" 

171 

172 @staticmethod 

173 def _rst_create_grid_table(headers: list[str], rows: list[list[str]]) -> str: 

174 """Create a reStructuredText grid table. 

175 

176 Args: 

177 headers (list[str]): Header row values. 

178 rows (list[list[str]]): Body row values. 

179 

180 Returns: 

181 str: The rendered grid table including trailing newline. 

182 """ 

183 if len(headers) == 0: 

184 return "" 

185 

186 max_widths = [len(value) for value in headers] 

187 

188 for row in rows: 

189 for index, value in enumerate(row): 

190 max_widths[index] = max(max_widths[index], len(value)) 

191 

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) 

196 

197 for row in rows: 

198 table += Gfm2RstRenderer._rst_table_append_row(row, max_widths) 

199 table += Gfm2RstRenderer._rst_table_append_border("-", max_widths) 

200 

201 table += "\n" 

202 

203 return table 

204 

205# Functions ******************************************************************** 

206 

207# Main *************************************************************************