diff --git a/lib/markdown2.py b/lib/markdown2.py index 3632a54e..f63f2ef0 100755 --- a/lib/markdown2.py +++ b/lib/markdown2.py @@ -1581,6 +1581,20 @@ def _find_balanced(self, text: str, start: int, open_c: str, close_c: str) -> in out - the same number of open_c and close_c are encountered - or the end of string if it's reached before the balance point is found. """ + # If no `close_c` remains in the text, the loop below runs to the end, + # and the link scanner calls this once per `[...](`, which is quadratic + # for unclosed brackets. Skip the scan using a per-text cache of the + # last `close_c` position. + cache = getattr(self, "_balanced_last_close", None) + if cache is None or cache[0] is not text: + cache = [text, {}] + self._balanced_last_close = cache + last = cache[1].get(close_c) + if last is None: + last = text.rfind(close_c) + cache[1][close_c] = last + if start > last: + return len(text) i = start l = len(text) count = 1 @@ -3157,6 +3171,10 @@ def parse_inline_anchor_or_image(self, text: str, _link_text: str, start_idx: in if has_anglebrackets: end_idx = self.md._find_balanced(text, end_idx+1, "<", ">") end_idx = self.md._find_balanced(text, end_idx, "(", ")") + if text[end_idx - 1] != ")": + # The destination's "(" never closed, so `_inline_link_title` (which + # ends in `\)$`) cannot match; skip the search. + return match = self.md._inline_link_title.search(text, idx, end_idx) if not match: return diff --git a/test/tm-cases/unclosed_link_parens.html b/test/tm-cases/unclosed_link_parens.html new file mode 100644 index 00000000..c6dee763 --- /dev/null +++ b/test/tm-cases/unclosed_link_parens.html @@ -0,0 +1,409 @@ +

[a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

![img](

+ +

[c](unclosed

+ +

[d]( + +

[e](a(b)c

+ +

A valid ok link, a titled link, and a nested one.

diff --git a/test/tm-cases/unclosed_link_parens.text b/test/tm-cases/unclosed_link_parens.text new file mode 100644 index 00000000..e4b5a263 --- /dev/null +++ b/test/tm-cases/unclosed_link_parens.text @@ -0,0 +1,409 @@ +[a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]([a]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +![img]( + +[c](unclosed + +[d](