From 7c1e4892f5117649268729e884dbd46ba40e49a7 Mon Sep 17 00:00:00 2001 From: Kyle King Date: Sun, 23 Jun 2024 20:58:40 -0400 Subject: [PATCH] feat(#33): partially fix initial header-autoref implementation --- mdformat_mkdocs/mdit_plugins/__init__.py | 2 + .../mdit_plugins/_mkdocstrings_autorefs.py | 78 +++++++++++++++++++ mdformat_mkdocs/plugin.py | 11 ++- .../format/fixtures/mkdocstrings_autorefs.md | 13 ++-- 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/mdformat_mkdocs/mdit_plugins/__init__.py b/mdformat_mkdocs/mdit_plugins/__init__.py index b2a69fd..06bb36a 100644 --- a/mdformat_mkdocs/mdit_plugins/__init__.py +++ b/mdformat_mkdocs/mdit_plugins/__init__.py @@ -7,6 +7,7 @@ ) from ._mkdocstrings_autorefs import ( MKDOCSTRINGS_AUTOREFS_PREFIX, + MKDOCSTRINGS_HEADER_AUTOREFS_PREFIX, mkdocstrings_autorefs_plugin, ) from ._mkdocstrings_crossreference import ( @@ -20,6 +21,7 @@ "MATERIAL_CONTENT_TAB_MARKERS", "MKDOCSTRINGS_AUTOREFS_PREFIX", "MKDOCSTRINGS_CROSSREFERENCE_PREFIX", + "MKDOCSTRINGS_HEADER_AUTOREFS_PREFIX", "PYMD_ABBREVIATIONS_PREFIX", "material_admon_plugin", "material_content_tabs_plugin", diff --git a/mdformat_mkdocs/mdit_plugins/_mkdocstrings_autorefs.py b/mdformat_mkdocs/mdit_plugins/_mkdocstrings_autorefs.py index 478fd30..d804844 100644 --- a/mdformat_mkdocs/mdit_plugins/_mkdocstrings_autorefs.py +++ b/mdformat_mkdocs/mdit_plugins/_mkdocstrings_autorefs.py @@ -10,14 +10,20 @@ """ +from __future__ import annotations + import re +from re import Match from markdown_it import MarkdownIt +from markdown_it.rules_block import StateBlock from markdown_it.rules_inline import StateInline from mdformat_admon.factories import new_token _AUTOREFS_PATTERN = re.compile(r"\[\]\(?\){#(?P[^ }]+)}") +_HEADER_PATTERN = re.compile(r"(?P^#{1,6}) (?P.+)") MKDOCSTRINGS_AUTOREFS_PREFIX = "mkdocstrings_autorefs" +MKDOCSTRINGS_HEADER_AUTOREFS_PREFIX = f"{MKDOCSTRINGS_AUTOREFS_PREFIX}_header" def _mkdocstrings_autorefs_plugin(state: StateInline, silent: bool) -> bool: @@ -38,6 +44,72 @@ def _mkdocstrings_autorefs_plugin(state: StateInline, silent: bool) -> bool: return True +def _mkdocstrings_header_alias_plugin( + state: StateBlock, + start_line: int, + end_line: int, + silent: bool, +) -> bool: + """Identify when an autoref anchor is directly before a header. + + Simplified header parsing adapted from: + https://github.com/executablebooks/markdown-it-py/blob/c10312e2e475a22edb92abede15d3dcabd0cac0c/markdown_it/rules_block/heading.py + + """ + if start_line == 0 or state.is_code_block(start_line): + return False + + # Exit quickly if paragraph doesn't start with an autoref + start = state.bMarks[start_line] + state.tShift[start_line] + try: + if state.src[start : start + 3] != "[](": + return False + except IndexError: + return False + + matches: list[Match[str]] = [] + header: Match[str] | None = None + next_line = start_line + while next_line <= end_line: + start = state.bMarks[next_line] + state.tShift[next_line] + maximum = state.eMarks[next_line] + line = state.src[start:maximum] + # Catch as many sequential autorefs as possible before a single header + if match := _AUTOREFS_PATTERN.match(line): + matches.append(match) + else: + header = _HEADER_PATTERN.match(line) + break + next_line += 1 + if header is None: # for pylint + return False + + if silent: + return True + + state.line = start_line + 1 + with new_token(state, MKDOCSTRINGS_HEADER_AUTOREFS_PREFIX, "div"): + for match in matches: + anchor = match["anchor"] + with new_token(state, MKDOCSTRINGS_AUTOREFS_PREFIX, "a") as a_token: + a_token.attrs = {"id": anchor, "href": ""} + a_token.meta = {"content": f"[](){{#{anchor}}}"} + + level = len(header["markdown"]) + with new_token(state, "header", f"h{level}") as h_token: + h_token.markup = header["markdown"] + h_token.map = [start_line, state.line] + + inline = state.push("inline", "", 0) + inline.content = " ".join(header["content"]) + inline.map = [start_line, state.line] + inline.children = [] + + state.line = next_line + 1 + + return True + + def mkdocstrings_autorefs_plugin(md: MarkdownIt) -> None: md.inline.ruler.before( "link", @@ -45,3 +117,9 @@ def mkdocstrings_autorefs_plugin(md: MarkdownIt) -> None: _mkdocstrings_autorefs_plugin, {"alt": ["link"]}, ) + md.block.ruler.before( + "paragraph", + MKDOCSTRINGS_HEADER_AUTOREFS_PREFIX, + _mkdocstrings_header_alias_plugin, + {"alt": ["paragraph"]}, + ) diff --git a/mdformat_mkdocs/plugin.py b/mdformat_mkdocs/plugin.py index 81fefe7..b4f81f7 100644 --- a/mdformat_mkdocs/plugin.py +++ b/mdformat_mkdocs/plugin.py @@ -16,6 +16,7 @@ from .mdit_plugins import ( MKDOCSTRINGS_AUTOREFS_PREFIX, MKDOCSTRINGS_CROSSREFERENCE_PREFIX, + MKDOCSTRINGS_HEADER_AUTOREFS_PREFIX, PYMD_ABBREVIATIONS_PREFIX, material_admon_plugin, material_content_tabs_plugin, @@ -87,11 +88,18 @@ def _render_meta_content(node: RenderTreeNode, context: RenderContext) -> str: def _render_inline_content(node: RenderTreeNode, context: RenderContext) -> str: # noqa: ARG001 - """Render the node's markup and inline content.""" + """Render the node's inline content.""" [inline] = node.children return inline.content +def _tbd(node: RenderTreeNode, context: RenderContext) -> str: # noqa: ARG001 + [*autorefs, header] = node.children + lines = [_n.content for _n in autorefs] + lines.append(f"{header.markup} {header.content}") + return "\n".join(lines) + + def _render_with_default_renderer( node: RenderTreeNode, context: RenderContext, @@ -126,6 +134,7 @@ def _render_cross_reference(node: RenderTreeNode, context: RenderContext) -> str "content_tab_mkdocs": ADMON_RENDERS["admonition"], "content_tab_mkdocs_title": ADMON_RENDERS["admonition_title"], MKDOCSTRINGS_AUTOREFS_PREFIX: _render_meta_content, + MKDOCSTRINGS_HEADER_AUTOREFS_PREFIX: _tbd, MKDOCSTRINGS_CROSSREFERENCE_PREFIX: _render_cross_reference, PYMD_ABBREVIATIONS_PREFIX: _render_inline_content, } diff --git a/tests/format/fixtures/mkdocstrings_autorefs.md b/tests/format/fixtures/mkdocstrings_autorefs.md index f06fb9a..062deb7 100644 --- a/tests/format/fixtures/mkdocstrings_autorefs.md +++ b/tests/format/fixtures/mkdocstrings_autorefs.md @@ -1,11 +1,12 @@ -Header-Anchor links, but I'm unsure how to remove the line separator (Initial attempt: https://github.com/KyleKing/mdformat-mkdocs/commit/055aca6ebadc505d60a00c3a8ae575314b11c276) +Header aliases . -[](){#some-anchor-name} -# Some Title +[](){#contributing} +[](){#development-setup} +## How to contribute to the project? . -[](){#some-anchor-name} - -# Some Title +[](){#contributing} +[](){#development-setup} +## How to contribute to the project? . Broken Anchor links (https://github.com/KyleKing/mdformat-mkdocs/issues/25)