Skip to content

Commit

Permalink
Add support to handle custom HTML tags
Browse files Browse the repository at this point in the history
  • Loading branch information
facelessuser committed Jan 7, 2025
1 parent dc2cda9 commit 45005c4
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 3 deletions.
6 changes: 6 additions & 0 deletions docs/src/markdown/about/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 10.14

- **NEW**: Blocks.HTML: Add new `custom` option to specify tags and the assumed handling for them when automatic mode
is assumed. This can also be used to override the handling for recognized tags with automatic handling.
- **FIX**: Fix tests to pass with Pygments 2.19+.

## 10.13

- **NEW**: Snippets: Allow multiple line numbers or line number blocks separated by `,`.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/markdown/extensions/blocks/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ Result\ Value | Description
When using `raw` mode, all text will be accumulated under the specified element as an [`AtomicString`][atomic]. If
nothing is done with the content during the [`on_end` event](#on_end-event), all the content will be HTML escaped by the
Python Markdown parser. If desired, the content can be placed into the Python Markdown [HTML stash][stash] which will
protect it from any other rouge Treeprocessors. Keep in mind, if the content is stashed HTML escaping will not be
protect it from any other rouge Treeprocessors. Keep in mind, if the content is stashed, HTML escaping will not be
applied automatically, so HTML escape if it is required.

/// warning | Indent Raw Content
Expand Down
43 changes: 43 additions & 0 deletions docs/src/markdown/extensions/blocks/plugins/html.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,49 @@ some *markdown* content
////
///

## Custom Tag Handling

It is possible that in the future new tags could be added to the HTML spec, and it is also possible that the extension
may not recognize such tags for some length of time. While such tags can be added in future releases, the HTML extension
allows for users to specify such tags to navigate around such issues, or even make up their own tags.

To teach the HTML extension about unknown tags, simply use the `custom` [option](#global-options) to specify a tag name
and the assumed default `mode`.

```py3
import markdown
from pymdownx.blocks.html import HTML
extensions = ['pymdownx.blocks.html']
extension_configs = {
'pymdown.blocks.html': {
'custom': [
{'tag': 'sometag', 'mode': 'block'}
]
}
}
md = markdown.Markdown(extensions=extensions, extension_configs=extension_configs)
```

Then you can use your custom tag knowing it will be handled properly.

```
/// html | sometag
Some block content.
- List item A.
- List item B.
///
```

## Global Options

Options | Type | Descriptions
-------- | --------------- | ------------
`custom` | \[dictionary\] | Specify custom tags with assumed default handling.

## Per Block Options

Options | Type | Descriptions
Expand Down
2 changes: 1 addition & 1 deletion pymdownx/__meta__.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,5 +185,5 @@ def parse_version(ver, pre=False):
return Version(major, minor, micro, release, pre, post, dev)


__version_info__ = Version(10, 13, 0, "final")
__version_info__ = Version(10, 14, 0, "final")
__version__ = __version_info__._get_canonical()
27 changes: 26 additions & 1 deletion pymdownx/blocks/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
RE_ATTR = re.compile(fr'(?P<attr_name>{IDENTIFIER}){ATTR}', flags=re.I | re.X)

ATTRIBUTES = {'id': RE_ID, 'class': RE_CLASS, 'attr': RE_ATTRS}
VALID_MODES = {'auto', 'inline', 'block', 'raw', 'html'}


def parse_selectors(selector):
Expand Down Expand Up @@ -125,13 +126,17 @@ class HTML(Block):
NAME = 'html'
ARGUMENT = True
OPTIONS = {
'markdown': ['auto', type_string_in(['auto', 'inline', 'block', 'raw', 'html'])]
'markdown': ['auto', type_string_in(VALID_MODES)]
}

def __init__(self, length, tracker, md, config):
"""Initialize."""

self.markdown = None
self.custom = {}
for entry in config.get('custom'):
mode = entry.get('mode', 'auto')
self.custom[entry['tag']] = mode if mode in VALID_MODES else 'auto'
super().__init__(length, tracker, md, config)

def on_validate(self, parent):
Expand All @@ -148,6 +153,10 @@ def on_markdown(self):
"""Check if this is atomic."""

mode = self.options['markdown']
if mode == 'auto':
tag = self.tag.lower()
mode = self.custom.get(tag, mode)

if mode == 'html':
mode = 'raw'
return mode
Expand All @@ -167,6 +176,10 @@ def on_end(self, block):
"""On end event."""

mode = self.options['markdown']
if mode == 'auto':
tag = self.tag.lower()
mode = self.custom.get(tag, mode)

if (mode == 'auto' and self.is_html(block)) or mode == 'html':
block.text = self.md.htmlStash.store(block.text)
elif (mode == 'auto' and self.is_raw(block)) or mode == 'raw':
Expand All @@ -176,6 +189,18 @@ def on_end(self, block):
class HTMLExtension(BlocksExtension):
"""HTML Blocks Extension."""

def __init__(self, *args, **kwargs):
"""Initialize."""

self.config = {
"custom": [
[],
"Specify handling for custom blocks."
]
}

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

def extendMarkdownBlocks(self, md, block_mgr):
"""Extend Markdown blocks."""

Expand Down
42 changes: 42 additions & 0 deletions tests/test_extensions/test_blocks/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ class TestBlocksHTML(util.MdCase):
"""Test Blocks HTML cases."""

extension = ['pymdownx.blocks.html', 'md_in_html']
extension_configs = {
'pymdownx.blocks.html': {
'custom': [
{'tag': 'custom', 'mode': 'block'}
]
}
}

def test_bad_tag(self):
"""Test bad HTML tag."""
Expand Down Expand Up @@ -323,3 +330,38 @@ def test_html_and_script(self):
''',
True
)

def test_custom(self):
"""Test custom block handling."""

self.check_markdown(
R'''
/// html | custom
- a
- b
///
''',
'''
<custom><ul><li>a</li><li>b</li></ul></custom>
''',
True
)

def test_custom_override(self):
"""Test custom block handling but mode is overridden."""

self.check_markdown(
R'''
/// html | custom
markdown: inline
- a
- b
///
''',
'''
<custom>- a
- b</custom>
''',
True
)

0 comments on commit 45005c4

Please sign in to comment.