|
4 | 4 |
|
5 | 5 | from ..core import BaseRenderer, BlockState |
6 | 6 | from ..util import strip_end |
7 | | -from ._list import render_list |
| 7 | +from ._list import render_list, render_list_item |
8 | 8 |
|
9 | 9 | fenced_re = re.compile(r"^[`~]+", re.M) |
10 | 10 |
|
@@ -134,6 +134,45 @@ def block_error(self, token: Dict[str, Any], state: BlockState) -> str: |
134 | 134 | def list(self, token: Dict[str, Any], state: BlockState) -> str: |
135 | 135 | return render_list(self, token, state) |
136 | 136 |
|
| 137 | + def list_item(self, token: Dict[str, Any], state: BlockState) -> str: |
| 138 | + return render_list_item(self, token, state) |
| 139 | + |
| 140 | + def task_list_item(self, token: Dict[str, Any], state: BlockState) -> str: |
| 141 | + checked = token.get("attrs", {}).get("checked") |
| 142 | + marker = "[x] " if checked else "[ ] " |
| 143 | + return render_list_item(self, token, state, marker) |
| 144 | + |
| 145 | + def table(self, token: Dict[str, Any], state: BlockState) -> str: |
| 146 | + children = token.get("children", []) |
| 147 | + if not children: |
| 148 | + return "\n" |
| 149 | + |
| 150 | + head = children[0] |
| 151 | + body = children[1] if len(children) > 1 else None |
| 152 | + head_cells = head.get("children", []) |
| 153 | + align = [_table_cell_align(cell) for cell in head_cells] |
| 154 | + lines = [ |
| 155 | + _render_table_row(self, head_cells, state), |
| 156 | + _render_table_delimiter(align), |
| 157 | + ] |
| 158 | + if body: |
| 159 | + for row in body.get("children", []): |
| 160 | + lines.append(_render_table_row(self, row.get("children", []), state)) |
| 161 | + return "\n".join(lines) + "\n\n" |
| 162 | + |
| 163 | + def table_head(self, token: Dict[str, Any], state: BlockState) -> str: |
| 164 | + cells = token.get("children", []) |
| 165 | + return _render_table_row(self, cells, state) + "\n" + _render_table_delimiter([_table_cell_align(c) for c in cells]) |
| 166 | + |
| 167 | + def table_body(self, token: Dict[str, Any], state: BlockState) -> str: |
| 168 | + return "\n".join(self.render_token(row, state).rstrip("\n") for row in token.get("children", [])) |
| 169 | + |
| 170 | + def table_row(self, token: Dict[str, Any], state: BlockState) -> str: |
| 171 | + return _render_table_row(self, token.get("children", []), state) + "\n" |
| 172 | + |
| 173 | + def table_cell(self, token: Dict[str, Any], state: BlockState) -> str: |
| 174 | + return _render_table_cell(self, token, state) |
| 175 | + |
137 | 176 |
|
138 | 177 | def _escape_block_prefix(text: str) -> str: |
139 | 178 | """Backslash-escape a leading block marker on each line so that literal |
@@ -168,3 +207,33 @@ def _get_fenced_marker(code: str) -> str: |
168 | 207 | if not waves: |
169 | 208 | return "~~~" |
170 | 209 | return "`" * (max(ticks) + 1) |
| 210 | + |
| 211 | + |
| 212 | +def _render_table_row(renderer: MarkdownRenderer, cells: Iterable[Dict[str, Any]], state: BlockState) -> str: |
| 213 | + return "| " + " | ".join(_render_table_cell(renderer, cell, state) for cell in cells) + " |" |
| 214 | + |
| 215 | + |
| 216 | +def _render_table_delimiter(aligns: Iterable[Any]) -> str: |
| 217 | + cells = [] |
| 218 | + for align in aligns: |
| 219 | + if align == "left": |
| 220 | + cells.append(":---") |
| 221 | + elif align == "center": |
| 222 | + cells.append(":---:") |
| 223 | + elif align == "right": |
| 224 | + cells.append("---:") |
| 225 | + else: |
| 226 | + cells.append("---") |
| 227 | + return "| " + " | ".join(cells) + " |" |
| 228 | + |
| 229 | + |
| 230 | +def _render_table_cell(renderer: MarkdownRenderer, token: Dict[str, Any], state: BlockState) -> str: |
| 231 | + if "children" in token: |
| 232 | + text = renderer.render_children(token, state) |
| 233 | + else: |
| 234 | + text = cast(str, token.get("raw", "")) |
| 235 | + return text.replace("\n", " ").replace("|", "\\|").strip() |
| 236 | + |
| 237 | + |
| 238 | +def _table_cell_align(token: Dict[str, Any]) -> Any: |
| 239 | + return token.get("attrs", {}).get("align") |
0 commit comments