diff --git a/CHANGES.md b/CHANGES.md index 90f815ec..e3d52acb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ## python-markdown2 2.5.6 (not yet released) +- [pull #746] Preserve query strings and fragments in `markdown-file-links` conversions, including reference-link targets. - [pull #739] Fix `header-ids` extra putting sanitized HTML into header ids in safe mode, which allowed attribute injection (#649) - [pull #732] Fix excessive CPU use in the inline HTML tokenizer on repeated unclosed tag fragments (#707) - [pull #736] Clarify how to enable file variables in the Python API and command-line help (#269, #365). diff --git a/lib/markdown2.py b/lib/markdown2.py index 66d49a69..625f4cc7 100755 --- a/lib/markdown2.py +++ b/lib/markdown2.py @@ -3928,7 +3928,8 @@ class _MarkdownFileLinksExtraOpts(_LinkProcessorExtraOpts, total=False): class MarkdownFileLinks(LinkProcessor): ''' - Replace links to `.md` files with `.html` links + Replace links to `.md` files with `.html` links, preserving query strings + and fragments. ''' name = 'markdown-file-links' @@ -3942,24 +3943,37 @@ def __init__(self, md: Markdown, options: Optional[dict]): def parse_inline_anchor_or_image(self, text: str, _link_text: str, start_idx: int): result = super().parse_inline_anchor_or_image(text, _link_text, start_idx) - if not result or not result[1] or not result[1].endswith('.md'): - # return None for invalid markup, or links that don't end with '.md' - # so that we don't touch them, and other extras can process them freely + if not result or not result[1]: + return + url = self._convert_url(result[1]) + if url == result[1]: + # Leave other links available for processing by other extras. return - url = result[1].removesuffix('.md') + '.html' return result[0], url, *result[2:] + @staticmethod + def _convert_url(url: str) -> str: + # Split before the suffix, keeping its spelling and empty delimiters. + path = re.split(r'[?#]', url, maxsplit=1)[0] + if path.endswith('.md'): + return path.removesuffix('.md') + '.html' + url[len(path):] + return url + def run(self, text: str): if Stage.LINKS > self.md.order > Stage.LINK_DEFS and self.options.get('link_defs', True): # running just after link defs have been stripped for key, url in self.md.urls.items(): - if url.endswith('.md'): - self.md.urls[key] = url.removesuffix('.md') + '.html' + path = re.split(r'[?#]', url, maxsplit=1)[0] + if path.endswith('.md'): + self.md.urls[key] = self._convert_url(url) return super().run(text) def test(self, text: str): - return super().test(text) and '.md' in text + # Link definitions have already been removed from text at this stage. + return super().test(text) and ( + '.md' in text or self.md.stage == Stage.LINK_DEFS + ) class Mermaid(FencedCodeBlocks): diff --git a/test/tm-cases/markdown_file_links_inline_suffixes.html b/test/tm-cases/markdown_file_links_inline_suffixes.html new file mode 100644 index 00000000..20c52ea3 --- /dev/null +++ b/test/tm-cases/markdown_file_links_inline_suffixes.html @@ -0,0 +1,35 @@ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

image

