From 7a9586555807518d349df3d1b35e62d4a721cd7d Mon Sep 17 00:00:00 2001 From: Isaac Muse Date: Wed, 25 Sep 2024 18:22:59 -0600 Subject: [PATCH] Add support for `*tag1 **tag1,tag2***` cases for `*`, `_`, `~`, and `^` (#2467) * Add support for `*em **em,strong***` cases for `*` and `_` Fixes #2466 * Add support for additional single, double cases for tilde and caret Clean up formatting as well * Spelling * Add more tests and move some tests for tilde to new test style * More tests and move some caret tests * Fix minor typo in BetterEm docs --- docs/src/markdown/about/changelog.md | 6 + docs/src/markdown/extensions/betterem.md | 5 +- pymdownx/betterem.py | 64 ++++-- pymdownx/caret.py | 24 ++- pymdownx/mark.py | 4 +- pymdownx/tilde.py | 24 ++- tests/extensions/caret/caret (dumb).html | 10 - tests/extensions/caret/caret (dumb).txt | 19 -- tests/extensions/caret/caret.html | 11 - tests/extensions/caret/caret.txt | 21 -- tests/extensions/caret/tests.yml | 9 - tests/extensions/tilde/tests.yml | 9 - tests/extensions/tilde/tilde (dumb).html | 11 - tests/extensions/tilde/tilde (dumb).txt | 21 -- tests/extensions/tilde/tilde.html | 11 - tests/extensions/tilde/tilde.txt | 21 -- tests/test_extensions/test_betterem.py | 59 +++++- tests/test_extensions/test_caret.py | 249 ++++++++++++++++++++++ tests/test_extensions/test_tilde.py | 258 +++++++++++++++++++++++ 19 files changed, 649 insertions(+), 187 deletions(-) delete mode 100644 tests/extensions/caret/caret (dumb).html delete mode 100644 tests/extensions/caret/caret (dumb).txt delete mode 100644 tests/extensions/caret/caret.html delete mode 100644 tests/extensions/caret/caret.txt delete mode 100644 tests/extensions/tilde/tilde (dumb).html delete mode 100644 tests/extensions/tilde/tilde (dumb).txt delete mode 100644 tests/extensions/tilde/tilde.html delete mode 100644 tests/extensions/tilde/tilde.txt create mode 100644 tests/test_extensions/test_caret.py create mode 100644 tests/test_extensions/test_tilde.py diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md index 3c2caa2a0..03ae5319b 100644 --- a/docs/src/markdown/about/changelog.md +++ b/docs/src/markdown/about/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 10.10.2 + +- **FIX**: BetterEm: Add better support for `*em, **em,strong***` and `_em, __em,strong___` cases. +- **FIX**: Caret: Add better support for `*sup, **sup,ins***`. +- **FIX**: Tilde: Add better support for `*sub, **sub,del***`. + ## 10.10.1 - **FIX**: FancyLists: Remove a mistaken semicolon from injected classes. diff --git a/docs/src/markdown/extensions/betterem.md b/docs/src/markdown/extensions/betterem.md index ec9961a45..6b6b14ee2 100644 --- a/docs/src/markdown/extensions/betterem.md +++ b/docs/src/markdown/extensions/betterem.md @@ -78,8 +78,7 @@ ___A lot of underscores____________is okay___ ___A lot of underscores____________is okay___ /// -BetterEm will also ensure that smart mode breaks proper when an inner like token signifies an end. - +BetterEm will also ensure that smart mode breaks properly when an inner like token signifies an end. ```text title="Smart Break" __This will all be bold __because of the placement of the center underscores.__ @@ -94,7 +93,7 @@ __This will all be bold_ because of the token is less than that of the surroundi /// html | div.result __This will all be bold __because of the placement of the center underscores.__ -__This will all be bold __ because of the placement of the center asterisks.__ +__This will all be bold __ because of the placement of the center underscores.__ __This will NOT all be bold__ because of the placement of the center underscores.__ diff --git a/pymdownx/betterem.py b/pymdownx/betterem.py index 9e9235e15..0277c9b66 100644 --- a/pymdownx/betterem.py +++ b/pymdownx/betterem.py @@ -54,55 +54,71 @@ # __strong_em,strong___ UNDER_STRONG_EM3 = r'(_{{2}})(?![\s_]){}_(?![\s_]){}(?x2 + y2 = 4

-

Textsuperscript

-

Text^superscript failed^

-

Textsuperscript success

-

Test: ^^ Won't insert ^^

-

Test: Will insert

-

Test: ^^Escaped^^

-

Test: All will ^ be insert

-

Test: All will^ be insert with superscript in middle

-

Test: All will ^ be insert with superscript in middle

\ No newline at end of file diff --git a/tests/extensions/caret/caret (dumb).txt b/tests/extensions/caret/caret (dumb).txt deleted file mode 100644 index 443335f65..000000000 --- a/tests/extensions/caret/caret (dumb).txt +++ /dev/null @@ -1,19 +0,0 @@ -x^2^ + y^2^ = 4 - -Text^superscript^ - -Text^superscript failed^ - -Text^superscript\ success^ - -Test: ^^ Won't insert ^^ - -Test: ^^Will insert^^ - -Test: \^\^Escaped\^\^ - -Test: ^^All will ^ be insert^^ - -Test: ^^All will^\^^ be insert with superscript in middle^^ - -Test: ^^All will ^\^^ be insert with superscript in middle^^ diff --git a/tests/extensions/caret/caret.html b/tests/extensions/caret/caret.html deleted file mode 100644 index 9478ca46d..000000000 --- a/tests/extensions/caret/caret.html +++ /dev/null @@ -1,11 +0,0 @@ -

x2 + y2 = 4

-

Textsuperscript

-

Text^superscript failed^

-

Textsuperscript success

-

Test: ^^ Won't insert ^^

-

Test: Will insert

-

Test: ^^Escaped^^

-

Test: This will all be inserted ^^because of the placement of the center carets.

-

Test: This will all be inserted ^^ because of the placement of the center carets.

-

Test: This will NOT all be inserted because of the placement of the center caret.^^

-

Test: This will all be inserted^ because of the token is less than that of the caret.

\ No newline at end of file diff --git a/tests/extensions/caret/caret.txt b/tests/extensions/caret/caret.txt deleted file mode 100644 index a92f63502..000000000 --- a/tests/extensions/caret/caret.txt +++ /dev/null @@ -1,21 +0,0 @@ -x^2^ + y^2^ = 4 - -Text^superscript^ - -Text^superscript failed^ - -Text^superscript\ success^ - -Test: ^^ Won't insert ^^ - -Test: ^^Will insert^^ - -Test: \^\^Escaped\^\^ - -Test: ^^This will all be inserted ^^because of the placement of the center carets.^^ - -Test: ^^This will all be inserted ^^ because of the placement of the center carets.^^ - -Test: ^^This will NOT all be inserted^^ because of the placement of the center caret.^^ - -Test: ^^This will all be inserted^ because of the token is less than that of the caret.^^ diff --git a/tests/extensions/caret/tests.yml b/tests/extensions/caret/tests.yml index 7f6efa6d9..401fab836 100644 --- a/tests/extensions/caret/tests.yml +++ b/tests/extensions/caret/tests.yml @@ -1,14 +1,5 @@ __default__: {} -caret: - extensions: - pymdownx.caret: - -caret (dumb): - extensions: - pymdownx.caret: - smart_insert: false - caret (dumb no sup): extensions: pymdownx.caret: diff --git a/tests/extensions/tilde/tests.yml b/tests/extensions/tilde/tests.yml index 7a70bacdb..7ef840d8d 100644 --- a/tests/extensions/tilde/tests.yml +++ b/tests/extensions/tilde/tests.yml @@ -1,14 +1,5 @@ __default__: {} -tilde: - extensions: - pymdownx.tilde: - -tilde (dumb): - extensions: - pymdownx.tilde: - smart_delete: false - tilde (dumb no sub): extensions: pymdownx.tilde: diff --git a/tests/extensions/tilde/tilde (dumb).html b/tests/extensions/tilde/tilde (dumb).html deleted file mode 100644 index 9282fbc1a..000000000 --- a/tests/extensions/tilde/tilde (dumb).html +++ /dev/null @@ -1,11 +0,0 @@ -

CH3CH2OH

-

Textsubscript

-

Text~subscript failed~

-

Textsubscript success

-

Test: ~~ Won't delete ~~

-

Test: Will delete

-

Test: ~~Escaped~~

-

Test: All will ~ be deleted

-

Test: All will~ be deleted with subscript in middle

-

Test: All will ~ be deleted with subscript in middle

-

Test: Subscript ~~~

\ No newline at end of file diff --git a/tests/extensions/tilde/tilde (dumb).txt b/tests/extensions/tilde/tilde (dumb).txt deleted file mode 100644 index 725c09c75..000000000 --- a/tests/extensions/tilde/tilde (dumb).txt +++ /dev/null @@ -1,21 +0,0 @@ -CH~3~CH~2~OH - -Text~subscript~ - -Text~subscript failed~ - -Text~subscript\ success~ - -Test: ~~ Won't delete ~~ - -Test: ~~Will delete~~ - -Test: \~\~Escaped\~\~ - -Test: ~~All will ~ be deleted~~ - -Test: ~~All will~\~~ be deleted with subscript in middle~~ - -Test: ~~All will ~\~~ be deleted with subscript in middle~~ - -Test: Subscript ~~~ diff --git a/tests/extensions/tilde/tilde.html b/tests/extensions/tilde/tilde.html deleted file mode 100644 index 8c7af71e9..000000000 --- a/tests/extensions/tilde/tilde.html +++ /dev/null @@ -1,11 +0,0 @@ -

CH3CH2OH

-

Textsubscript

-

Text~subscript failed~

-

Textsubscript success

-

Test: ~~ Won't delete ~~

-

Test: Will delete

-

Test: ~~Escaped~~

-

Test: This will all be deleted ~~because of the placement of the center tilde.

-

Test: This will all be deleted ~~ because of the placement of the center tilde.

-

Test: This will NOT all be deleted because of the placement of the center tilde.~~

-

Test: This will all be deleted~ because of the token is less than that of the tilde.

\ No newline at end of file diff --git a/tests/extensions/tilde/tilde.txt b/tests/extensions/tilde/tilde.txt deleted file mode 100644 index 76a366db8..000000000 --- a/tests/extensions/tilde/tilde.txt +++ /dev/null @@ -1,21 +0,0 @@ -CH~3~CH~2~OH - -Text~subscript~ - -Text~subscript failed~ - -Text~subscript\ success~ - -Test: ~~ Won't delete ~~ - -Test: ~~Will delete~~ - -Test: \~\~Escaped\~\~ - -Test: ~~This will all be deleted ~~because of the placement of the center tilde.~~ - -Test: ~~This will all be deleted ~~ because of the placement of the center tilde.~~ - -Test: ~~This will NOT all be deleted~~ because of the placement of the center tilde.~~ - -Test: ~~This will all be deleted~ because of the token is less than that of the tilde.~~ diff --git a/tests/test_extensions/test_betterem.py b/tests/test_extensions/test_betterem.py index 5e1cbe533..a2a73f1a8 100644 --- a/tests/test_extensions/test_betterem.py +++ b/tests/test_extensions/test_betterem.py @@ -2,7 +2,7 @@ from .. import util -class TestBetterEmNoSmart(util.MdCase): +class TestBetterNoSmart(util.MdCase): """Test escaping cases for BetterEm without smart enabled.""" extension = [ @@ -46,10 +46,43 @@ def test_complex_multple_underscore_type_variant2(self): '

on the 1-4 row of the AP Combat Table and receive

' ) + def test_complex_em_strong_star(self): + """Test `*text **text***`.""" + + self.check_markdown( + "*I'm italic. **I'm bold and italic.***", + "

I'm italic. I'm bold and italic.

" + ) + + def test_complex_em_strong_under(self): + """Test `_text __text___`.""" + + self.check_markdown( + "_I'm italic. __I'm bold and italic.___", + "

I'm italic. I'm bold and italic.

" + ) + + def test_complex_strong_em_under(self): + """Test `__text _text___`.""" + + self.check_markdown( + "__I'm bold. _I'm bold and italic.___", + "

I'm bold. I'm bold and italic.

" + ) + class TestBetterSmartAll(util.MdCase): """Test escaping cases for BetterEm with smart enabled everywhere.""" + extension = [ + 'pymdownx.betterem' + ] + extension_configs = { + "pymdownx.betterem": { + "smart_enable": "all" + } + } + def test_smart_complex_cases_star(self): """Test some complex cases with star.""" @@ -81,3 +114,27 @@ def test_smart_complex_cases_underscore(self): ''', True ) + + def test_smart_complex_em_strong_star(self): + """Test `*text **text***`.""" + + self.check_markdown( + "*I'm italic. **I'm bold and italic.***", + "

I'm italic. I'm bold and italic.

" + ) + + def test_smart_complex_em_strong_under(self): + """Test `_text __text___`.""" + + self.check_markdown( + "_I'm italic. __I'm bold and italic.___", + "

I'm italic. I'm bold and italic.

" + ) + + def test_complex_strong_em_under(self): + """Test `__text _text___`.""" + + self.check_markdown( + "__I'm bold. _I'm bold and italic.___", + "

I'm bold. I'm bold and italic.

" + ) diff --git a/tests/test_extensions/test_caret.py b/tests/test_extensions/test_caret.py new file mode 100644 index 000000000..3b642c9dd --- /dev/null +++ b/tests/test_extensions/test_caret.py @@ -0,0 +1,249 @@ +"""Test caret.""" +from .. import util + + +class TestCaretSmart(util.MdCase): + """Test escaping cases for Caret with smart enabled.""" + + extension = [ + 'pymdownx.caret' + ] + extension_configs = { + "pymdownx.caret": { + "smart_insert": True + } + } + + def test_case_1(self): + """Test case 1.""" + + self.check_markdown( + R"x^2^ + y^2^ = 4", + "

x2 + y2 = 4

", + True + ) + + def test_case_2(self): + """Test case 2.""" + + self.check_markdown( + R"Text^superscript^", + "

Textsuperscript

", + True + ) + + def test_case_3(self): + """Test case 3.""" + + self.check_markdown( + R"Text^superscript failed^", + "

Text^superscript failed^

", + True + ) + + def test_case_4(self): + """Test case 4.""" + + self.check_markdown( + R"Text^superscript\ success^", + "

Textsuperscript success

", + True + ) + + def test_case_5(self): + """Test case 5.""" + + self.check_markdown( + R"Test: ^^ Won't insert ^^", + "

Test: ^^ Won't insert ^^

", + True + ) + + def test_case_6(self): + """Test case 6.""" + + self.check_markdown( + R"Test: ^^Will insert^^", + "

Test: Will insert

", + True + ) + + def test_case_7(self): + """Test case 7.""" + + self.check_markdown( + R"Test: \^\^Escaped\^\^", + "

Test: ^^Escaped^^

", + True + ) + + def test_case_8(self): + """Test case 8.""" + + self.check_markdown( + R"Test: ^^This will all be inserted ^^because of the placement of the center carets.^^", + "

Test: This will all be inserted ^^because of the placement of the center carets.

", + True + ) + + def test_case_9(self): + """Test case 9.""" + + self.check_markdown( + R"Test: ^^This will all be inserted ^^ because of the placement of the center carets.^^", + "

Test: This will all be inserted ^^ because of the placement of the center carets.

", + True + ) + + def test_case_10(self): + """Test case 10.""" + + self.check_markdown( + R"Test: ^^This will NOT all be inserted^^ because of the placement of the center caret.^^", + "

Test: This will NOT all be inserted because of the placement of the center caret.^^

", + True + ) + + def test_case_11(self): + """Test case 11.""" + + self.check_markdown( + R"Test: ^^This will all be inserted^ because of the token is less than that of the caret.^^", + "

Test: This will all be inserted^ because of the token is less than that of the caret.

", + True + ) + + def test_complex_sup_ins_under(self): + """Test `^text ^^text^^^`.""" + + self.check_markdown( + R"^I'm\ sup.\ ^^I'm\ sup\ and\ insert.^^^", + "

I'm sup. I'm sup and insert.

" + ) + + def test_complex_del_sup_inser(self): + """Test `^^text ^text^^^`.""" + + self.check_markdown( + R"^^I'm insert. ^I'm\ sup\ and\ insert.^^^", + "

I'm insert. I'm sup and insert.

" + ) + + +class TestCaretNoSmart(util.MdCase): + """Test escaping cases for Caret without smart enabled.""" + + extension = [ + 'pymdownx.caret' + ] + extension_configs = { + "pymdownx.caret": { + "smart_insert": False + } + } + + def test_case_1(self): + """Test case 1.""" + + self.check_markdown( + R"x^2^ + y^2^ = 4", + "

x2 + y2 = 4

", + True + ) + + def test_case_2(self): + """Test case 2.""" + + self.check_markdown( + R"Text^superscript^", + "

Textsuperscript

", + True + ) + + def test_case_3(self): + """Test case 3.""" + + self.check_markdown( + R"Text^superscript failed^", + "

Text^superscript failed^

", + True + ) + + def test_case_4(self): + """Test case 4.""" + + self.check_markdown( + R"Text^superscript\ success^", + "

Textsuperscript success

", + True + ) + + def test_case_5(self): + """Test case 5.""" + + self.check_markdown( + R"Test: ^^ Won't insert ^^", + "

Test: ^^ Won't insert ^^

", + True + ) + + def test_case_6(self): + """Test case 6.""" + + self.check_markdown( + R"Test: ^^Will insert^^", + "

Test: Will insert

", + True + ) + + def test_case_7(self): + """Test case 7.""" + + self.check_markdown( + R"Test: \^\^Escaped\^\^", + "

Test: ^^Escaped^^

", + True + ) + + def test_case_8(self): + """Test case 8.""" + + self.check_markdown( + R"Test: ^^All will ^ be insert^^", + "

Test: All will ^ be insert

", + True + ) + + def test_case_9(self): + """Test case 9.""" + + self.check_markdown( + R"Test: ^^All will^\^^ be insert with superscript in middle^^", + "

Test: All will^ be insert with superscript in middle

", + True + ) + + def test_case_10(self): + """Test case 10.""" + + self.check_markdown( + R"Test: ^^All will ^\^^ be insert with superscript in middle^^", + "

Test: All will ^ be insert with superscript in middle

", + True + ) + + def test_complex_sup_ins_under(self): + """Test `^text ^^text^^^`.""" + + self.check_markdown( + R"^I'm\ sup.\ ^^I'm\ sup\ and\ insert.^^^", + "

I'm sup. I'm sup and insert.

" + ) + + def test_complex_del_sup_inser(self): + """Test `^^text ^text^^^`.""" + + self.check_markdown( + R"^^I'm insert. ^I'm\ sup\ and\ insert.^^^", + "

I'm insert. I'm sup and insert.

" + ) diff --git a/tests/test_extensions/test_tilde.py b/tests/test_extensions/test_tilde.py new file mode 100644 index 000000000..5eb06498e --- /dev/null +++ b/tests/test_extensions/test_tilde.py @@ -0,0 +1,258 @@ +"""Test tilde.""" +from .. import util + + +class TestTildeSmart(util.MdCase): + """Test escaping cases for Tilde with smart enabled.""" + + extension = [ + 'pymdownx.tilde' + ] + extension_configs = { + "pymdownx.tilde": { + "smart_delete": True + } + } + + def test_case_1(self): + """Test case 1.""" + + self.check_markdown( + R"CH~3~CH~2~OH", + "

CH3CH2OH

", + True + ) + + def test_case_2(self): + """Test case 2.""" + + self.check_markdown( + R"Text~subscript~", + "

Textsubscript

", + True + ) + + def test_case_3(self): + """Test case 3.""" + + self.check_markdown( + R"Text~subscript failed~", + "

Text~subscript failed~

", + True + ) + + def test_case_4(self): + """Test case 4.""" + + self.check_markdown( + R"Text~subscript\ success~", + "

Textsubscript success

", + True + ) + + def test_case_5(self): + """Test case 5.""" + + self.check_markdown( + R"Test: ~~ Won't delete ~~", + "

Test: ~~ Won't delete ~~

", + True + ) + + def test_case_6(self): + """Test case 6.""" + + self.check_markdown( + R"Test: ~~Will delete~~", + "

Test: Will delete

", + True + ) + + def test_case_7(self): + """Test case 7.""" + + self.check_markdown( + R"Test: \~\~Escaped\~\~", + "

Test: ~~Escaped~~

", + True + ) + + def test_case_8(self): + """Test case 8.""" + + self.check_markdown( + R"Test: ~~This will all be deleted ~~because of the placement of the center tilde.~~", + "

Test: This will all be deleted ~~because of the placement of the center tilde.

", + True + ) + + def test_case_9(self): + """Test case 9.""" + + self.check_markdown( + R"Test: ~~This will all be deleted ~~ because of the placement of the center tilde.~~", + "

Test: This will all be deleted ~~ because of the placement of the center tilde.

", + True + ) + + def test_case_10(self): + """Test case 10.""" + + self.check_markdown( + R"Test: ~~This will NOT all be deleted~~ because of the placement of the center tilde.~~", + "

Test: This will NOT all be deleted because of the placement of the center tilde.~~

", + True + ) + + def test_case_11(self): + """Test case 11.""" + + self.check_markdown( + R"Test: ~~This will all be deleted~ because of the token is less than that of the tilde.~~", + "

Test: This will all be deleted~ because of the token is less than that of the tilde.

", + True + ) + + def test_complex_sub_del_under(self): + """Test `~text ~~text~~~`.""" + + self.check_markdown( + R"~I'm\ sub.\ ~~I'm\ sub\ and\ delete.~~~", + "

I'm sub. I'm sub and delete.

" + ) + + def test_complex_del_sub_under(self): + """Test `~~text ~text~~~`.""" + + self.check_markdown( + R"~~I'm delete. ~I'm\ sub\ and\ delete.~~~", + "

I'm delete. I'm sub and delete.

" + ) + + +class TestTildeNoSmart(util.MdCase): + """Test escaping cases for Tilde without smart enabled.""" + + extension = [ + 'pymdownx.tilde' + ] + extension_configs = { + "pymdownx.tilde": { + "smart_delete": False + } + } + + def test_case_1(self): + """Test case 1.""" + + self.check_markdown( + R"CH~3~CH~2~OH", + "

CH3CH2OH

", + True + ) + + def test_case_2(self): + """Test case 2.""" + + self.check_markdown( + R"Text~subscript~", + "

Textsubscript

", + True + ) + + def test_case_3(self): + """Test case 3.""" + + self.check_markdown( + R"Text~subscript failed~", + "

Text~subscript failed~

", + True + ) + + def test_case_4(self): + """Test case 4.""" + + self.check_markdown( + R"Text~subscript\ success~", + "

Textsubscript success

", + True + ) + + def test_case_5(self): + """Test case 5.""" + + self.check_markdown( + R"Test: ~~ Won't delete ~~", + "

Test: ~~ Won't delete ~~

", + True + ) + + def test_case_6(self): + """Test case 6.""" + + self.check_markdown( + R"Test: ~~Will delete~~", + "

Test: Will delete

", + True + ) + + def test_case_7(self): + """Test case 7.""" + + self.check_markdown( + R"Test: \~\~Escaped\~\~", + "

Test: ~~Escaped~~

", + True + ) + + def test_case_8(self): + """Test case 8.""" + + self.check_markdown( + R"Test: ~~All will ~ be deleted~~", + "

Test: All will ~ be deleted

", + True + ) + + def test_case_9(self): + """Test case 9.""" + + self.check_markdown( + R"Test: ~~All will~\~~ be deleted with subscript in middle~~", + "

Test: All will~ be deleted with subscript in middle

", + True + ) + + def test_case_10(self): + """Test case 10.""" + + self.check_markdown( + R"Test: ~~All will ~\~~ be deleted with subscript in middle~~", + "

Test: All will ~ be deleted with subscript in middle

", + True + ) + + def test_case_11(self): + """Test case 11.""" + + self.check_markdown( + R"Test: Subscript ~~~", + "

Test: Subscript ~~~

", + True + ) + + def test_complex_sub_del_under(self): + """Test `~text ~~text~~~`.""" + + self.check_markdown( + R"~I'm\ sub.\ ~~I'm\ sub\ and\ delete.~~~", + "

I'm sub. I'm sub and delete.

" + ) + + def test_complex_del_sub_under(self): + """Test `~~text ~text~~~`.""" + + self.check_markdown( + R"~~I'm delete. ~I'm\ sub\ and\ delete.~~~", + "

I'm delete. I'm sub and delete.

" + )