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 ',