Pull request #13: feat: render units with non-breaking space

Merge in FRAG/50ohm.de from feature/unit-html-renderer to main

* commit '4c82c9fb146332bd3abe8e2d1d0308aafbdc54d4':
  refactor: improve code readability
  feat: improve tests for unit rendering
  refactor: use helper to format unit
  refactor: create module
  feat: render units with non-breaking space
This commit is contained in:
Tim Grelka DA2TG
2024-12-10 23:43:14 +00:00
6 changed files with 129 additions and 10 deletions
View File
-8
View File
@@ -1,8 +0,0 @@
import sys
import mistletoe
from fifty_ohm_html_renderer import FiftyOhmHtmlRenderer
if __name__ == "__main__":
with open(sys.argv[1]) as fin, open(sys.argv[2], "w") as fout:
fout.write(mistletoe.markdown(fin, FiftyOhmHtmlRenderer))
+27
View File
@@ -10,6 +10,7 @@ from renderer.quote import Quote
from renderer.references import References
from renderer.tag import Tag
from renderer.underline import Underline
from renderer.unit import Unit
class FiftyOhmHtmlRenderer(HtmlRenderer):
@@ -24,6 +25,7 @@ class FiftyOhmHtmlRenderer(HtmlRenderer):
BlockComment,
SpanComment,
Quote,
Unit,
Underline,
Tag,
HalfwidthSpaces,
@@ -50,6 +52,31 @@ class FiftyOhmHtmlRenderer(HtmlRenderer):
def render_underline(self, token):
return f"<u>{self.render_inner(token)}</u>"
@classmethod
def render_unit(cls, token: Unit):
unit = token.prefix + cls.convert_unit_helper(token.unit)
if token.unit in ["°", "%"]:
# Special cases with no space between value and unit.
return f"{token.value}{unit}"
else:
# Default case is rendered with a narrow no-break space.
return f"{token.value}&#8239;{unit}"
units = {
"Ohm": "Ω",
}
@classmethod
def convert_unit_helper(cls, unit: str) -> str:
"""Converts human-typable units to their preferred representation.
:param str unit: The unit to convert
"""
if unit in cls.units.keys():
return cls.units[unit]
else:
return unit
def render_thematic_break(self, token):
self.margin_anchor_id += 1
return f'<a id="margin_{self.margin_anchor_id}"></a>'
+55
View File
@@ -0,0 +1,55 @@
import re
from mistletoe.span_token import SpanToken
class Unit(SpanToken):
units = [
"A",
"Ah",
"A/mm²",
"baud",
"Bit",
"Bit/s",
"dB",
"dBi",
"dBm",
"dBW",
"F",
"J",
"Hz",
"H",
"cm",
"m",
"m²",
"Ohm",
"ppm",
"pps",
"s",
"V",
"W",
"Wh",
"°",
"%",
]
prefixes = ["p", "n", "μ", "m", "k", "M", "G"]
pattern = re.compile(
r"(?P<value>[0-9,]+) ?(?P<prefix>"
# Group all prefixes together in a capture group
# Empty string at end of list is required so the group always exists in the resulting match
+ "|".join(prefixes + [""])
+ ")(?P<unit>"
# Group all units together in a capture group.
+ "|".join(units)
+ r")(?!\w)"
)
value: str
prefix: str
unit: str
def __init__(self, match: re.Match):
self.value = match.group("value")
self.prefix = match.group("prefix") or ""
self.unit = match.group("unit")
+45
View File
@@ -0,0 +1,45 @@
import mistletoe
import pytest
from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer
from .util import paragraph
@pytest.mark.html
def test_basic_units_html():
"""Test basic units: Insert non-breaking, smaller space."""
assertions = {
"1 V": "1&#8239;V",
"0,12 V": "0,12&#8239;V",
"0,75 kW": "0,75&#8239;kW",
"33 A": "33&#8239;A",
"1 Ah": "1&#8239;Ah", # Ah can be a tricky case if the RegEx is wrong, as A might match before Ah, thus rendering the output with an "A" unit
}
for input, output in assertions.items():
assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output)
@pytest.mark.html
def test_translated_units_html():
"""Test translated units: Replace Ohm with Ω."""
assertions = {
"50 Ohm": "50&#8239;Ω",
"100 mOhm": "100&#8239;mΩ",
}
for input, output in assertions.items():
assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output)
@pytest.mark.html
def test_special_units_html():
"""Test special units: Don't put space before ° and %."""
assertions = {
"100%": "100%",
"42,24°": "42,24°",
}
for input, output in assertions.items():
assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output)
+2 -2
View File
@@ -1,2 +1,2 @@
def paragraph(text) :
return f'<p>{text}</p>\n'
def paragraph(text):
return f"<p>{text}</p>\n"