From 6bd69a2832ff53ce9d63133b2481ef1dec821246 Mon Sep 17 00:00:00 2001 From: cssho Date: Mon, 26 Oct 2026 21:41:10 +0900 Subject: [PATCH] Support multiple defined symbols (closes #6) `symbol` now accepts several symbols: a string separated by spaces and/or `|` ("DEBUG RELEASE", "DEBUG|RELEASE") or a list (MkDocs: `symbol: [DEBUG, RELEASE]`). An `#if` is true when any defined symbol is among its listed names. A single symbol behaves exactly as before; None / [] define nothing. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012pKTVb5TrGn27R29rHHNck --- README.md | 14 ++++++++-- md_condition/extension.py | 2 +- md_condition/preprocessor.py | 9 ++++--- tests/test_condition.py | 50 ++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 669548e..041d027 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,11 @@ not RELEASE ### Rules -- `` is true when the configured `symbol` is exactly one of the listed names +- `symbol` defines one or more symbols: `DEBUG`, `DEBUG RELEASE`, `DEBUG|RELEASE`, or a list + `[DEBUG, RELEASE]`. With no `symbol` configured, every `#if` is false. +- `` is true when any defined symbol is exactly one of the listed names (`DEBUG` does not match `DEBUG_X`). Names are separated by spaces and/or `|`, so - `#if A | B` and `#if A|B` mean the same. With no `symbol` configured, every `#if` is false. + `#if A | B` and `#if A|B` mean the same. - `#else` is optional; nested `#if` blocks are not supported. - Malformed blocks (unclosed `#if`, stray `#else` / `#endif`, duplicate `#else`, nesting) raise `md_condition.ConditionSyntaxError` with the offending line number. @@ -53,3 +55,11 @@ markdown_extensions: - md_condition: symbol: DEBUG ``` + +To define several symbols at once: + +```yaml +markdown_extensions: + - md_condition: + symbol: [DEBUG, RELEASE] +``` diff --git a/md_condition/extension.py b/md_condition/extension.py index 490c2a4..4a9653c 100644 --- a/md_condition/extension.py +++ b/md_condition/extension.py @@ -7,7 +7,7 @@ class ConditionExtension(Extension): def __init__(self, **kwargs) -> None: self.config = { - "symbol": ["", "Condition symbol"], + "symbol": ["", "Defined symbol(s): a string separated by spaces/`|`, or a list"], } super().__init__(**kwargs) diff --git a/md_condition/preprocessor.py b/md_condition/preprocessor.py index b6ec8cb..468a4f9 100644 --- a/md_condition/preprocessor.py +++ b/md_condition/preprocessor.py @@ -25,7 +25,10 @@ class ConditionPreprocessor(Preprocessor): def __init__(self, md: Markdown, extension: ConditionExtension) -> None: super().__init__(md) - self.symbol = extension.getConfig("symbol") + # `symbol` is a string ("DEBUG", "DEBUG RELEASE", "DEBUG|RELEASE") or a list of them + symbol = extension.getConfig("symbol") or "" + values = [symbol] if isinstance(symbol, str) else symbol + self.symbols = {name for value in values for name in self.RE_NAME.findall(str(value))} def run(self, lines: list[str]) -> list[str]: new_lines = [] @@ -43,8 +46,8 @@ def run(self, lines: list[str]) -> list[str]: ) block_start = number seen_else = False - # `#if A B` / `#if A | B` is true when the symbol is one of the listed names - keep = self.symbol in self.RE_NAME.findall(start.group(1)) + # `#if A B` / `#if A | B` is true when any configured symbol is listed + keep = not self.symbols.isdisjoint(self.RE_NAME.findall(start.group(1))) continue if self.RE_ELSE.match(line): if block_start is None: diff --git a/tests/test_condition.py b/tests/test_condition.py index d733796..8fbf7df 100644 --- a/tests/test_condition.py +++ b/tests/test_condition.py @@ -112,6 +112,47 @@ def test_symbol_is_matched_as_whole_name(symbol: str, condition: str, kept: bool assert output == ("

yes

" if kept else "

no

") +ISSUE_6_SOURCE = """\ +# 1 + +DEBUG + +No DEBUG + +# 2 + +RELEASE + +No RELEASE +""" + + +@pytest.mark.parametrize( + ("symbol", "debug", "release"), + [ + # Issue #6: multiple defined symbols, each usable as its own condition + pytest.param("DEBUG RELEASE", True, True, id="space_separated"), + pytest.param("DEBUG|RELEASE", True, True, id="pipe_separated"), + pytest.param(["DEBUG", "RELEASE"], True, True, id="list"), + pytest.param(("DEBUG",), True, False, id="tuple"), + pytest.param("RELEASE", False, True, id="single"), + pytest.param([], False, False, id="empty_list"), + pytest.param(None, False, False, id="none"), + ], +) +def test_multiple_symbols(symbol, debug: bool, release: bool) -> None: + md = markdown.Markdown(extensions=[ConditionExtension(symbol=symbol)]) + expected = "

1

\n

{}

\n

2

\n

{}

".format( + "DEBUG" if debug else "No DEBUG", "RELEASE" if release else "No RELEASE" + ) + assert md.convert(ISSUE_6_SOURCE) == expected + + +def test_multiple_symbols_any_listed_name_matches() -> None: + md = markdown.Markdown(extensions=[ConditionExtension(symbol=["DEBUG", "STAGING"])]) + assert md.convert("\nyes\n") == "

yes

" + + @pytest.mark.parametrize( ("source", "message"), [ @@ -177,3 +218,12 @@ def test_make_extension_by_name() -> None: }, ) assert md.convert("\nyes\n") == "

yes

" + + +def test_make_extension_by_name_with_list() -> None: + # MkDocs passes YAML lists through as Python lists + md = markdown.Markdown( + extensions=["md_condition"], + extension_configs={"md_condition": {"symbol": ["DEBUG", "RELEASE"]}}, + ) + assert md.convert("\nyes\n") == "

yes

"