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 -
1
import import hello_world\n
\n
+
1
import hello_world
+
+``` +/// + +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 +
1
import hello_world
+
``` /// @@ -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''' +
import test
+            
+ ''', # 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''' +
1
import test
+            
+ ''', # 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."""