feat: add special entity support for inline code
This commit is contained in:
parent
22876eea7b
commit
48c58a42ab
|
@ -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]!",
|
||||
[
|
||||
|
|
|
@ -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}",
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue