Coverage for src / pyTRLCConverter / marko / gfm2rst_renderer.py: 88%

57 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-02 12:20 +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 """ 

40 Renderer for reStructuredText output. 

41 It is used to convert GitHub Flavored Markdown to reStructuredText format. 

42 """ 

43 

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

45 

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

47 """ 

48 Renders a list item element. 

49 

50 Args: 

51 element (block.ListItem): The list item element to render. 

52 marker (str, optional): The marker to use for the list item. Defaults to "*". 

53 

54 Returns: 

55 str: The rendered list item as a string. 

56 """ 

57 indent = 2 

58 content = self.render_children(element) 

59 

60 # GitHub Flavored Markdown task-list items expose a "checked" flag in the paragraph node. 

61 # Prefix the rendered item text to preserve the task state in reStructuredText output. 

62 if 0 < len(element.children): 

63 first_child = element.children[0] 

64 checked = getattr(first_child, "checked", None) 

65 if checked is True: 

66 content = "[x] " + content.lstrip() 

67 elif checked is False: 

68 content = "[ ] " + content.lstrip() 

69 

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

71 

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

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

74 

75 Args: 

76 element (Any): The GFM table element. 

77 

78 Returns: 

79 str: The rendered grid table as a string. 

80 """ 

81 if not element.children: 

82 return "" 

83 

84 header_row = element.children[0] 

85 body_rows = element.children[1:] 

86 

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

88 row_values = [] 

89 

90 for row in body_rows: 

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

92 

93 return self._rst_create_grid_table(header_values, row_values) 

94 

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

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

97 

98 Args: 

99 element (Any): The table row element. 

100 

101 Returns: 

102 str: The rendered row as a string. 

103 """ 

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

105 

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

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

108 

109 Args: 

110 element (Any): The table cell element. 

111 

112 Returns: 

113 str: The rendered table cell as a string. 

114 """ 

115 return self.render_children(element) 

116 

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

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

119 

120 Args: 

121 element (Any): The strikethrough element. 

122 

123 Returns: 

124 str: The rendered strikethrough text. 

125 """ 

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

127 

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

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

130 

131 Args: 

132 element (Any): The URL element. 

133 

134 Returns: 

135 str: The rendered URL as an RST link. 

136 """ 

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

138 

139 @staticmethod 

140 def _rst_table_append_border(char: str, max_widths: list[int]) -> str: 

141 """ 

142 Appends a table border line for the grid table. 

143 

144 Args: 

145 char (str): The character to use for the border line. 

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

147 

148 Returns: 

149 str: The rendered border line as a string. 

150 """ 

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

152 

153 @staticmethod 

154 def _rst_table_append_row(values: list[str], max_widths: list[int]) -> str: 

155 """ 

156 Appends a table row for the grid table. 

157 

158 Args: 

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

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

161 

162 Returns: 

163 str: The rendered row as a string. 

164 """ 

165 padded = [f" {value.ljust(max_widths[index])} " for index, value in enumerate(values)] 

166 

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

168 

169 @staticmethod 

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

171 """Create a reStructuredText grid table. 

172 

173 Args: 

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

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

176 

177 Returns: 

178 str: The rendered grid table including trailing newline. 

179 """ 

180 if len(headers) == 0: 

181 return "" 

182 

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

184 

185 for row in rows: 

186 for index, value in enumerate(row): 

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

188 

189 table = "" 

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

191 table += Gfm2RstRenderer._rst_table_append_row(headers, max_widths) 

192 table += Gfm2RstRenderer._rst_table_append_border("=", max_widths) 

193 

194 for row in rows: 

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

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

197 

198 table += "\n" 

199 

200 return table 

201 

202# Functions ******************************************************************** 

203 

204# Main *************************************************************************