2
0
Fork 0

feat: add special entity support for inline code

This commit is contained in:
Thomas Touhey 2023-12-01 18:54:22 +01:00
parent 22876eea7b
commit 48c58a42ab
2 changed files with 90 additions and 30 deletions

View File

@ -10,7 +10,15 @@ from __future__ import annotations
from itertools import chain
from typing import Iterable, Sequence
from docutils.nodes import Element, Node, Text, container, emphasis, strong
from docutils.nodes import (
Element,
Node,
Text,
container,
emphasis,
literal,
strong,
)
from docutils.utils import new_document
import pytest
@ -162,6 +170,18 @@ def compare_nodes(
"the message is: [rot=13]uryyb[/rot] - the - [rot13]jbeyq",
[Text("the message is: hello - the - world")],
),
(
"a`[code]`b",
[
Text("a"),
literal("", Text("[code]"), **{"class": "inline-code"}),
Text("b"),
],
),
(
"a[code]`",
[Text("a"), container("", Text("`"), **{"class": "code"})],
),
(
"[spoiler=should open|should close]spoiler [b]content[/b]!",
[

View File

@ -34,6 +34,7 @@ from .lexer import (
Entity,
NewlineEntity,
OpenTagEntity,
SpecialEntity,
TextEntity,
iter_textout_entities,
)
@ -153,6 +154,37 @@ class StateMachine:
return [Text(text)]
def open_tag(self, tag: Tag, /) -> None:
"""Open a new stack level.
:param tag: The tag with which to open the tag.
"""
# Add the text currently in the buffer to the top of the stack
# before inserting the new element.
text_nodes = self.flush_text()
if text_nodes:
prev: Sequence[Node] | Document
if self.stack:
prev = self.stack[0].children
else:
prev = self.document
if len(prev) > 0 and isinstance(prev[-1], Text):
prev[-1] = Text(str(prev[-1]) + str(text_nodes[0]))
else:
prev.extend(text_nodes)
# Insert the element.
self.stack.insert(
0,
StackElement(
name=tag.name,
tag=tag,
children=[],
is_raw=tag.is_raw(),
),
)
def close_multiple(self, count: int, /) -> None:
"""Close multiple tags.
@ -225,31 +257,7 @@ class StateMachine:
self.text += entity.raw
return
# Add the text currently in the buffer to the top of the stack
# before inserting the new element.
text_nodes = self.flush_text()
if text_nodes:
prev: Sequence[Node] | Document
if self.stack:
prev = self.stack[0].children
else:
prev = self.document
if len(prev) > 0 and isinstance(prev[-1], Text):
prev[-1] = Text(str(prev[-1]) + str(text_nodes[0]))
else:
prev.extend(text_nodes)
# Insert the element.
self.stack.insert(
0,
StackElement(
name=f"[{entity.name}]",
tag=tag,
children=[],
is_raw=tag.is_raw(),
),
)
self.open_tag(tag)
return
if isinstance(entity, CloseTagEntity):
@ -271,12 +279,44 @@ class StateMachine:
if ent_name in ("[]", el.name):
self.close_multiple(1 + i)
return
else:
# The closing tag doesn't correspond to an existing tag,
# so we consider it as simple text.
self.text += entity.raw
# The closing tag doesn't correspond to an existing tag,
# so we consider it as simple text.
self.text += entity.raw
return
if isinstance(entity, SpecialEntity):
# This either opens or closes a tag.
if self.stack and self.stack[0].is_raw:
if self.stack[0].name == entity.value:
self.close_multiple(1)
else:
self.text += entity.value
return
# If the tag is opened, we want to close it.
for i, el in enumerate(self.stack):
if entity.value == el.name:
self.close_multiple(1 + i)
return
tag_cls = self.tags.get(entity.value)
if tag_cls is None:
self.text += entity.value
return
# Otherwise, we want to open the tag.
try:
tag = tag_cls(name=entity.value)
except TagValidationError:
# TODO: Add a warning.
self.text += entity.value
return
self.open_tag(tag)
return
raise NotImplementedError( # pragma: no cover
f"Unsupported element {entity!r}",
)