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.references import References
from renderer.tag import Tag from renderer.tag import Tag
from renderer.underline import Underline from renderer.underline import Underline
from renderer.unit import Unit
class FiftyOhmHtmlRenderer(HtmlRenderer): class FiftyOhmHtmlRenderer(HtmlRenderer):
@@ -24,6 +25,7 @@ class FiftyOhmHtmlRenderer(HtmlRenderer):
BlockComment, BlockComment,
SpanComment, SpanComment,
Quote, Quote,
Unit,
Underline, Underline,
Tag, Tag,
HalfwidthSpaces, HalfwidthSpaces,
@@ -50,6 +52,31 @@ class FiftyOhmHtmlRenderer(HtmlRenderer):
def render_underline(self, token): def render_underline(self, token):
return f"<u>{self.render_inner(token)}</u>" 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): def render_thematic_break(self, token):
self.margin_anchor_id += 1 self.margin_anchor_id += 1
return f'<a id="margin_{self.margin_anchor_id}"></a>' 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) : def paragraph(text):
return f'<p>{text}</p>\n' return f"<p>{text}</p>\n"