diff --git a/test/tm-cases/markdown_file_links_inline_suffixes.opts b/test/tm-cases/markdown_file_links_inline_suffixes.opts new file mode 100644 index 00000000..82a0acfa --- /dev/null +++ b/test/tm-cases/markdown_file_links_inline_suffixes.opts @@ -0,0 +1 @@ +{'extras': ['markdown-file-links']} diff --git a/test/tm-cases/markdown_file_links_inline_suffixes.text b/test/tm-cases/markdown_file_links_inline_suffixes.text new file mode 100644 index 00000000..a1210a10 --- /dev/null +++ b/test/tm-cases/markdown_file_links_inline_suffixes.text @@ -0,0 +1,35 @@ +[doc](guide.md) + +[doc](guide.md#install) + +[doc](guide.md?view=print) + +[doc](guide.md?view=print#install) + +[doc](guide.md#part?view=print) + +[doc](guide.md?) + +[doc](guide.md#) + +[doc](guide.md?#) + +[doc](./guide.md?next=other.md#part.md) + +[doc](https://example.com/guide.md#install) + +[doc](//example.com/guide.md?view=print) + +[doc](#part.md) + +[doc](?download=guide.md) + +[doc](guide.html#part.md) + +[doc](guide.html?next=guide.md) + +[doc](guide.md/child) + +[doc](guide.md.bak#part) + +![image](guide.md#install) diff --git a/test/tm-cases/markdown_file_links_no_linkdefs.html b/test/tm-cases/markdown_file_links_no_linkdefs.html index 48f897df..7b3fa3f8 100644 --- a/test/tm-cases/markdown_file_links_no_linkdefs.html +++ b/test/tm-cases/markdown_file_links_no_linkdefs.html @@ -1 +1,3 @@

This is a reference to a markdown file link but link definition swapping is disabled

+ +

inline and reference

diff --git a/test/tm-cases/markdown_file_links_no_linkdefs.text b/test/tm-cases/markdown_file_links_no_linkdefs.text index 6715929d..dce5c184 100644 --- a/test/tm-cases/markdown_file_links_no_linkdefs.text +++ b/test/tm-cases/markdown_file_links_no_linkdefs.text @@ -1,4 +1,7 @@ [This is a reference to a markdown file link][] but link definition swapping is disabled +[inline](guide.md#install) and [reference][suffix-ref] + [This is a reference to a markdown file link]: ./something.md +[suffix-ref]: guide.md#install diff --git a/test/tm-cases/markdown_file_links_reference_suffixes.html b/test/tm-cases/markdown_file_links_reference_suffixes.html new file mode 100644 index 00000000..2028f28c --- /dev/null +++ b/test/tm-cases/markdown_file_links_reference_suffixes.html @@ -0,0 +1,33 @@ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

+ +

doc

diff --git a/test/tm-cases/markdown_file_links_reference_suffixes.opts b/test/tm-cases/markdown_file_links_reference_suffixes.opts new file mode 100644 index 00000000..82a0acfa --- /dev/null +++ b/test/tm-cases/markdown_file_links_reference_suffixes.opts @@ -0,0 +1 @@ +{'extras': ['markdown-file-links']} diff --git a/test/tm-cases/markdown_file_links_reference_suffixes.text b/test/tm-cases/markdown_file_links_reference_suffixes.text new file mode 100644 index 00000000..a0037c1d --- /dev/null +++ b/test/tm-cases/markdown_file_links_reference_suffixes.text @@ -0,0 +1,51 @@ +[doc][suffix-1] + +[doc][suffix-2] + +[doc][suffix-3] + +[doc][suffix-4] + +[doc][suffix-5] + +[doc][suffix-6] + +[doc][suffix-7] + +[doc][suffix-8] + +[doc][suffix-9] + +[doc][suffix-10] + +[doc][suffix-11] + +[doc][suffix-12] + +[doc][suffix-13] + +[doc][suffix-14] + +[doc][suffix-15] + +[doc][suffix-16] + +[doc][suffix-17] + +[suffix-1]: guide.md +[suffix-2]: guide.md#install +[suffix-3]: guide.md?view=print +[suffix-4]: guide.md?view=print#install +[suffix-5]: guide.md#part?view=print +[suffix-6]: guide.md? +[suffix-7]: guide.md# +[suffix-8]: guide.md?# +[suffix-9]: ./guide.md?next=other.md#part.md +[suffix-10]: https://example.com/guide.md#install +[suffix-11]: //example.com/guide.md?view=print +[suffix-12]: #part.md +[suffix-13]: ?download=guide.md +[suffix-14]: guide.html#part.md +[suffix-15]: guide.html?next=guide.md +[suffix-16]: guide.md/child +[suffix-17]: guide.md.bak#part