Repository: Python-Markdown/markdown (python)

Issue:
SmartyPants: Apostrophes at the start of leading contractions
As mentioned in the original project [here](https://daringfireball.net/projects/smartypants/#:~:text=or%20email%20client.-,ALGORITHMIC%20SHORTCOMINGS), "SmartyPants will turn the apostrophe into an opening single-quote, when in fact it should be a closing one."

This is specifically presenting an issue for me when `smarty` collides with `abbr`. For instance, this code:

```
Each section of the editor controls an access group's ability to view, and actions within, the listed pages.
Select the checkboxes to allow the appropriate actions.

*[access group]: Access groups allow admin users to<br>assign custom permissions to all<br>users assigned to that group.
```

is rendering like this:

![image](https://user-images.githubusercontent.com/97999520/202307054-76b288f7-ed2e-4712-8f58-4d0d0b03b9d4.png)

because when `access group` gets rendered by `abbr`, the `'` is no longer recognized as being mid-word and instead renders as a leading single quote for the possessive `s`.

I've implemented a stopgap via javascript, but there are a lot of different contraction situations and it would be great to have a real fix.

Pull request:
smarty: Add a special case for 's at the beginning of the token
When `'s` is not preceded by anything, it probably means that it comes after some HTML tag, so it should be converted to a closing quote.

The reference Perl implementation makes the close_class optional and adds a lookahead check for `(\s|s\b)` when close_class was not matched:

```perl
# Single closing quotes:
s {
    ($close_class)?
    '
    (?(1)|          # If $1 captured, then do nothing;
      (?=\s | s\b)  # otherwise, positive lookahead for a whitespace
    )               # char or an 's' at a word ending position. This
                    # is a special case to handle something like:
                    # "<i>Custer</i>'s Last Stand."
} {$1&#8217;}xgi;
```

Let's copy that behavior by removing closeClass lookbehind check from closingSingleQuotesRegex2.

Fixes #1305.

Patch:
diff --git a/docs/change_log/index.md b/docs/change_log/index.md
index 584583f..ee2445c 100644
--- a/docs/change_log/index.md
+++ b/docs/change_log/index.md
@@ -3,6 +3,10 @@ title: Change Log
 Python-Markdown Change Log
 =========================
 
+*under development*: version 3.4.4 (a bug-fix release).
+
+* Add a special case for initial 's to smarty extension (#1305).
+
 March 23, 2023: version 3.4.3 (a bug-fix release).
 
 * Restore console script (#1327).
diff --git a/markdown/extensions/smarty.py b/markdown/extensions/smarty.py
index 15e5784..f03169e 100644
--- a/markdown/extensions/smarty.py
+++ b/markdown/extensions/smarty.py
@@ -139,7 +139,7 @@ openingSingleQuotesRegex = r"%s'(?=\w)" % openingQuotesBase
 
 # Single closing quotes:
 closingSingleQuotesRegex = r"(?<=%s)'(?!\s|s\b|\d)" % closeClass
-closingSingleQuotesRegex2 = r"(?<=%s)'(\s|s\b)" % closeClass
+closingSingleQuotesRegex2 = r"'(\s|s\b)"
 
 # All remaining quotes should be opening ones
 remainingSingleQuotesRegex = r"'"


Test patch:
diff --git a/tests/test_syntax/extensions/test_smarty.py b/tests/test_syntax/extensions/test_smarty.py
index f2d074d..aeb0988 100644
--- a/tests/test_syntax/extensions/test_smarty.py
+++ b/tests/test_syntax/extensions/test_smarty.py
@@ -64,6 +64,10 @@ class TestSmarty(TestCase):
             '"Ellipsis within quotes..."',
             '<p>&ldquo;Ellipsis within quotes&hellip;&rdquo;</p>'
         )
+        self.assertMarkdownRenders(
+            "*Custer*'s Last Stand",
+            "<p><em>Custer</em>&rsquo;s Last Stand</p>"
+        )
 
     def test_years(self):
         self.assertMarkdownRenders("1440--80's", '<p>1440&ndash;80&rsquo;s</p>')