diff --git a/ChangeLog.md b/ChangeLog.md index fe9ec19..6fa6e6b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,12 @@ # Gemtext ChangeLog +## Unreleased + +**Released: WiP** + +- Added optional (on my default) support for Spartan input style lines + (`=:`). ([#6](https://github.com/davep/gemtext/pull/6)) + ## v1.0.0 **Released: 2026-07-16** diff --git a/src/gemtext/__init__.py b/src/gemtext/__init__.py index c66e67a..f73a2bc 100644 --- a/src/gemtext/__init__.py +++ b/src/gemtext/__init__.py @@ -25,6 +25,7 @@ Paragraph, PreFormatted, Quote, + SpartanPrompt, ) ############################################################################## @@ -38,6 +39,7 @@ "Paragraph", "PreFormatted", "Quote", + "SpartanPrompt", ] ### __init__.py ends here diff --git a/src/gemtext/parser.py b/src/gemtext/parser.py index 2995107..111aa35 100644 --- a/src/gemtext/parser.py +++ b/src/gemtext/parser.py @@ -127,6 +127,20 @@ def _repr_props(self) -> tuple[str, ...]: return super()._repr_props() + (f"uri={self._uri!r}",) +############################################################################## +class SpartanPrompt(Link): + """A Spartan protocol link that prompts for input. + + Note: + This is a subclass of [`Link`][gemtext.Link], as it behaves similarly to a + link in Gemtext. + + This class will only ever be created if the Gemtext parser is + initialized with [`with_spartan_support=True`][gemtext.Gemtext]. + Otherwise, Spartan prompts will be treated as regular paragraphs. + """ + + ############################################################################## _LINK: Final[str] = "=>" """Marker for a link in Gemtext.""" @@ -144,20 +158,25 @@ def _repr_props(self) -> tuple[str, ...]: """Marker for a list item in Gemtext.""" _NON_CONFORMING_LIST_ITEM: Final[str] = "*\t" """Marker for a non-conforming list item in Gemtext.""" +_SPARTAN_PROMPT: Final[str] = "=:" +"""Marker for a Spartan protocol prompt in Gemtext.""" ############################################################################## class Gemtext: """A Gemtext parser.""" - def __init__(self, text: str) -> None: + def __init__(self, text: str, *, with_spartan_support: bool = True) -> None: """Initialize the GemText parser. Args: text: The Gemtext content to parse. + with_spartan_support: Whether to support [Spartan protocol prompts][gemtext.SpartanPrompt]. """ self._text = text """The raw Gemtext content to be parsed.""" + self._with_spartan_support = with_spartan_support + """Whether to support Spartan protocol prompts.""" def _parse(self) -> Iterator[Line]: """Parse the Gemtext content into Line objects. @@ -194,6 +213,9 @@ def _parse(self) -> Iterator[Line]: yield ListItem(line.removeprefix(_LIST_ITEM).strip()) elif is_a(_NON_CONFORMING_LIST_ITEM): yield ListItem(line.removeprefix(_NON_CONFORMING_LIST_ITEM).strip()) + elif is_a(_SPARTAN_PROMPT) and self._with_spartan_support: + parts = line.removeprefix(_SPARTAN_PROMPT).strip().split(maxsplit=1) + yield SpartanPrompt(parts[0], parts[1] if len(parts) > 1 else "") else: yield Paragraph(line) if in_preformat: diff --git a/tests/test_spartan.py b/tests/test_spartan.py new file mode 100644 index 0000000..346e581 --- /dev/null +++ b/tests/test_spartan.py @@ -0,0 +1,25 @@ +"""Unit tests for the Spartan extensions.""" + +############################################################################## +# Local imports. +from gemtext import Gemtext, Link, Paragraph, SpartanPrompt + + +############################################################################## +def test_without_spartan_is_paragraph() -> None: + """Test that a Spartan link is treated as a paragraph when Spartan support is disabled.""" + gemtext = Gemtext("=: spartan://spartan.example.com", with_spartan_support=False) + assert isinstance(gemtext.content[0], Paragraph) + assert str(gemtext.content[0]) == "=: spartan://spartan.example.com" + + +############################################################################## +def test_with_spartan_is_spartan_prompt() -> None: + """Test that a Spartan link is treated as a SpartanPrompt when Spartan support is enabled.""" + gemtext = Gemtext("=: spartan://spartan.example.com") + assert isinstance(gemtext.content[0], SpartanPrompt) + assert isinstance(gemtext.content[0], Link) + assert gemtext.content[0].uri == "spartan://spartan.example.com" + + +### test_spartan.py ends here