Skip to content

Commit 658a65e

Browse files
committed
Add pandoc BulletList & OrderedList classes
1 parent 01fdf10 commit 658a65e

File tree

1 file changed

+93
-1
lines changed

1 file changed

+93
-1
lines changed

quartodoc/pandoc/blocks.py

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
"""
44
from __future__ import annotations
55

6+
import itertools
67
import typing
7-
from typing import Optional, TypeAlias, Sequence
8+
from typing import Literal, Optional, TypeAlias, Sequence
89
import collections.abc as abc
910

1011
from textwrap import indent
@@ -16,10 +17,12 @@
1617
__all__ = (
1718
"Block",
1819
"Blocks",
20+
"BulletList",
1921
"CodeBlock",
2022
"DefinitionList",
2123
"Div",
2224
"Header",
25+
"OrderedList",
2326
"Para",
2427
"Plain",
2528
)
@@ -208,6 +211,38 @@ def __str__(self):
208211
return CodeBlock_TPL.format(content=content, attr=attr)
209212

210213

214+
@dataclass
215+
class BulletList(Block):
216+
"""
217+
A bullet list
218+
"""
219+
content: Optional[BlockContent] = None
220+
221+
def __str__(self):
222+
"""
223+
Return a bullet list as markdown
224+
"""
225+
if not self.content:
226+
return ""
227+
return blockcontent_to_str_items(self.content, "bullet")
228+
229+
230+
@dataclass
231+
class OrderedList(Block):
232+
"""
233+
An Ordered list
234+
"""
235+
content: Optional[BlockContent] = None
236+
237+
def __str__(self):
238+
"""
239+
Return an ordered list as markdown
240+
"""
241+
if not self.content:
242+
return ""
243+
return blockcontent_to_str_items(self.content, "ordered")
244+
245+
211246
# Helper functions
212247

213248
def join_block_content(content: Sequence[BlockContent]) -> str:
@@ -236,3 +271,60 @@ def blockcontent_to_str(content: Optional[BlockContent]) -> str:
236271
else:
237272
raise TypeError(f"Could not process type: {type(content)}")
238273

274+
275+
def blockcontent_to_str_items(
276+
content: Optional[BlockContent],
277+
kind: Literal["bullet", "ordered"]
278+
) -> str:
279+
"""
280+
Convert block content to strings of items
281+
282+
Parameters
283+
----------
284+
content:
285+
What to convert
286+
287+
kind:
288+
How to mark (prefix) each item in the of content.
289+
"""
290+
291+
def fmt(s:str, pfx: str):
292+
"""
293+
Format as a list item with one or more blocks
294+
"""
295+
# Aligns the content in all lines to start in the same column.
296+
# e.g. If pfx = "12.", we get output like
297+
#
298+
# 12. abcd
299+
# efgh
300+
#
301+
# ijkl
302+
# mnop
303+
if not s:
304+
return ""
305+
pad = " " * (len(pfx) + 1)
306+
return f"{pfx} " + indent(s, pad).lstrip(pad)
307+
308+
if not content:
309+
return ""
310+
311+
if kind == "bullet":
312+
pfx_it = itertools.cycle("*")
313+
else:
314+
pfx_it = (f"{i}." for i in itertools.count(1))
315+
316+
if isinstance(content, (str, Inline, Block)):
317+
return fmt(str(content), next(pfx_it))
318+
elif isinstance(content, abc.Sequence):
319+
# To balance correctness, compactness and readability,
320+
# items with content get an empty line between them and
321+
# the next item.
322+
items = []
323+
pad = ""
324+
for item in content:
325+
s = fmt(str(item), next(pfx_it))
326+
pad = f"{SEP}{SEP}" if isinstance(item, Block) else f"{SEP}"
327+
items.append(f"{s}{pad}")
328+
return "".join(items)[:-len(pad)]
329+
else:
330+
raise TypeError(f"Could not process type: {type(content)}")

0 commit comments

Comments
 (0)