diff --git a/tests/test_parser.py b/tests/test_parser.py index 11d79e7..e78c6d3 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -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) diff --git a/textoutpc/__init__.py b/textoutpc/__init__.py index 2554786..42637a7 100755 --- a/textoutpc/__init__.py +++ b/textoutpc/__init__.py @@ -6,3 +6,8 @@ """textout() equivalent from Planète Casio.""" from __future__ import annotations + +from .parser import Parser + + +__all__ = ["Parser"] diff --git a/textoutpc/html.py b/textoutpc/html.py new file mode 100644 index 0000000..add1b96 --- /dev/null +++ b/textoutpc/html.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# ***************************************************************************** +# Copyright (C) 2023 Thomas Touhey +# 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:: + +
+ {{ content }} +
+
+    {{ value }}% +
+
+
+ + :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}%", + ), + ) + + def visit_spoiler(self, node: spoiler) -> None: + """Visit a spoiler tag. + + The format for the spoiler tag in HTML form is the following:: + +
+
+ {{ closed title }} +
+
+ {{ opened title }} +
+ {{ content }} +
+ + :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), + "", + self.starttag( + node, + "div", + CLASS="title off", + onclick="toggleSpoiler(this.parentNode, 'close')", + ), + escape(node.opened_title), + "", + ), + ) + + def depart_spoiler(self, node: spoiler) -> None: + """Depart a spoiler tag. + + :param node: The spoiler node. + """ + self.body.append("") + + +class Writer(BaseWriter): + """HTML writer with support for BBCode nodes.""" + + def __init__(self, /): + super().__init__() + self.translator_class = HTMLTranslator diff --git a/textoutpc/nodes.py b/textoutpc/nodes.py index 763a6e6..9d66164 100644 --- a/textoutpc/nodes.py +++ b/textoutpc/nodes.py @@ -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 diff --git a/textoutpc/parser.py b/textoutpc/parser.py index ce11f4a..4bb56d6 100644 --- a/textoutpc/parser.py +++ b/textoutpc/parser.py @@ -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()