feat: add html writer for using the docutils cli
This commit is contained in:
parent
26a0decd8a
commit
22876eea7b
|
@ -15,7 +15,7 @@ from docutils.utils import new_document
|
|||
import pytest
|
||||
|
||||
from textoutpc.nodes import progress, spoiler
|
||||
from textoutpc.parser import TextoutParser
|
||||
from textoutpc.parser import Parser
|
||||
|
||||
|
||||
def represent_node(node: Node) -> str:
|
||||
|
@ -190,6 +190,6 @@ def compare_nodes(
|
|||
def test_parser(inputstring: str, expected: Sequence[Node]) -> None:
|
||||
"""Test that the parser works correctly."""
|
||||
doc = new_document("/tmp/fake-source.bbcode") # noqa: S108
|
||||
parser = TextoutParser()
|
||||
parser = Parser()
|
||||
parser.parse(inputstring, doc)
|
||||
assert compare_nodes(doc, expected)
|
||||
|
|
|
@ -6,3 +6,8 @@
|
|||
"""textout() equivalent from Planète Casio."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .parser import Parser
|
||||
|
||||
|
||||
__all__ = ["Parser"]
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env python
|
||||
# *****************************************************************************
|
||||
# Copyright (C) 2023 Thomas Touhey <thomas@touhey.fr>
|
||||
# This file is part of the textoutpc project, which is MIT-licensed.
|
||||
# *****************************************************************************
|
||||
"""HTML writer definition for textoutpc."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from html import escape
|
||||
|
||||
from docutils.writers.html5_polyglot import (
|
||||
HTMLTranslator as BaseHTMLTranslator,
|
||||
Writer as BaseWriter,
|
||||
)
|
||||
|
||||
from textoutpc.nodes import progress, spoiler
|
||||
|
||||
|
||||
__all__ = ["Writer"]
|
||||
|
||||
|
||||
class HTMLTranslator(BaseHTMLTranslator):
|
||||
"""HTML translator with support for BBCode nodes."""
|
||||
|
||||
def visit_progress(self, node: progress) -> None:
|
||||
"""Visit a progress tag.
|
||||
|
||||
The format for the progress tag in HTML form is the following::
|
||||
|
||||
<div>
|
||||
{{ content }}
|
||||
<div style="background-color: white; border: 1px solid black;
|
||||
width: 50%; margin-top: 2px; text-align: left">
|
||||
<div style="background-color: #FF3E28; color: black;
|
||||
font-weight: bold; max-width: 100%;
|
||||
width: {{ value }}%; height: 18px">
|
||||
{{ value }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
:param node: The progress node.
|
||||
"""
|
||||
self.body.append(
|
||||
self.starttag(node, "div"),
|
||||
)
|
||||
|
||||
def depart_progress(self, node: progress) -> None:
|
||||
"""Depart a progress tag.
|
||||
|
||||
:param node: The progress node.
|
||||
"""
|
||||
self.body.extend(
|
||||
(
|
||||
self.starttag(
|
||||
node,
|
||||
"div",
|
||||
style="background-color: white; border: 1px solid black; "
|
||||
+ "width: 50%; margin-top: 2px; text-align: left",
|
||||
),
|
||||
self.starttag(
|
||||
node,
|
||||
"div",
|
||||
style="background-color: #FF3E28; color: black; "
|
||||
+ "font-weight: bold; max-width: 100%; "
|
||||
+ f"width: {node.value}%; height: 18px",
|
||||
),
|
||||
f" {node.value}%</div></div></div>",
|
||||
),
|
||||
)
|
||||
|
||||
def visit_spoiler(self, node: spoiler) -> None:
|
||||
"""Visit a spoiler tag.
|
||||
|
||||
The format for the spoiler tag in HTML form is the following::
|
||||
|
||||
<div class="spoiler">
|
||||
<div class="title on"
|
||||
onclick="toggleSpoiler(this.parentNode, 'open')">
|
||||
{{ closed title }}
|
||||
</div>
|
||||
<div class="title off"
|
||||
onclick="toggleSpoiler(this.parentNode, 'close')">
|
||||
{{ opened title }}
|
||||
</div>
|
||||
{{ content }}
|
||||
</div>
|
||||
|
||||
:param node: The spoiler node.
|
||||
"""
|
||||
self.body.extend(
|
||||
(
|
||||
self.starttag(node, "div", CLASS="spoiler"),
|
||||
self.starttag(
|
||||
node,
|
||||
"div",
|
||||
CLASS="title on",
|
||||
onclick="toggleSpoiler(this.parentNode, 'open')",
|
||||
),
|
||||
escape(node.closed_title),
|
||||
"</div>",
|
||||
self.starttag(
|
||||
node,
|
||||
"div",
|
||||
CLASS="title off",
|
||||
onclick="toggleSpoiler(this.parentNode, 'close')",
|
||||
),
|
||||
escape(node.opened_title),
|
||||
"</div>",
|
||||
),
|
||||
)
|
||||
|
||||
def depart_spoiler(self, node: spoiler) -> None:
|
||||
"""Depart a spoiler tag.
|
||||
|
||||
:param node: The spoiler node.
|
||||
"""
|
||||
self.body.append("</div>")
|
||||
|
||||
|
||||
class Writer(BaseWriter):
|
||||
"""HTML writer with support for BBCode nodes."""
|
||||
|
||||
def __init__(self, /):
|
||||
super().__init__()
|
||||
self.translator_class = HTMLTranslator
|
|
@ -21,10 +21,10 @@ from docutils.nodes import Body, Element, General, TextElement
|
|||
class progress(General, TextElement):
|
||||
"""Simple body element used to represent a progress bar, as a block."""
|
||||
|
||||
value: float
|
||||
value: int
|
||||
"""The value between 0 and 100 of the progress bar."""
|
||||
|
||||
def __init__(self, *args, value: float, **kwargs):
|
||||
def __init__(self, *args, value: int, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.value = value
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from __future__ import annotations
|
|||
from typing import NamedTuple, Sequence
|
||||
|
||||
from docutils.nodes import document as Document, Node, Text
|
||||
from docutils.parsers import Parser
|
||||
from docutils.parsers import Parser as BaseParser
|
||||
|
||||
from .builtin import (
|
||||
AdminImageTag,
|
||||
|
@ -110,7 +110,7 @@ class StackElement(NamedTuple):
|
|||
"""Children nodes which to add to the parent element."""
|
||||
|
||||
|
||||
class TextoutStateMachine:
|
||||
class StateMachine:
|
||||
"""State machine for a "textout"-style language."""
|
||||
|
||||
__slots__ = ("document", "stack", "tags", "text")
|
||||
|
@ -286,7 +286,7 @@ class TextoutStateMachine:
|
|||
self.close_multiple(len(self.stack))
|
||||
|
||||
|
||||
class TextoutParser(Parser):
|
||||
class Parser(BaseParser):
|
||||
"""Parser for Planète Casio "textout"-type BBCode.
|
||||
|
||||
:param tags: The tags to use with the parser.
|
||||
|
@ -310,10 +310,11 @@ class TextoutParser(Parser):
|
|||
:param document: The document to populate.
|
||||
"""
|
||||
self.setup_parse(inputstring, document)
|
||||
self.lexer = iter_textout_entities(self.inputstring)
|
||||
state_machine = TextoutStateMachine(document=document, tags=self.tags)
|
||||
|
||||
for entity in self.lexer:
|
||||
lexer = iter_textout_entities(self.inputstring)
|
||||
state_machine = StateMachine(document=document, tags=self.tags)
|
||||
|
||||
for entity in lexer:
|
||||
state_machine.process(entity)
|
||||
|
||||
state_machine.close()
|
||||
|
|
Loading…
Reference in New Issue