diff --git a/docs/changelog.md b/docs/changelog.md index e0742d04..68ec2524 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,8 @@ See the [Contributing Guide](contributing.md) for details. ### Fixed +* Support automatic links with URL schemes other than HTTP and FTP, such as + `` (#1244). * Fix quadratic-time id generation in the `toc` extension when many headers share the same text. * Strip complete HTML tags with quoted `>` characters from `toc` labels and diff --git a/docs/index.md b/docs/index.md index 335508ff..7912d236 100644 --- a/docs/index.md +++ b/docs/index.md @@ -118,6 +118,13 @@ are summarized below: foreseeable future. That said, the [Sane List Extension](extensions/sane_lists.md) is available to provide a less surprising behavior. +* __Automatic Links__ + + Automatic links recognize URL schemes followed by `://`, including schemes + other than HTTP and FTP, such as `` and + ``. URI forms without `://`, such as + ``, continue to be treated as raw HTML. + Support ------- diff --git a/markdown/inlinepatterns.py b/markdown/inlinepatterns.py index ec9c7cb1..6f42bbf0 100644 --- a/markdown/inlinepatterns.py +++ b/markdown/inlinepatterns.py @@ -119,7 +119,7 @@ def build_inlinepatterns(md: Markdown, **kwargs: Any) -> util.Registry[InlinePro IMAGE_REFERENCE_RE = IMAGE_LINK_RE """ Match start of image reference (`![alt text][2]`). """ -AUTOLINK_RE = r'<((?:[Ff]|[Hh][Tt])[Tt][Pp][Ss]?://[^<>]*)>' +AUTOLINK_RE = r'<([A-Za-z][A-Za-z0-9+.-]*://[^<>]*)>' """ Match an automatic link (``). """ AUTOMAIL_RE = r'<([^<> !]+@[^@<> ]+)>' diff --git a/tests/test_syntax/inline/test_autolinks.py b/tests/test_syntax/inline/test_autolinks.py index b6bd1cf2..751ef134 100644 --- a/tests/test_syntax/inline/test_autolinks.py +++ b/tests/test_syntax/inline/test_autolinks.py @@ -24,6 +24,72 @@ class TestAutomaticLinks(TestCase): + def test_url_schemes(self): + for url in ( + 'http://example.org', + 'https://example.org', + 'ftp://example.org', + 'ftps://example.org', + 'HtTpS://example.org', + 'ssh://example.org', + 'git://example.org/repo', + 's3://bucket/key', + 'git+ssh://example.org/repo', + 'x.y-z://example.org', + ): + with self.subTest(url=url): + self.assertMarkdownRenders( + f'<{url}>', + f'

{url}

' + ) + + def test_url_schemes_with_userinfo(self): + for url in ( + 'ssh://user@example.org/path', + 'git+ssh://git@example.org/repo.git', + ): + with self.subTest(url=url): + self.assertMarkdownRenders( + f'<{url}>', + f'

{url}

' + ) + + def test_url_scheme_with_query(self): + self.assertMarkdownRenders( + '', + '

' + 'ssh://example.org/path?q=one&x=two

' + ) + + def test_adjacent_url_schemes(self): + self.assertMarkdownRenders( + 'Before after', + '

Before ssh://one' + 'git://two after

' + ) + + def test_url_scheme_without_closing_bracket(self): + self.assertMarkdownRenders( + '<ssh://example.org

' + ) + + def test_url_scheme_in_code_span(self): + self.assertMarkdownRenders( + '``', + '

<ssh://example.org>

' + ) + + def test_invalid_url_schemes(self): + for source, expected in ( + ('<1http://example.org>', '

<1http://example.org>

'), + ('', '

'), + ('', '

'), + ('boo!', '

boo!

'), + ): + with self.subTest(source=source): + self.assertMarkdownRenders(source, expected) + def test_email_address(self): self.assertMarkdownRenders( 'asdfasdfadsfasd or you can say ',