Skip to content

Commit

Permalink
refactor(#25): support anchor links as a plugin (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
KyleKing committed Jun 21, 2024
1 parent a866425 commit d9e9841
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .copier-answers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Answer file maintained by Copier for: https://github.com/KyleKing/mdformat-plugin-template
# DO NOT MODIFY THIS FILE. Edit by re-running copier and changing responses to the questions
# Check into version control.
_commit: 0.1.5
_commit: 0.1.6
_src_path: gh:KyleKing/mdformat-plugin-template
author_email: dev.act.kyle@gmail.com
author_name: Kyle King
Expand Down
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Development

## API Documentation

A collection of useful resources to reference when developing new features:

- [`markdown-it-py` documentation](https://markdown-it-py.readthedocs.io/en/latest/using.html)
- [`markdown-it` (JS) documentation](https://markdown-it.github.io/markdown-it)

## Local Development

This package utilizes [flit](https://flit.readthedocs.io) as the build engine, and [tox](https://tox.readthedocs.io) for test automation.

To install these development dependencies:
Expand Down
7 changes: 5 additions & 2 deletions mdformat_mkdocs/mdit_plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

from ._content_tabs import CONTENT_TAB_MARKERS, content_tabs_plugin
from ._mkdocs_admon import MKDOCS_ADMON_MARKERS, mkdocs_admon_plugin
from ._mkdocstrings_crossreference import PREFIX as MKDOCSTRINGS_CROSSREFERENCE_PREFIX
from ._mkdocstrings_crossreference import mkdocstrings_crossreference_plugin
from ._mkdocs_anchors import MKDOCS_ANCHORS_PREFIX, mkdocs_anchors_plugin
from ._mkdocstrings_crossreference import (
MKDOCSTRINGS_CROSSREFERENCE_PREFIX,
mkdocstrings_crossreference_plugin,
)
from ._pymd_abbreviations import PYMD_ABBREVIATIONS_PREFIX, pymd_abbreviations_plugin
47 changes: 47 additions & 0 deletions mdformat_mkdocs/mdit_plugins/_mkdocs_anchors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Match markdown anchors from the mkdocs-autorefs plugin.
Matches:
```md
[](){#some-anchor-name}
```
Docs: https://mkdocstrings.github.io/autorefs
"""

import re

from markdown_it import MarkdownIt
from markdown_it.rules_inline import StateInline

LINK_PATTERN = re.compile(r"\[\]\(<?>?\){#(?P<anchor>[^ }]+)}")
MKDOCS_ANCHORS_PREFIX = "mkdocs_anchor"


def _mkdocs_anchors_plugin(state: StateInline, silent: bool) -> bool:
match = LINK_PATTERN.match(state.src[state.pos : state.posMax])
if not match:
return False

if silent:
return True

anchor = match["anchor"]
o_token = state.push(f"{MKDOCS_ANCHORS_PREFIX}_open", "a", 1)
o_token.attrs = {"id": anchor, "href": ""}
o_token.meta = {"content": f"[](){{#{anchor}}}"}
state.push(f"{MKDOCS_ANCHORS_PREFIX}_close", "a", -1)

state.pos += match.end()

return True


def mkdocs_anchors_plugin(md: MarkdownIt) -> None:
md.inline.ruler.before(
"link",
MKDOCS_ANCHORS_PREFIX,
_mkdocs_anchors_plugin,
{"alt": ["link"]},
)
9 changes: 6 additions & 3 deletions mdformat_mkdocs/mdit_plugins/_mkdocstrings_crossreference.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from markdown_it.rules_inline import StateInline

LINK_PATTERN = re.compile(r"\[([^[|\]\n]+)\]\[([^\]\n]*)\]")
PREFIX = "mkdocstrings_crossreference"
MKDOCSTRINGS_CROSSREFERENCE_PREFIX = "mkdocstrings_crossreference"


def _mkdocstrings_crossreference(state: StateInline, silent: bool) -> bool:
Expand All @@ -26,7 +26,7 @@ def _mkdocstrings_crossreference(state: StateInline, silent: bool) -> bool:
if silent:
return True

token = state.push(PREFIX, "", 0)
token = state.push(MKDOCSTRINGS_CROSSREFERENCE_PREFIX, "", 0)
token.content = match.group()

state.pos += match.end()
Expand All @@ -35,4 +35,7 @@ def _mkdocstrings_crossreference(state: StateInline, silent: bool) -> bool:


def mkdocstrings_crossreference_plugin(md: MarkdownIt) -> None:
md.inline.ruler.push(PREFIX, _mkdocstrings_crossreference)
md.inline.ruler.push(
MKDOCSTRINGS_CROSSREFERENCE_PREFIX,
_mkdocstrings_crossreference,
)
50 changes: 10 additions & 40 deletions mdformat_mkdocs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

import argparse
from functools import lru_cache, partial
from functools import partial
from typing import Mapping

from markdown_it import MarkdownIt
Expand All @@ -14,10 +14,12 @@
from ._normalize_list import normalize_list as unbounded_normalize_list
from ._postprocess_inline import postprocess_list_wrap
from .mdit_plugins import (
MKDOCS_ANCHORS_PREFIX,
MKDOCSTRINGS_CROSSREFERENCE_PREFIX,
PYMD_ABBREVIATIONS_PREFIX,
content_tabs_plugin,
mkdocs_admon_plugin,
mkdocs_anchors_plugin,
mkdocstrings_crossreference_plugin,
pymd_abbreviations_plugin,
)
Expand Down Expand Up @@ -56,6 +58,7 @@ def update_mdit(mdit: MarkdownIt) -> None:
"""No changes to markdown parsing are necessary."""
mdit.use(content_tabs_plugin)
mdit.use(mkdocs_admon_plugin)
mdit.use(mkdocs_anchors_plugin)
mdit.use(pymd_abbreviations_plugin)

global _ALIGN_SEMANTIC_BREAKS_IN_LISTS # noqa: PLW0603
Expand All @@ -77,6 +80,11 @@ def _render_node_content(node: RenderTreeNode, context: RenderContext) -> str:
return node.content


def _render_meta_content(node: RenderTreeNode, context: RenderContext) -> str: # noqa: ARG001
"""Return node content without additional processing."""
return node.meta.get("content", "")


def _render_pymd_abbr(node: RenderTreeNode, context: RenderContext) -> str: # noqa: ARG001
"""Render an Abbreviation."""
return node.content
Expand All @@ -99,17 +107,6 @@ def _render_with_default_renderer(
return text


@lru_cache(maxsize=1)
def _match_plugin_renderer(syntax_type: str) -> Render | None:
from mdformat.plugins import PARSER_EXTENSIONS # noqa: PLC0415

for name, plugin in PARSER_EXTENSIONS.items():
# Ignore this plugin (mkdocs) to avoid recursion. Name is set in pyproject.toml
if name != "mkdocs" and plugin.RENDERERS.get(syntax_type):
return plugin.RENDERERS[syntax_type]
return None


def _render_cross_reference(node: RenderTreeNode, context: RenderContext) -> str:
"""Render a MkDocs crossreference link."""
if _IGNORE_MISSING_REFERENCES:
Expand All @@ -118,33 +115,6 @@ def _render_cross_reference(node: RenderTreeNode, context: RenderContext) -> str
return _render_with_default_renderer(node, context, "link")


def _render_links_and_mkdocs_anchors(
node: RenderTreeNode,
context: RenderContext,
) -> str:
"""Intercepts rendering of [MkDocs AutoRefs 'markdown anchors'](https://mkdocs.github.io/autorefs/#markdown-anchors).
Replaces `[...](<>)` with `[...]()` to produce output like:
```md
[](){#some-anchor-name}
```
If no match, defers to other plugins or the default
"""
syntax_type = node.type

rendered = _render_with_default_renderer(node, context, syntax_type)
if rendered.endswith("](<>)"):
return rendered[:-3] + ")"

# Run other plugin renders if they exist
if plugin_render := _match_plugin_renderer(syntax_type):
return plugin_render(node, context)
return rendered


# A mapping from `RenderTreeNode.type` to a `Render` function that can
# render the given `RenderTreeNode` type. These override the default
# `Render` funcs defined in `mdformat.renderer.DEFAULT_RENDERERS`.
Expand All @@ -154,8 +124,8 @@ def _render_links_and_mkdocs_anchors(
"content_tab_mkdocs": ADMON_RENDERS["admonition"],
"content_tab_mkdocs_title": ADMON_RENDERS["admonition_title"],
MKDOCSTRINGS_CROSSREFERENCE_PREFIX: _render_cross_reference,
MKDOCS_ANCHORS_PREFIX: _render_meta_content,
PYMD_ABBREVIATIONS_PREFIX: _render_pymd_abbr,
"link": _render_links_and_mkdocs_anchors,
}


Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = [
"mdformat >= 0.7.16",
"mdformat >= 0.7.17",
"mdformat-admon >= 2.0.3",
"mdformat-gfm >= 0.3.6",
"more-itertools >= 10.2.0",
Expand All @@ -25,7 +25,7 @@ readme = "README.md"
requires-python = ">=3.8.0"

[project.entry-points."mdformat.parser_extension"]
mkdocs = "mdformat_mkdocs" # Do not change name without modifying '_match_plugin_renderer'
mkdocs = "mdformat_mkdocs"

[project.optional-dependencies]
dev = ["pre-commit"]
Expand Down
16 changes: 16 additions & 0 deletions tests/format/fixtures/mkdocs_autorefs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Anchor links (FIXME: Remove inserted newline)
.
[](){#some-anchor-name}
# Some Title
.
[](){#some-anchor-name}

# Some Title
.

Broken Anchor links (https://github.com/KyleKing/mdformat-mkdocs/issues/25)
.
[](<>){#some-anchor-name}
.
[](){#some-anchor-name}
.
7 changes: 0 additions & 7 deletions tests/format/fixtures/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -1541,13 +1541,6 @@ Unsupported versions of 0-indexed markdown list (Within ordered list)
0. next
.

Anchor links (https://github.com/KyleKing/mdformat-mkdocs/issues/25)
.
[](){#some-anchor-name}
.
[](){#some-anchor-name}
.

Broken code block because of `>` (https://github.com/KyleKing/mdformat-mkdocs/issues/31)
.
# Title
Expand Down
9 changes: 5 additions & 4 deletions tests/format/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ def flatten(nested_list: list[list[T]]) -> list[T]:

fixtures = flatten(
[
read_fixture_file(Path(__file__).parent / fixture_path)
read_fixture_file(Path(__file__).parent / "fixtures" / fixture_path)
for fixture_path in (
"fixtures/content_tabs.md",
"fixtures/pymd_abbreviations.md",
"fixtures/text.md",
"content_tabs.md",
"mkdocs_autorefs.md",
"pymd_abbreviations.md",
"text.md",
)
],
)
Expand Down
29 changes: 29 additions & 0 deletions tests/render/fixtures/mkdocs_autorefs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Anchor links (https://github.com/KyleKing/mdformat-mkdocs/issues/25)
.
[](){#some-anchor-name}
.
<p><a id="some-anchor-name" href=""></a></p>
.

For header (https://github.com/DFiantHDL/DFHDL/blob/192d7e58e1107da6b0a885e54a853a88bb619f29/docs/user-guide/state/index.md?plain=1#L1-L4 & https://dfianthdl.github.io/user-guide/state/#state-initialization)
.
[](){#state}
# State & Initialization

Semantically, every DFiant dataflow variable references a token stream (TS).
.
<p><a id="state" href=""></a></p>
<h1>State &amp; Initialization</h1>
<p>Semantically, every DFiant dataflow variable references a token stream (TS).</p>
.

Example inline (https://github.com/gustavofoa/dicasdeprogramacao.com.br/blob/e35771b41fce6b7c46875bcf65000f241755c13b/content/posts/iniciante-em-programacao/2013-05-02-operadores-relacionais.md?plain=1#L116C44-L118C16)
.
**maior ****menor** para
verificar a precedência alfabética de um [](){#spiderWordFound4}texto em
relação a outro
.
<p>**maior **e <strong>menor</strong> para
verificar a precedência alfabética de um <a id="spiderWordFound4" href=""></a>texto em
relação a outro</p>
.
13 changes: 11 additions & 2 deletions tests/render/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
from markdown_it import MarkdownIt
from markdown_it.utils import read_fixture_file

from mdformat_mkdocs.mdit_plugins import content_tabs_plugin, mkdocs_admon_plugin
from mdformat_mkdocs.mdit_plugins import (
content_tabs_plugin,
mkdocs_admon_plugin,
mkdocs_anchors_plugin,
)

from ..helpers import print_text

Expand All @@ -23,7 +27,12 @@ def with_plugin(filename, plugins):
"content-tabs.md",
[mkdocs_admon_plugin, content_tabs_plugin],
),
*with_plugin("pymd_abbreviations.md", []),
*with_plugin("mkdocs_autorefs.md", [mkdocs_anchors_plugin]),
*with_plugin(
"pymd_abbreviations.md",
[], # FIXME: Test with `pymd_abbreviations_plugin`
),
# TODO: Test cross-reference!
],
)
def test_render(line, title, text, expected, plugins):
Expand Down

0 comments on commit d9e9841

Please sign in to comment.