Skip to content

Commit 07dfa4e

Browse files
authored
Allow reference links with backticks
Fixes #495.
1 parent fb6b27a commit 07dfa4e

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

docs/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ See the [Contributing Guide](contributing.md) for details.
1717
* Ensure nested elements inside inline comments are properly unescaped (#1571).
1818
* Make the docs build successfully with mkdocstrings-python 2.0 (#1575).
1919
* Fix infinite loop when multiple bogus or unclosed HTML comments appear in input (#1578).
20+
* Backtick formatting permitted in reference links to match conventional
21+
links (#495).
2022

2123
## [3.10.0] - 2025-11-03
2224

markdown/inlinepatterns.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def build_inlinepatterns(md: Markdown, **kwargs: Any) -> util.Registry[InlinePro
7070
7171
"""
7272
inlinePatterns = util.Registry()
73-
inlinePatterns.register(BacktickInlineProcessor(BACKTICK_RE), 'backtick', 190)
73+
inlinePatterns.register(BacktickInlineProcessor(BACKTICK_RE, md), 'backtick', 190)
7474
inlinePatterns.register(EscapeInlineProcessor(ESCAPE_RE, md), 'escape', 180)
7575
inlinePatterns.register(ReferenceInlineProcessor(REFERENCE_RE, md), 'reference', 170)
7676
inlinePatterns.register(LinkInlineProcessor(LINK_RE, md), 'link', 160)
@@ -435,12 +435,14 @@ def handleMatch(self, m: re.Match[str], data: str) -> tuple[etree.Element, int,
435435

436436
class BacktickInlineProcessor(InlineProcessor):
437437
""" Return a `<code>` element containing the escaped matching text. """
438-
def __init__(self, pattern: str):
438+
def __init__(self, pattern: str, md: Markdown):
439439
InlineProcessor.__init__(self, pattern)
440440
self.ESCAPED_BSLASH = '{}{}{}'.format(util.STX, ord('\\'), util.ETX)
441441
self.tag = 'code'
442442
""" The tag of the rendered element. """
443443

444+
self.md = md
445+
444446
def handleMatch(self, m: re.Match[str], data: str) -> tuple[etree.Element | str, int, int]:
445447
"""
446448
If the match contains `group(3)` of a pattern, then return a `code`
@@ -451,6 +453,8 @@ def handleMatch(self, m: re.Match[str], data: str) -> tuple[etree.Element | str,
451453
452454
"""
453455
if m.group(3):
456+
if data.lstrip('[').rstrip(']').lower() in self.md.references: # ignore known references
457+
return None, None, None
454458
el = etree.Element(self.tag)
455459
el.text = util.AtomicString(util.code_escape(m.group(3).strip()))
456460
return el, m.start(0), m.end(0)
@@ -879,6 +883,8 @@ class ReferenceInlineProcessor(LinkInlineProcessor):
879883

880884
RE_LINK = re.compile(r'\s?\[([^\]]*)\]', re.DOTALL | re.UNICODE)
881885

886+
RE_BACKTICK = re.compile(BACKTICK_RE, re.DOTALL | re.UNICODE)
887+
882888
def handleMatch(self, m: re.Match[str], data: str) -> tuple[etree.Element | None, int | None, int | None]:
883889
"""
884890
Return [`Element`][xml.etree.ElementTree.Element] returned by `makeTag` method or `(None, None, None)`.
@@ -925,7 +931,19 @@ def makeTag(self, href: str, title: str, text: str) -> etree.Element:
925931
if title:
926932
el.set('title', title)
927933

928-
el.text = text
934+
if '`' in text: # Process possible backtick within text
935+
m = self.RE_BACKTICK.search(text)
936+
if m and m.group(3):
937+
el2 = etree.Element('code')
938+
el2.text = util.AtomicString(util.code_escape(m.group(3).strip()))
939+
el.append(el2)
940+
el.text = text[0:m.start(0)]
941+
el2.tail = text[m.end(0):]
942+
else:
943+
el.text = text
944+
else:
945+
el.text = text
946+
929947
return el
930948

931949

tests/test_syntax/inline/test_links.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,24 @@ def test_angles_and_nonsense_url(self):
164164
'<p><a href="?}]*+|&amp;)">test nonsense</a>.</p>'
165165
)
166166

167+
def test_monospaced_title(self):
168+
self.assertMarkdownRenders(
169+
"""[`test`](link)""",
170+
"""<p><a href="link"><code>test</code></a></p>"""
171+
)
172+
173+
def test_title_containing_monospaced_title(self):
174+
self.assertMarkdownRenders(
175+
"""[some `test`](link)""",
176+
"""<p><a href="link">some <code>test</code></a></p>"""
177+
)
178+
179+
def test_title_containing_single_backtick(self):
180+
self.assertMarkdownRenders(
181+
"""[some `test](link)""",
182+
"""<p><a href="link">some `test</a></p>"""
183+
)
184+
167185

168186
class TestReferenceLinks(TestCase):
169187

@@ -434,3 +452,50 @@ def test_ref_round_brackets(self):
434452
"""
435453
)
436454
)
455+
456+
def test_ref_link_monospaced_text(self):
457+
self.assertMarkdownRenders(
458+
self.dedent(
459+
"""
460+
[`Text`]
461+
462+
[`Text`]: http://example.com
463+
"""
464+
),
465+
"""<p><a href="http://example.com"><code>Text</code></a></p>"""
466+
)
467+
468+
def test_ref_link_with_containing_monospaced_text(self):
469+
self.assertMarkdownRenders(
470+
self.dedent(
471+
"""
472+
[some `Text`]
473+
474+
[some `Text`]: http://example.com
475+
"""
476+
),
477+
"""<p><a href="http://example.com">some <code>Text</code></a></p>"""
478+
)
479+
480+
self.assertMarkdownRenders(
481+
self.dedent(
482+
"""
483+
[`Text` after]
484+
485+
[`Text` after]: http://example.com
486+
"""
487+
),
488+
"""<p><a href="http://example.com"><code>Text</code> after</a></p>"""
489+
)
490+
491+
def test_ref_link_with_single_backtick(self):
492+
self.assertMarkdownRenders(
493+
self.dedent(
494+
"""
495+
[some `Text]
496+
497+
[some `Text]: http://example.com
498+
"""
499+
),
500+
"""<p><a href="http://example.com">some `Text</a></p>"""
501+
)

0 commit comments

Comments
 (0)