Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions markdown/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,15 @@ def is_block_level(self, tag: Any) -> bool:
"""
Check if the given `tag` is a block level HTML tag.

Returns `True` for any string listed in `Markdown.block_level_elements`. A `tag` which is
not a string always returns `False`.
Returns `True` for any string listed in `Markdown.block_level_elements`.
Also returns `True` for valid custom element names (tag names that contain
a hyphen; see https://html.spec.whatwg.org/#valid-custom-element-name).
A `tag` which is not a string always returns `False`.

"""
if isinstance(tag, str):
return tag.lower().rstrip('/') in self.block_level_elements
tag = tag.lower().rstrip('/')
return tag in self.block_level_elements or '-' in tag
# Some ElementTree tags are not strings, so return False.
return False

Expand Down
21 changes: 18 additions & 3 deletions markdown/extensions/md_in_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@
from markdown import Markdown


class _BlockLevelTagSet(set):
"""A set of block-level tags that also treats custom element names as members.

Custom elements are required to contain a hyphen in their names.
See https://html.spec.whatwg.org/#valid-custom-element-name.
"""

def __contains__(self, item: object) -> bool:
return super().__contains__(item) or (isinstance(item, str) and '-' in item)


class HTMLExtractorExtra(HTMLExtractor):
"""
Override `HTMLExtractor` and create `etree` `Elements` for any elements which should have content parsed as
Expand All @@ -52,9 +63,13 @@ def __init__(self, md: Markdown, *args, **kwargs):

super().__init__(md, *args, **kwargs)

# Block-level tags in which the content gets parsed as blocks
self.block_tags = set(self.block_level_tags) - (self.span_tags | self.raw_tags | self.empty_tags)
self.span_and_blocks_tags = self.block_tags | self.span_tags
# Block-level tags in which the content gets parsed as blocks.
# Custom element names (tags with a hyphen) are treated as block-level.
self.block_level_tags = _BlockLevelTagSet(self.block_level_tags)
self.block_tags = _BlockLevelTagSet(
set(self.block_level_tags) - (self.span_tags | self.raw_tags | self.empty_tags)
)
self.span_and_blocks_tags = _BlockLevelTagSet(self.block_tags | self.span_tags)

def reset(self):
"""Reset this instance. Loses all unprocessed data."""
Expand Down
24 changes: 24 additions & 0 deletions tests/test_syntax/blocks/test_html_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,30 @@ def test_raw_uppercase_multiline(self):
)
)

def test_raw_custom_element(self):
# Custom element names contain a hyphen and are treated as block-level.
# https://github.com/Python-Markdown/markdown/issues/1246
self.assertMarkdownRenders(
self.dedent(
"""
<a-b>

asdf

</a-b>
"""
),
self.dedent(
"""
<a-b>

asdf

</a-b>
"""
)
)

def test_multiple_raw_single_line(self):
self.assertMarkdownRenders(
'<p>*foo*</p><div>*bar*</div>',
Expand Down
42 changes: 42 additions & 0 deletions tests/test_syntax/extensions/test_md_in_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,48 @@ def test_noname_tag(self):
)
)

def test_custom_element_markdown_attr(self):
# Custom element names contain a hyphen and are treated as block-level.
# https://github.com/Python-Markdown/markdown/issues/1246
self.assertMarkdownRenders(
self.dedent(
"""
<a-b markdown>

asdf

</a-b>
"""
),
self.dedent(
"""
<a-b>
<p>asdf</p>
</a-b>
"""
)
)

def test_custom_element_md1(self):
# https://github.com/Python-Markdown/markdown/issues/1246
self.assertMarkdownRenders(
'<a-b markdown="1">*foo*</a-b>',
self.dedent(
"""
<a-b>
<p><em>foo</em></p>
</a-b>
"""
)
)

def test_custom_element_inline(self):
# Inline custom elements (not at the start of a block) remain span-level.
self.assertMarkdownRenders(
'hello <my-icon></my-icon> world',
'<p>hello <my-icon></my-icon> world</p>'
)


def load_tests(loader, tests, pattern):
""" Ensure `TestHTMLBlocks` doesn't get run twice by excluding it here. """
Expand Down