From f6102ac0dca1756a1bd052277c44af29887edace Mon Sep 17 00:00:00 2001 From: Isaac Muse Date: Sat, 7 Dec 2024 14:26:19 -0700 Subject: [PATCH] Fix some complex cases in BetterEm (#2547) * Fix some complex cases in BetterEm - Fix some cases where mixed triple cases followed by single case could sometimes fail. - Fix cases where reverse, mixed triple cases could fail. * Remove redundant tests --- docs/src/markdown/about/changelog.md | 1 + pymdownx/betterem.py | 62 ++++--- pymdownx/caret.py | 15 +- pymdownx/tilde.py | 15 +- tests/test_extensions/test_betterem.py | 224 ++++++++++++++++++++----- tests/test_extensions/test_caret.py | 118 ++++++++++--- tests/test_extensions/test_tilde.py | 118 ++++++++++--- 7 files changed, 440 insertions(+), 113 deletions(-) diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md index acab38904..d50e6859a 100644 --- a/docs/src/markdown/about/changelog.md +++ b/docs/src/markdown/about/changelog.md @@ -3,6 +3,7 @@ ## 10.12.1 - **FIX**: Snippets: Fix issue where when non sections of files are included, section labels are not stripped. +- **FIX**: BetterEm: Fixes for complex cases. ## 10.12 diff --git a/pymdownx/betterem.py b/pymdownx/betterem.py index 0277c9b66..f6a4e4b0d 100644 --- a/pymdownx/betterem.py +++ b/pymdownx/betterem.py @@ -29,6 +29,8 @@ SMART_UNDER_CONTENT = r'(.+?_*?)' SMART_STAR_CONTENT = r'(.+?\**?)' +SMART_STAR_LIMITED_CONTENT = r'((?:[^\*]|(?<=\w)\*+?(?=\w)|(?<=\s)\*+?(?=\s))+?)' +SMART_UNDER_LIMITED_CONTENT = r'((?:[^_]|(?<=[^\W_])_+?(?=[^\W_])|(?<=\s)_+?(?=\s))+?)' UNDER_CONTENT = r'(_|(?:(?<=\s)_|[^_])+?)' UNDER_CONTENT2 = r'((?:[^_]|(?on the 1-4 row of the AP Combat Table and receive

' ) - def test_complex_em_strong_star(self): - """Test `*text **text***`.""" + def test_complex_cases_star(self): + """Test some complex cases for asterisks.""" self.check_markdown( - "*I'm italic. **I'm bold and italic.***", - "

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

" - ) + ''' + ***I'm italic and bold* I am just bold.** - def test_complex_em_strong_under(self): - """Test `_text __text___`.""" + ***I'm bold and italic!** I am just italic.* - self.check_markdown( - "_I'm italic. __I'm bold and italic.___", - "

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

" + *italic and **italic bold*** and *italic* + + **bold and *italic bold*** and *italic* + + ***I'm italic and bold* I am just bold.** *italic* + + ***I'm bold and italic!** I am just italic.* *italic* + + *italic and **italic bold*** and italic* + + **bold and *italic bold*** and bold* + + *italic and **italic bold*** + + **bold and *italic bold*** + + *italic **italic bold** italic* + + ***italic and bold* bold**: foo bar **italic** + + ***italic and bold** italic* foo bar **italic** + + *italic and **italic bold*** **italic** + + **bold and *italic bold*** **italic** + ''', + ''' +

I'm italic and bold I am just bold.

+

I'm bold and italic! I am just italic.

+

italic and italic bold and italic

+

bold and italic bold and italic

+

I'm italic and bold I am just bold. italic

+

I'm bold and italic! I am just italic. italic

+

italic and italic bold and italic*

+

bold and italic bold and bold*

+

italic and italic bold

+

bold and italic bold

+

italic italic bold italic

+

italic and bold bold: foo bar italic

+

italic and bold italic foo bar italic

+

italic and italic bold italic

+

bold and italic bold italic

+ ''', + True ) - def test_complex_strong_em_under(self): - """Test `__text _text___`.""" + def test_complex_cases_underscore(self): + """Test some complex cases for underscore.""" self.check_markdown( - "__I'm bold. _I'm bold and italic.___", - "

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

" + ''' + ___I'm italic and bold_ I am just bold.__ + + ___I'm bold and italic!__ I am just italic._ + + _italic and __italic bold___ and _italic_ + + __bold and _italic bold___ and _italic_ + + ___I'm italic and bold_ I am just bold.__ _italic_ + + ___I'm bold and italic!__ I am just italic._ _italic_ + + _italic and __italic bold___ and italic_ + + __bold and _italic bold___ and bold_ + + _italic and __italic bold___ + + __bold and _italic bold___ + + _italic __italic bold__ italic_ + + ___italic and bold_ bold__: foo bar __italic__ + + ___italic and bold__ italic_ foo bar __italic__ + + _italic and __italic bold___ __italic__ + + __bold and _italic bold___ __italic__ + ''', + ''' +

I'm italic and bold I am just bold.

+

I'm bold and italic! I am just italic.

+

italic and italic bold and italic

+

bold and italic bold and italic

+

I'm italic and bold I am just bold. italic

+

I'm bold and italic! I am just italic. italic

+

italic and italic bold and italic_

+

bold and italic bold and bold_

+

italic and italic bold

+

bold and italic bold

+

italic italic bold italic

+

italic and bold bold: foo bar italic

+

italic and bold italic foo bar italic

+

italic and italic bold italic

+

bold and italic bold italic

+ ''', + True ) @@ -83,58 +169,112 @@ class TestBetterSmartAll(util.MdCase): } } - def test_smart_complex_cases_star(self): - """Test some complex cases with star.""" + def test_complex_cases_star(self): + """Test some complex cases for asterisks.""" self.check_markdown( ''' ***I'm italic and bold* I am just bold.** ***I'm bold and italic!** I am just italic.* + + *italic and **italic bold*** and *italic* + + **bold and *italic bold*** and *italic* + + ***I'm italic and bold* I am just bold.** *italic* + + ***I'm bold and italic!** I am just italic.* *italic* + + *italic and **italic bold*** and not italic* + + **bold and *italic bold*** and not italic* + + *italic and **italic bold*** + + **bold and *italic bold*** + + *italic **italic bold** italic* + + ***italic and bold* bold**: foo bar **bold** + + ***italic and bold** italic* foo bar **bold** + + *italic and **italic bold*** **bold** + + **bold and *italic bold*** **bold** ''', '''

I'm italic and bold I am just bold.

I'm bold and italic! I am just italic.

+

italic and italic bold and italic

+

bold and italic bold and italic

+

I'm italic and bold I am just bold. italic

+

I'm bold and italic! I am just italic. italic

+

italic and italic bold and not italic*

+

bold and italic bold and not italic*

+

italic and italic bold

+

bold and italic bold

+

italic italic bold italic

+

italic and bold bold: foo bar bold

+

italic and bold italic foo bar bold

+

italic and italic bold bold

+

bold and italic bold bold

''', True ) - def test_smart_complex_cases_underscore(self): - """Test some complex cases with underscore.""" + def test_complex_cases_underscore(self): + """Test some complex cases for underscore.""" self.check_markdown( ''' ___I'm italic and bold_ I am just bold.__ ___I'm bold and italic!__ I am just italic._ - ''', - ''' -

I'm italic and bold I am just bold.

-

I'm bold and italic! I am just italic.

- ''', - True - ) - def test_smart_complex_em_strong_star(self): - """Test `*text **text***`.""" + _italic and __italic bold___ and _italic_ - self.check_markdown( - "*I'm italic. **I'm bold and italic.***", - "

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

" - ) + __bold and _italic bold___ and _italic_ - def test_smart_complex_em_strong_under(self): - """Test `_text __text___`.""" + ___I'm italic and bold_ I am just bold.__ _italic_ - self.check_markdown( - "_I'm italic. __I'm bold and italic.___", - "

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

" - ) + ___I'm bold and italic!__ I am just italic._ _italic_ - def test_complex_strong_em_under(self): - """Test `__text _text___`.""" + _italic and __italic bold___ and not italic_ - self.check_markdown( - "__I'm bold. _I'm bold and italic.___", - "

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

" + __bold and _italic bold___ and not italic_ + + _italic and __italic bold___ + + __bold and _italic bold___ + + _italic __italic bold__ italic_ + + ___italic and bold_ bold__: foo bar __bold__ + + ___italic and bold__ italic_ foo bar __bold__ + + _italic and __italic bold___ __bold__ + + __bold and _italic bold___ __bold__ + ''', + ''' +

I'm italic and bold I am just bold.

+

I'm bold and italic! I am just italic.

+

italic and italic bold and italic

+

bold and italic bold and italic

+

I'm italic and bold I am just bold. italic

+

I'm bold and italic! I am just italic. italic

+

italic and italic bold and not italic_

+

bold and italic bold and not italic_

+

italic and italic bold

+

bold and italic bold

+

italic italic bold italic

+

italic and bold bold: foo bar bold

+

italic and bold italic foo bar bold

+

italic and italic bold bold

+

bold and italic bold bold

+ ''', + True ) diff --git a/tests/test_extensions/test_caret.py b/tests/test_extensions/test_caret.py index 3b642c9dd..2b8c51778 100644 --- a/tests/test_extensions/test_caret.py +++ b/tests/test_extensions/test_caret.py @@ -113,20 +113,59 @@ def test_case_11(self): True ) - def test_complex_sup_ins_under(self): - """Test `^text ^^text^^^`.""" + def test_complex_cases(self): + """Test some complex cases.""" self.check_markdown( - R"^I'm\ sup.\ ^^I'm\ sup\ and\ insert.^^^", - "

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

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

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

" + ^sup\ and\ ^^sup\ insert^^^ and ^sup^ + + ^^insert and ^sup\ insert^^^ and ^sup^ + + ^^^I'm\ sup\ and\ insert^ I am just insert.^^ ^sup^ + + ^^^I'm\ insert\ and\ sup!^^\ I\ am\ just\ sup.^ ^sup^ + + ^sup\ and\ ^^sup\ insert^^^ and not sup^ + + ^^insert and ^sup\ insert^^^ and not sup^ + + ^sup\ and\ ^^sup\ insert^^^ + + ^^insert and ^sup\ insert^^^ + + ^sup\ ^^sup\ insert^^\ sup^ + + ^^^sup\ and\ insert^ insert^^: foo bar ^^insert^^ + + ^^^sup\ and\ insert^^\ sup^ foo bar ^^insert^^ + + ^sup\ and\ ^^sup\ insert^^^ ^^insert^^ + + ^^insert and ^sup\ insert^^^ ^^insert^^ + ''', + ''' +

I'm insert and sup I am just insert.

+

I'm insert and sup! I am just sup.

+

sup and sup insert and sup

+

insert and sup insert and sup

+

I'm sup and insert I am just insert. sup

+

I'm insert and sup! I am just sup. sup

+

sup and sup insert and not sup^

+

insert and sup insert and not sup^

+

sup and sup insert

+

insert and sup insert

+

sup sup insert sup

+

sup and insert insert: foo bar insert

+

sup and insert sup foo bar insert

+

sup and sup insert insert

+

insert and sup insert insert

+ ''', + True ) @@ -232,18 +271,57 @@ def test_case_10(self): True ) - def test_complex_sup_ins_under(self): - """Test `^text ^^text^^^`.""" + def test_complex_cases(self): + """Test some complex cases.""" self.check_markdown( - R"^I'm\ sup.\ ^^I'm\ sup\ and\ insert.^^^", - "

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

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

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

" + ^sup\ and\ ^^sup\ insert^^^ and ^sup^ + + ^^insert and ^sup\ insert^^^ and ^sup^ + + ^^^I'm\ sup\ and\ insert^ I am just insert.^^ ^sup^ + + ^^^I'm\ insert\ and\ sup!^^\ I\ am\ just\ sup.^ ^sup^ + + ^sup\ and\ ^^sup\ insert^^^ and not sup^ + + ^^insert and ^sup\ insert^^^ and not sup^ + + ^sup\ and\ ^^sup\ insert^^^ + + ^^insert and ^sup\ insert^^^ + + ^sup\ ^^sup\ insert^^\ sup^ + + ^^^sup\ and\ insert^ insert^^: foo bar ^^insert^^ + + ^^^sup\ and\ insert^^\ sup^ foo bar ^^insert^^ + + ^sup\ and\ ^^sup\ insert^^^ ^^insert^^ + + ^^insert and ^sup\ insert^^^ ^^insert^^ + ''', + ''' +

I'm insert and sup I am just insert.

+

I'm insert and sup! I am just sup.

+

sup and sup insert and sup

+

insert and sup insert and sup

+

I'm sup and insert I am just insert. sup

+

I'm insert and sup! I am just sup. sup

+

sup and sup insert and not sup^

+

insert and sup insert and not sup^

+

sup and sup insert

+

insert and sup insert

+

sup sup insert sup

+

sup and insert insert: foo bar insert

+

sup and insert sup foo bar insert

+

sup and sup insert insert

+

insert and sup insert insert

+ ''', + True ) diff --git a/tests/test_extensions/test_tilde.py b/tests/test_extensions/test_tilde.py index 5eb06498e..130c8abd5 100644 --- a/tests/test_extensions/test_tilde.py +++ b/tests/test_extensions/test_tilde.py @@ -113,20 +113,59 @@ def test_case_11(self): True ) - def test_complex_sub_del_under(self): - """Test `~text ~~text~~~`.""" + def test_complex_cases(self): + """Test some complex cases.""" self.check_markdown( - R"~I'm\ sub.\ ~~I'm\ sub\ and\ delete.~~~", - "

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

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

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

" + ~sub\ and\ ~~sub\ delete~~~ and ~sub~ + + ~~delete and ~sub\ delete~~~ and ~sub~ + + ~~~I'm\ sub\ and\ delete~ I am just delete.~~ ~sub~ + + ~~~I'm\ delete\ and\ sub!~~\ I\ am\ just\ sub.~ ~sub~ + + ~sub\ and\ ~~sub\ delete~~~ and not sub~ + + ~~delete and ~sub\ delete~~~ and not sub~ + + ~sub\ and\ ~~sub\ delete~~~ + + ~~delete and ~sub\ delete~~~ + + ~sub\ ~~sub\ delete~~\ sub~ + + ~~~sub\ and\ delete~ delete~~: foo bar ~~delete~~ + + ~~~sub\ and\ delete~~\ sub~ foo bar ~~delete~~ + + ~sub\ and\ ~~sub\ delete~~~ ~~delete~~ + + ~~delete and ~sub\ delete~~~ ~~delete~~ + ''', + ''' +

I'm delete and sub I am just delete.

+

I'm delete and sub! I am just sub.

+

sub and sub delete and sub

+

delete and sub delete and sub

+

I'm sub and delete I am just delete. sub

+

I'm delete and sub! I am just sub. sub

+

sub and sub delete and not sub~

+

delete and sub delete and not sub~

+

sub and sub delete

+

delete and sub delete

+

sub sub delete sub

+

sub and delete delete: foo bar delete

+

sub and delete sub foo bar delete

+

sub and sub delete delete

+

delete and sub delete delete

+ ''', + True ) @@ -241,18 +280,57 @@ def test_case_11(self): True ) - def test_complex_sub_del_under(self): - """Test `~text ~~text~~~`.""" + def test_complex_cases(self): + """Test some complex cases.""" self.check_markdown( - R"~I'm\ sub.\ ~~I'm\ sub\ and\ delete.~~~", - "

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

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

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

" + ~sub\ and\ ~~sub\ delete~~~ and ~sub~ + + ~~delete and ~sub\ delete~~~ and ~sub~ + + ~~~I'm\ sub\ and\ delete~ I am just delete.~~ ~sub~ + + ~~~I'm\ delete\ and\ sub!~~\ I\ am\ just\ sub.~ ~sub~ + + ~sub\ and\ ~~sub\ delete~~~ and not sub~ + + ~~delete and ~sub\ delete~~~ and not sub~ + + ~sub\ and\ ~~sub\ delete~~~ + + ~~delete and ~sub\ delete~~~ + + ~sub\ ~~sub\ delete~~\ sub~ + + ~~~sub\ and\ delete~ delete~~: foo bar ~~delete~~ + + ~~~sub\ and\ delete~~\ sub~ foo bar ~~delete~~ + + ~sub\ and\ ~~sub\ delete~~~ ~~delete~~ + + ~~delete and ~sub\ delete~~~ ~~delete~~ + ''', + ''' +

I'm delete and sub I am just delete.

+

I'm delete and sub! I am just sub.

+

sub and sub delete and sub

+

delete and sub delete and sub

+

I'm sub and delete I am just delete. sub

+

I'm delete and sub! I am just sub. sub

+

sub and sub delete and not sub~

+

delete and sub delete and not sub~

+

sub and sub delete

+

delete and sub delete

+

sub sub delete sub

+

sub and delete delete: foo bar delete

+

sub and delete sub foo bar delete

+

sub and sub delete delete

+

delete and sub delete delete

+ ''', + True )