2
0
Fork 0

feat: add html writer for using the docutils cli

This commit is contained in:
Thomas Touhey 2023-12-01 13:38:11 +01:00
parent 26a0decd8a
commit 22876eea7b
5 changed files with 143 additions and 10 deletions

View File

@ -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)

View File

@ -6,3 +6,8 @@
"""textout() equivalent from Planète Casio."""
from __future__ import annotations
from .parser import Parser
__all__ = ["Parser"]

127
textoutpc/html.py Normal file
View File

@ -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">
&nbsp;&nbsp;&nbsp;{{ 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"&nbsp;&nbsp;&nbsp;{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

View File

@ -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

View File

@ -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()