diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md
index 03ae5319b..f79007c78 100644
--- a/docs/src/markdown/about/changelog.md
+++ b/docs/src/markdown/about/changelog.md
@@ -1,5 +1,9 @@
# Changelog
+## 10.11
+
+- **NEW**: SuperFences: Allow fenced code to be parsed in the form ` ```lang {.class #id} `.
+
## 10.10.2
- **FIX**: BetterEm: Add better support for `*em, **em,strong***` and `_em, __em,strong___` cases.
diff --git a/docs/src/markdown/extensions/superfences.md b/docs/src/markdown/extensions/superfences.md
index 872c6f29f..33a7e5383 100644
--- a/docs/src/markdown/extensions/superfences.md
+++ b/docs/src/markdown/extensions/superfences.md
@@ -89,21 +89,36 @@ md = markdown.Markdown(extensions=['pymdownx.superfences'])
## Injecting Classes, IDs, and Attributes
-You can use the brace format to specify classes and IDs. The first provided class is always used as the language class.
-IDs (`#id`) can also be inserted as well. Arbitrary attributes in the form `key="value"` can be inserted as well if
-the [`attr_list`][attr-list] extension is enabled, but when using Pygments, only `key="value"` attributes that start
-with the `data-` prefix will be recognized, all others will be treated as options for Pygments and will be rejected if
-not valid.
+You can use the brace format to specify classes and IDs where IDs use the form `#id` and classes use the form `.class`.
+Arbitrary attributes in the form `key="value"` can be inserted as well if the [`attr_list`][attr-list] extension is
+enabled, but when using Pygments, only `key="value"` attributes that start with the `data-` prefix will be recognized,
+all others will be treated as options for Pygments and will be rejected if not valid.
````text title="Injecting Classes"
-```{.python .extra-class linenums="1"}
+```python {.extra-class #id linenums="1"}
import hello_world
```
````
/// html | div.result
```html
-
+
+```
+///
+
+Alternatively, if a language is not provided, the first class is assumed to specify the language.
+
+````text title="Injecting Classes"
+```{.python .extra-class #id linenums="1"}
+import hello_world
+```
+````
+
+/// html | div.result
+```html
+
```
///
@@ -111,7 +126,7 @@ When generating additional classes on a JavaScript style code block (non-Pygment
the `#!html code` block.
````text title="Non-Pygments Injecting Classes"
-```{.python .extra-class #id linenums="1"}
+```python {.extra-class #id linenums="1"}
import hello_world
```
````
@@ -206,13 +221,13 @@ sign and the value must be quoted. Valid line numbers are n > 0. If `linenums`
control the starting line shown in the block.
````text title="Line Numbers"
-```{.python linenums="1"}
+```python {linenums="1"}
import foo.bar
```
````
/// html | div.result
-```{.python linenums="1"}
+```python {linenums="1"}
import foo.bar
```
///
@@ -221,13 +236,13 @@ import foo.bar
And, if we wanted to start with a different starting line number, we would just specify something other than `1`.
````text title="Custom Line Number Start"
-```{.python linenums="2"}
+```python {linenums="2"}
import foo.bar
```
````
/// html | div.result
-```{.python linenums="2"}
+```python {linenums="2"}
import foo.bar
```
///
@@ -241,7 +256,7 @@ So to set showing only every other line number, we could do the following. Line
"line step" is always the second option, so you must specify line start before line step.
````text title="Nth Line"
-``` {.python linenums="1 2"}
+```python {linenums="1 2"}
"""Some file."""
import foo.bar
import boo.baz
@@ -250,7 +265,7 @@ import foo.bar.baz
````
/// html | div.result
-``` {.python linenums="1 2"}
+```python {linenums="1 2"}
"""Some file."""
import foo.bar
import boo.baz
@@ -291,7 +306,7 @@ after the opening tokens (and language if present). The setting is named `hl_li
targeted line numbers separated by spaces.
````text title="Highlight Lines"
-```{.python hl_lines="1 3"}
+```python {hl_lines="1 3"}
"""Some file."""
import foo.bar
import boo.baz
@@ -300,7 +315,7 @@ import foo.bar.baz
````
/// html | div.result
-```{.python hl_lines="1 3"}
+```python {hl_lines="1 3"}
"""Some file."""
import foo.bar
import boo.baz
@@ -312,7 +327,7 @@ import foo.bar.baz
Line numbers are always referenced starting at 1 ignoring what the line number is labeled as when showing line numbers.
````text title="Highlight Lines with Line Numbers"
-```{.py3 hl_lines="1 3" linenums="2"}
+```python {hl_lines="1 3" linenums="2"}
"""Some file."""
import foo.bar
import boo.baz
@@ -321,7 +336,7 @@ import foo.bar.baz
````
/// html | div.result
-```{.py3 hl_lines="1 3" linenums="2"}
+```python {hl_lines="1 3" linenums="2"}
"""Some file."""
import foo.bar
import boo.baz
@@ -333,7 +348,7 @@ If you'd like to do a range of lines, you can use the notation `x-y` where `x` i
ending line. You can do multiple ranges and even mix them with non ranges.
````text title="Highlight Ranges"
-```{.py3 hl_lines="1-2 5 7-8"}
+```python {hl_lines="1-2 5 7-8"}
import foo
import boo.baz
import foo.bar.baz
@@ -347,7 +362,7 @@ class Foo:
````
/// html | div.result
-```{.py3 hl_lines="1-2 5 7-8"}
+```python {hl_lines="1-2 5 7-8"}
import foo
import boo.baz
import foo.bar.baz
@@ -398,7 +413,7 @@ element at the start of the table set to span both the line number column and th
```
````text title="Adding Titles"
-```{.py3 title="My Cool Header"}
+```python {title="My Cool Header"}
import foo.bar
import boo.baz
import foo.bar.baz
@@ -406,7 +421,7 @@ import foo.bar.baz
````
/// html | div.result
-```{.py3 title="My Cool Header"}
+```python {title="My Cool Header"}
import foo.bar
import boo.baz
import foo.bar.baz
@@ -535,7 +550,7 @@ extension_configs = {
/// tab | Markdown
````
-```{.python linenums="1 1" }
+```python {linenums="1 1" }
import foo
```
````
@@ -576,7 +591,7 @@ extension_configs = {
/// tab | Markdown
````
-```{.python linenums="1 1" }
+```python {linenums="1 1" }
import foo
```
````
@@ -705,7 +720,7 @@ conditionally control logic within the formatter. If no validator is defined, th
inputs to `attrs`.
SuperFences will only pass `attrs` to a formatter if an attribute style header is used for a fenced block
-(` ``` {.lang attr="value"}`) and the [`attr_list`][attr-list] extension is enabled. Attribute are not supported in the
+(` ```lang {attr="value"}`) and the [`attr_list`][attr-list] extension is enabled. Attribute are not supported in the
form (` ```lang attr=value`) and will cause the parsing of the fenced block to abort.
Custom options can be used as keys with quoted values (`key="value"`), or as keys with no value (`key`). If a key is
@@ -753,7 +768,7 @@ def custom_format(source, language, class_name, options, md, **kwargs):
This would allow us to use the following custom fence:
````
-```{.test opt="A"}
+```test {opt="A"}
test
```
````
diff --git a/docs/src/markdown/extras/mermaid.md b/docs/src/markdown/extras/mermaid.md
index f052457fe..4399c7150 100644
--- a/docs/src/markdown/extras/mermaid.md
+++ b/docs/src/markdown/extras/mermaid.md
@@ -355,7 +355,7 @@ window.mermaidConfig = {
///
/// tab | JS
-```{.js .md-max-height}
+```js {.md-max-height}
const uml = async className => {
// Custom element to encapsulate Mermaid content.
diff --git a/pymdownx/__meta__.py b/pymdownx/__meta__.py
index 8988d347b..fecd67690 100644
--- a/pymdownx/__meta__.py
+++ b/pymdownx/__meta__.py
@@ -185,5 +185,5 @@ def parse_version(ver, pre=False):
return Version(major, minor, micro, release, pre, post, dev)
-__version_info__ = Version(10, 10, 2, "final")
+__version_info__ = Version(10, 11, 0, "final")
__version__ = __version_info__._get_canonical()
diff --git a/pymdownx/superfences.py b/pymdownx/superfences.py
index 8ca5f831d..b37f8f580 100644
--- a/pymdownx/superfences.py
+++ b/pymdownx/superfences.py
@@ -43,15 +43,16 @@
RE_NESTED_FENCE_START = re.compile(
r'''(?x)
- (?P~{3,}|`{3,})[ \t]* # Fence opening
- (?:(\{(?P[^\n]*)\})?| # Optional attributes or
- (?:\.?(?P[\w#.+-]*))?[ \t]* # Language
+ (?P~{3,}|`{3,})
+ (?:[ \t]*\.?(?P[\w#.+-]+))? # Language
+ (?:
+ [ \t]*(\{(?P[^\n]*)\}) | # Optional attributes or
(?P
(?:
- (?:\b[a-zA-Z][a-zA-Z0-9_]*(?:=(?P"|').*?(?P=quot))?[ \t]*) | # Options
- )*
+ (?:[ \t]*[a-zA-Z][a-zA-Z0-9_]*(?:=(?P"|').*?(?P=quot))?) # Options
+ )+
)
- )[ \t]*$
+ )?[ \t]*$
'''
)
@@ -639,7 +640,10 @@ def handle_attrs(self, m):
else:
values[k] = v
- self.lang = self.classes.pop(0) if self.classes else ''
+ if m.group('lang'):
+ self.lang = m.group('lang')
+ else:
+ self.lang = self.classes.pop(0) if self.classes else ''
# Run per language validator
for entry in reversed(self.extension.superfences):
diff --git a/tests/test_extensions/test_superfences.py b/tests/test_extensions/test_superfences.py
index 17a8fba7e..50dfe11c7 100644
--- a/tests/test_extensions/test_superfences.py
+++ b/tests/test_extensions/test_superfences.py
@@ -664,6 +664,22 @@ def test_attr(self):
True
)
+ def test_attrs_alternate_form(self):
+ """Test attributes with new alternate form."""
+
+ self.check_markdown(
+ r'''
+ ```python {.test .class #class}
+ import test
+ ```
+ ''',
+ r'''
+
+ ''', # noqa: E501
+ True
+ )
+
class TestSuperFencesClassesIdsAttrList(util.MdCase):
"""Test fence ids and classes with attribute lists."""
@@ -767,6 +783,22 @@ def test_data_attr_linenums(self):
True
)
+ def test_attrs_alternate_form(self):
+ """Test attributes with new alternate form."""
+
+ self.check_markdown(
+ r'''
+ ```python {.test .class #id data-attr="test" linenums="1"}
+ import test
+ ```
+ ''',
+ r'''
+
+ ''', # noqa: E501
+ True
+ )
+
class TestSuperFencesClassesIdsAttrListNoPygments(util.MdCase):
"""Test fence ids and classes with attribute lists and with no Pygments."""
@@ -929,7 +961,7 @@ def test_bad_option_value(self):
class TestSuperFencesCustom(util.MdCase):
"""Test custom validator and format."""
- extension = ['pymdownx.superfences']
+ extension = ['pymdownx.superfences', 'attr_list']
extension_configs = {
'pymdownx.superfences': {
'custom_fences': [
@@ -1026,6 +1058,21 @@ def test_custom_options(self):
True
)
+ def test_custom_options_attr_list(self):
+ """Test option with correct value."""
+
+ self.check_markdown(
+ r'''
+ ```test {opt="A"}
+ test
+ ```
+ ''',
+ r'''
+ test
+ ''',
+ True
+ )
+
class TestSuperFencesCustomException(util.MdCase):
"""Test custom validator and format."""