feat(markdown): added presentation metadata
This commit is contained in:
parent
bcb3263149
commit
87c8e9e8cf
|
@ -15,5 +15,9 @@ def main(argv: list[str]) -> NoReturn:
|
|||
print(f"{file} does not exist", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
MarkdownParser(file.read_text())
|
||||
slides = MarkdownParser(file.read_text()).parse()
|
||||
|
||||
from pprint import pprint
|
||||
pprint(slides)
|
||||
|
||||
exit(0)
|
||||
|
|
|
@ -11,19 +11,23 @@ Char = TypeVar("Char", str, bytes)
|
|||
|
||||
class MarkdownElement: ...
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkdownText(MarkdownElement):
|
||||
content: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkdownComment(MarkdownElement):
|
||||
content: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkdownHeader(MarkdownElement):
|
||||
level: Literal[1, 2, 3, 4, 5]
|
||||
content: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkdownListElement(MarkdownElement):
|
||||
class ListType(StrEnum):
|
||||
|
@ -34,23 +38,35 @@ class MarkdownListElement(MarkdownElement):
|
|||
content: str
|
||||
index: Optional[int]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkdownCodeBlock(MarkdownElement):
|
||||
lang: str
|
||||
content: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkdownPreprocessor(MarkdownElement):
|
||||
program: str
|
||||
content: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkdownSlide(MarkdownElement):
|
||||
elements: list[MarkdownElement]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkdownNewLine(MarkdownElement):
|
||||
...
|
||||
class MarkdownNewLine(MarkdownElement): ...
|
||||
|
||||
|
||||
# --- Presentation class ----------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class Presentation:
|
||||
slides: list[MarkdownSlide]
|
||||
metadata: dict[str, str]
|
||||
|
||||
|
||||
# --- Markdown parser ----------------------------------------------------------
|
||||
|
@ -89,7 +105,7 @@ class MarkdownParser:
|
|||
if char == c or char is None:
|
||||
break
|
||||
content += char
|
||||
|
||||
|
||||
return content
|
||||
|
||||
def eat_line(self) -> str:
|
||||
|
@ -98,13 +114,13 @@ class MarkdownParser:
|
|||
def eat_until_pattern(self, pattern: str) -> str:
|
||||
content = ""
|
||||
while char := self.next():
|
||||
if char is None:
|
||||
if char is None:
|
||||
break
|
||||
if self.text[self.offset:].startswith(pattern):
|
||||
if self.text[self.offset :].startswith(pattern):
|
||||
self.offset += len(pattern)
|
||||
break
|
||||
content += char
|
||||
|
||||
|
||||
return content
|
||||
|
||||
def eat_number(self) -> int:
|
||||
|
@ -118,8 +134,9 @@ class MarkdownParser:
|
|||
|
||||
return int(nbr)
|
||||
|
||||
def parse(self) -> list[MarkdownSlide]:
|
||||
def parse(self) -> Presentation:
|
||||
slides: list[MarkdownSlide] = []
|
||||
metadata: dict[str, str] = {}
|
||||
current_slide: list[MarkdownElement] = []
|
||||
|
||||
while c := self.peek():
|
||||
|
@ -133,16 +150,32 @@ class MarkdownParser:
|
|||
case "-":
|
||||
count = self.eat_count("-")
|
||||
if count == 1:
|
||||
self.eat(' ')
|
||||
self.eat(" ")
|
||||
content = self.eat_line()
|
||||
current_slide.append(MarkdownListElement(MarkdownListElement.ListType.UNORDERED, content, None))
|
||||
current_slide.append(
|
||||
MarkdownListElement(
|
||||
MarkdownListElement.ListType.UNORDERED, content, None
|
||||
)
|
||||
)
|
||||
elif count == 3:
|
||||
self.eat("\n")
|
||||
slides.append(MarkdownSlide(current_slide.copy()))
|
||||
current_slide.clear()
|
||||
continue
|
||||
if len(slides) == 0 and len(current_slide) == 0:
|
||||
raw_metadata = self.eat_until_pattern("---").split("\n")
|
||||
metadata = {
|
||||
line.split(":")[0].strip(): line.split(":")[1].strip()
|
||||
for line in raw_metadata
|
||||
if line
|
||||
}
|
||||
self.eat("\n")
|
||||
continue
|
||||
else:
|
||||
slides.append(MarkdownSlide(current_slide.copy()))
|
||||
current_slide.clear()
|
||||
continue
|
||||
else:
|
||||
current_slide.append(MarkdownText("-" * count + self.eat_line()))
|
||||
current_slide.append(
|
||||
MarkdownText("-" * count + self.eat_line())
|
||||
)
|
||||
|
||||
case "`":
|
||||
count = self.eat_count("`")
|
||||
|
@ -151,17 +184,25 @@ class MarkdownParser:
|
|||
content = self.eat_until_pattern("```")
|
||||
current_slide.append(MarkdownCodeBlock(lang, content.strip()))
|
||||
else:
|
||||
current_slide.append(MarkdownText("`" * count + self.eat_line()))
|
||||
current_slide.append(
|
||||
MarkdownText("`" * count + self.eat_line())
|
||||
)
|
||||
|
||||
case "*":
|
||||
count = self.eat_count("*")
|
||||
|
||||
if count == 1 and self.peek() == " ":
|
||||
self.eat(' ')
|
||||
self.eat(" ")
|
||||
content = self.eat_line()
|
||||
current_slide.append(MarkdownListElement(MarkdownListElement.ListType.UNORDERED, content, None))
|
||||
current_slide.append(
|
||||
MarkdownListElement(
|
||||
MarkdownListElement.ListType.UNORDERED, content, None
|
||||
)
|
||||
)
|
||||
else:
|
||||
current_slide.append(MarkdownText("*" * count + self.eat_line()))
|
||||
current_slide.append(
|
||||
MarkdownText("*" * count + self.eat_line())
|
||||
)
|
||||
|
||||
case "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9":
|
||||
index = self.eat_number()
|
||||
|
@ -169,12 +210,16 @@ class MarkdownParser:
|
|||
self.eat(".")
|
||||
self.eat(" ")
|
||||
content = self.eat_line()
|
||||
current_slide.append(MarkdownListElement(MarkdownListElement.ListType.ORDERED, content, index))
|
||||
current_slide.append(
|
||||
MarkdownListElement(
|
||||
MarkdownListElement.ListType.ORDERED, content, index
|
||||
)
|
||||
)
|
||||
else:
|
||||
current_slide.append(MarkdownText(str(count) + self.eat_line()))
|
||||
|
||||
case '<':
|
||||
if self.text[self.offset:].startswith("<!--"):
|
||||
case "<":
|
||||
if self.text[self.offset :].startswith("<!--"):
|
||||
self.offset += 4
|
||||
self.eat(" ")
|
||||
content = self.eat_until_pattern("-->")
|
||||
|
@ -182,21 +227,23 @@ class MarkdownParser:
|
|||
else:
|
||||
current_slide.append(MarkdownText(self.eat_line()))
|
||||
|
||||
case '~':
|
||||
case "~":
|
||||
count = self.eat_count("~")
|
||||
if count == 3:
|
||||
program = self.eat_line()
|
||||
content = self.eat_until_pattern("~~~")
|
||||
current_slide.append(MarkdownPreprocessor(program, content))
|
||||
else:
|
||||
current_slide.append(MarkdownText("~" * count + self.eat_line()))
|
||||
current_slide.append(
|
||||
MarkdownText("~" * count + self.eat_line())
|
||||
)
|
||||
|
||||
case "\n":
|
||||
self.eat("\n")
|
||||
current_slide.append(MarkdownNewLine())
|
||||
|
||||
case _:
|
||||
current_slide.append(MarkdownText(self.eat_line()))
|
||||
|
||||
case _:
|
||||
current_slide.append(MarkdownText(self.eat_line()))
|
||||
|
||||
slides.append(MarkdownSlide(current_slide))
|
||||
return slides
|
||||
return Presentation(slides, metadata)
|
||||
|
|
|
@ -2,131 +2,281 @@ import unittest
|
|||
from cuteslides import markdown
|
||||
from inspect import cleandoc
|
||||
|
||||
|
||||
class TestMarkdown(unittest.TestCase):
|
||||
maxDiff = None
|
||||
|
||||
def test_paragraphs(self):
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser("Hello, World!").parse(),
|
||||
markdown.MarkdownParser("Hello, World!").parse().slides,
|
||||
[markdown.MarkdownSlide([markdown.MarkdownText("Hello, World!")])],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser("Hello,\nWorld!").parse(),
|
||||
[markdown.MarkdownSlide([markdown.MarkdownText("Hello,"), markdown.MarkdownText("World!")])],
|
||||
markdown.MarkdownParser("Hello,\nWorld!").parse().slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[markdown.MarkdownText("Hello,"), markdown.MarkdownText("World!")]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser("Hello,\n\nWorld!").parse(),
|
||||
[markdown.MarkdownSlide([markdown.MarkdownText("Hello,"), markdown.MarkdownNewLine(), markdown.MarkdownText("World!")])],
|
||||
markdown.MarkdownParser("Hello,\n\nWorld!").parse().slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownText("Hello,"),
|
||||
markdown.MarkdownNewLine(),
|
||||
markdown.MarkdownText("World!"),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def test_headers(self):
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser("# Hello, World!").parse(),
|
||||
markdown.MarkdownParser("# Hello, World!").parse().slides,
|
||||
[markdown.MarkdownSlide([markdown.MarkdownHeader(1, "Hello, World!")])],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser("## Hello, World!").parse(),
|
||||
markdown.MarkdownParser("## Hello, World!").parse().slides,
|
||||
[markdown.MarkdownSlide([markdown.MarkdownHeader(2, "Hello, World!")])],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser("### Hello, World!").parse(),
|
||||
markdown.MarkdownParser("### Hello, World!").parse().slides,
|
||||
[markdown.MarkdownSlide([markdown.MarkdownHeader(3, "Hello, World!")])],
|
||||
)
|
||||
|
||||
def test_lists(self):
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(cleandoc("""- Milk
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""- Milk
|
||||
- Bread
|
||||
- Cheese""")).parse(),
|
||||
[markdown.MarkdownSlide([
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Milk", None),
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Bread", None),
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Cheese", None),
|
||||
])],
|
||||
- Cheese"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Milk",
|
||||
None,
|
||||
),
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Bread",
|
||||
None,
|
||||
),
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Cheese",
|
||||
None,
|
||||
),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(cleandoc("""* Milk
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""* Milk
|
||||
* Bread
|
||||
* Cheese""")).parse(),
|
||||
[markdown.MarkdownSlide([
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Milk", None),
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Bread", None),
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Cheese", None),
|
||||
])],
|
||||
* Cheese"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Milk",
|
||||
None,
|
||||
),
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Bread",
|
||||
None,
|
||||
),
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Cheese",
|
||||
None,
|
||||
),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(cleandoc("""1. Milk
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""1. Milk
|
||||
2. Bread
|
||||
3. Cheese""")).parse(),
|
||||
[markdown.MarkdownSlide([
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.ORDERED, "Milk", 1),
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.ORDERED, "Bread", 2),
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.ORDERED, "Cheese", 3),
|
||||
])],
|
||||
3. Cheese"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.ORDERED, "Milk", 1
|
||||
),
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.ORDERED, "Bread", 2
|
||||
),
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.ORDERED, "Cheese", 3
|
||||
),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def test_comments(self):
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(cleandoc("""<!-- This is a comment -->
|
||||
Hello, World!""")).parse(),
|
||||
[markdown.MarkdownSlide([
|
||||
markdown.MarkdownComment("This is a comment"),
|
||||
markdown.MarkdownNewLine(),
|
||||
markdown.MarkdownText("Hello, World!"),
|
||||
])],
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""<!-- This is a comment -->
|
||||
Hello, World!"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownComment("This is a comment"),
|
||||
markdown.MarkdownNewLine(),
|
||||
markdown.MarkdownText("Hello, World!"),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(cleandoc("""Hello, World!
|
||||
<!-- This is a comment -->""")).parse(),
|
||||
[markdown.MarkdownSlide([
|
||||
markdown.MarkdownText("Hello, World!"),
|
||||
markdown.MarkdownComment("This is a comment"),
|
||||
])],
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""Hello, World!
|
||||
<!-- This is a comment -->"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownText("Hello, World!"),
|
||||
markdown.MarkdownComment("This is a comment"),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def test_code_blocks(self):
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(cleandoc("""```python
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""```python
|
||||
print("Hello, World!")
|
||||
```""")).parse(),
|
||||
[markdown.MarkdownSlide([
|
||||
markdown.MarkdownCodeBlock("python", 'print("Hello, World!")'),
|
||||
])],
|
||||
```"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownCodeBlock("python", 'print("Hello, World!")'),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def test_preprocessors(self):
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(cleandoc("""~~~bash
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""~~~bash
|
||||
echo 'Hello, World!'
|
||||
~~~
|
||||
Hi""")).parse(),
|
||||
[markdown.MarkdownSlide([
|
||||
markdown.MarkdownPreprocessor("bash", "echo 'Hello, World!'"),
|
||||
markdown.MarkdownNewLine(),
|
||||
markdown.MarkdownText("Hi"),
|
||||
])],
|
||||
Hi"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.slides,
|
||||
[
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownPreprocessor("bash", "echo 'Hello, World!'"),
|
||||
markdown.MarkdownNewLine(),
|
||||
markdown.MarkdownText("Hi"),
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def test_slide(self):
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(cleandoc("""# Hello, World!
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""# Hello, World!
|
||||
---
|
||||
- Milk
|
||||
- Bread
|
||||
- Cheese""")).parse(),
|
||||
- Cheese"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.slides,
|
||||
[
|
||||
markdown.MarkdownSlide([markdown.MarkdownHeader(1, "Hello, World!")]),
|
||||
markdown.MarkdownSlide([
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Milk", None),
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Bread", None),
|
||||
markdown.MarkdownListElement(markdown.MarkdownListElement.ListType.UNORDERED, "Cheese", None),
|
||||
]),
|
||||
markdown.MarkdownSlide(
|
||||
[
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Milk",
|
||||
None,
|
||||
),
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Bread",
|
||||
None,
|
||||
),
|
||||
markdown.MarkdownListElement(
|
||||
markdown.MarkdownListElement.ListType.UNORDERED,
|
||||
"Cheese",
|
||||
None,
|
||||
),
|
||||
]
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def test_metadata(self):
|
||||
self.assertEqual(
|
||||
markdown.MarkdownParser(
|
||||
cleandoc(
|
||||
"""---
|
||||
title: Hello, World!
|
||||
author: Keyb
|
||||
date: 01 Jan 1970
|
||||
---
|
||||
"""
|
||||
)
|
||||
)
|
||||
.parse()
|
||||
.metadata,
|
||||
{"title": "Hello, World!", "author": "Keyb", "date": "01 Jan 1970"}
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue