diff options
| author | robot-piglet <[email protected]> | 2026-05-24 13:39:51 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2026-05-24 14:17:03 +0300 |
| commit | a328b20007004568c994dcd2aad60d2f8dbab5db (patch) | |
| tree | 92b24327bfadb198b1525cde8b341179dca4c0db /contrib/python/markdown-it-py | |
| parent | 61c451edf88125e836d9af09faac48c197673cad (diff) | |
Intermediate changes
commit_hash:9e9eb9789dc376bac164b089669f6c8adeefdc63
Diffstat (limited to 'contrib/python/markdown-it-py')
5 files changed, 116 insertions, 70 deletions
diff --git a/contrib/python/markdown-it-py/.dist-info/METADATA b/contrib/python/markdown-it-py/.dist-info/METADATA index 488e089b08e..c44604a6677 100644 --- a/contrib/python/markdown-it-py/.dist-info/METADATA +++ b/contrib/python/markdown-it-py/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: markdown-it-py -Version: 4.1.0 +Version: 4.2.0 Summary: Python port of markdown-it. Markdown parsing, done right! Keywords: markdown,lexer,parser,commonmark,markdown-it Author-email: Chris Sewell <[email protected]> diff --git a/contrib/python/markdown-it-py/markdown_it/__init__.py b/contrib/python/markdown-it-py/markdown_it/__init__.py index 523d92ef4ba..58dfa3bcc46 100644 --- a/contrib/python/markdown-it-py/markdown_it/__init__.py +++ b/contrib/python/markdown-it-py/markdown_it/__init__.py @@ -1,6 +1,6 @@ """A Python port of Markdown-It""" __all__ = ("MarkdownIt",) -__version__ = "4.1.0" +__version__ = "4.2.0" from .main import MarkdownIt diff --git a/contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py b/contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py index 517da2312aa..2c5efa85ccf 100644 --- a/contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py +++ b/contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py @@ -8,6 +8,7 @@ __all__ = ( "html_block", "lheading", "list_block", + "make_fence_rule", "paragraph", "reference", "table", @@ -15,7 +16,7 @@ __all__ = ( from .blockquote import blockquote from .code import code -from .fence import fence +from .fence import fence, make_fence_rule from .heading import heading from .hr import hr from .html_block import html_block diff --git a/contrib/python/markdown-it-py/markdown_it/rules_block/fence.py b/contrib/python/markdown-it-py/markdown_it/rules_block/fence.py index 263f1b8de8d..0d7e651eb06 100644 --- a/contrib/python/markdown-it-py/markdown_it/rules_block/fence.py +++ b/contrib/python/markdown-it-py/markdown_it/rules_block/fence.py @@ -1,4 +1,7 @@ # fences (``` lang, ~~~ lang) +from __future__ import annotations + +from collections.abc import Callable import logging from .state_block import StateBlock @@ -6,96 +9,138 @@ from .state_block import StateBlock LOGGER = logging.getLogger(__name__) -def fence(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool: - LOGGER.debug("entering fence: %s, %s, %s, %s", state, startLine, endLine, silent) +def make_fence_rule( + *, + markers: tuple[str, ...] = ("~", "`"), + token_type: str = "fence", + exact_match: bool = False, + disallow_marker_in_info: tuple[str, ...] = ("`",), + min_markers: int = 3, +) -> Callable[[StateBlock, int, int, bool], bool]: + """Create a fence parsing rule with configurable options. - haveEndMarker = False - pos = state.bMarks[startLine] + state.tShift[startLine] - maximum = state.eMarks[startLine] + :param markers: Tuple of single characters that can be used as fence markers. + :param token_type: The token type name to emit (e.g. "fence", "colon_fence"). + :param exact_match: If True, the closing fence must have exactly the same + number of marker characters as the opening fence (not "at least as many"). + This enables nesting of fences with different marker counts. + :param disallow_marker_in_info: Tuple of marker characters that are not allowed + to appear in the info string. The check only applies when the actual opening + marker is in this tuple (e.g. a tilde fence is unaffected by ``"`"`` being + listed). Per CommonMark, backtick fences cannot have backticks in the info + string. Use ``()`` to disable this restriction. + :param min_markers: Minimum number of marker characters to form a fence. + :return: A block rule function with signature + ``(state, startLine, endLine, silent) -> bool``. + """ - if state.is_code_block(startLine): - return False + closing_matcher: Callable[[int, int], bool] + if exact_match: + # closing code fence must have exactly the same number of markers as the opening one + closing_matcher = lambda opening_len, closing_len: closing_len == opening_len # noqa: E731 + else: + # closing code fence must be at least as long as the opening one + closing_matcher = lambda opening_len, closing_len: closing_len >= opening_len # noqa: E731 - if pos + 3 > maximum: - return False + def _fence_rule( + state: StateBlock, startLine: int, endLine: int, silent: bool + ) -> bool: + LOGGER.debug( + "entering fence: %s, %s, %s, %s", state, startLine, endLine, silent + ) - marker = state.src[pos] + haveEndMarker = False + pos = state.bMarks[startLine] + state.tShift[startLine] + maximum = state.eMarks[startLine] - if marker not in ("~", "`"): - return False + if state.is_code_block(startLine): + return False - # scan marker length - mem = pos - pos = state.skipCharsStr(pos, marker) + if pos + min_markers > maximum: + return False - length = pos - mem + marker = state.src[pos] - if length < 3: - return False + if marker not in markers: + return False - markup = state.src[mem:pos] - params = state.src[pos:maximum] + # scan marker length + mem = pos + pos = state.skipCharsStr(pos, marker) - if marker == "`" and marker in params: - return False + length = pos - mem - # Since start is found, we can report success here in validation mode - if silent: - return True + if length < min_markers: + return False - # search end of block - nextLine = startLine + markup = state.src[mem:pos] + params = state.src[pos:maximum] - while True: - nextLine += 1 - if nextLine >= endLine: - # unclosed block should be autoclosed by end of document. - # also block seems to be autoclosed by end of parent - break + if marker in disallow_marker_in_info and marker in params: + return False - pos = mem = state.bMarks[nextLine] + state.tShift[nextLine] - maximum = state.eMarks[nextLine] + # Since start is found, we can report success here in validation mode + if silent: + return True - if pos < maximum and state.sCount[nextLine] < state.blkIndent: - # non-empty line with negative indent should stop the list: - # - ``` - # test - break + # search end of block + nextLine = startLine + + while True: + nextLine += 1 + if nextLine >= endLine: + # unclosed block should be autoclosed by end of document. + # also block seems to be autoclosed by end of parent + break + + pos = mem = state.bMarks[nextLine] + state.tShift[nextLine] + maximum = state.eMarks[nextLine] + + if pos < maximum and state.sCount[nextLine] < state.blkIndent: + # non-empty line with negative indent should stop the list: + # - ``` + # test + break + + try: + if state.src[pos] != marker: + continue + except IndexError: + break - try: - if state.src[pos] != marker: + if state.is_code_block(nextLine): continue - except IndexError: - break - if state.is_code_block(nextLine): - continue + pos = state.skipCharsStr(pos, marker) - pos = state.skipCharsStr(pos, marker) + if not closing_matcher(length, pos - mem): + continue - # closing code fence must be at least as long as the opening one - if pos - mem < length: - continue + # make sure tail has spaces only + pos = state.skipSpaces(pos) - # make sure tail has spaces only - pos = state.skipSpaces(pos) + if pos < maximum: + continue - if pos < maximum: - continue + haveEndMarker = True + # found! + break + + # If a fence has heading spaces, they should be removed from its inner block + length = state.sCount[startLine] - haveEndMarker = True - # found! - break + state.line = nextLine + (1 if haveEndMarker else 0) - # If a fence has heading spaces, they should be removed from its inner block - length = state.sCount[startLine] + token = state.push(token_type, "code", 0) + token.info = params + token.content = state.getLines(startLine + 1, nextLine, length, True) + token.markup = markup + token.map = [startLine, state.line] + + return True - state.line = nextLine + (1 if haveEndMarker else 0) + return _fence_rule - token = state.push("fence", "code", 0) - token.info = params - token.content = state.getLines(startLine + 1, nextLine, length, True) - token.markup = markup - token.map = [startLine, state.line] - return True +#: The default fence rule (backtick and tilde markers, CommonMark compliant). +fence = make_fence_rule() diff --git a/contrib/python/markdown-it-py/ya.make b/contrib/python/markdown-it-py/ya.make index dd380e6d343..bdbb0a782c7 100644 --- a/contrib/python/markdown-it-py/ya.make +++ b/contrib/python/markdown-it-py/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(4.1.0) +VERSION(4.2.0) LICENSE(MIT) |
