Skip to content

Commit d96a282

Browse files
committed
👌 IMPROVE: Backticks performance
Adds a cache `StateInline->backticks` which remembers positions for every possible backtick closer. The algorithm is similar to the one described here: mity/md4c@685b714 Implements: markdown-it/markdown-it@1e8aff0 and markdown-it/markdown-it@fece91e
1 parent edb0143 commit d96a282

File tree

3 files changed

+36
-6
lines changed

3 files changed

+36
-6
lines changed

markdown_it/rules_inline/backticks.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,37 @@ def backtick(state: StateInline, silent: bool) -> bool:
1919
pos += 1
2020
maximum = state.posMax
2121

22-
# /* ` */
23-
while pos < maximum and (state.srcCharCode[pos] == 0x60):
22+
# scan marker length
23+
while pos < maximum and (state.srcCharCode[pos] == 0x60): # /* ` */
2424
pos += 1
2525

2626
marker = state.src[start:pos]
27+
openerLength = len(marker)
28+
29+
if state.backticksScanned and state.backticks.get(openerLength, 0) <= start:
30+
if not silent:
31+
state.pending += marker
32+
state.pos += openerLength
33+
return True
2734

2835
matchStart = matchEnd = pos
2936

37+
# Nothing found in the cache, scan until the end of the line (or until marker is found)
3038
while True:
3139
try:
3240
matchStart = state.src.index("`", matchEnd)
3341
except ValueError:
3442
break
3543
matchEnd = matchStart + 1
36-
# /* ` */
37-
while matchEnd < maximum and (state.srcCharCode[matchEnd] == 0x60):
44+
45+
# scan marker length
46+
while matchEnd < maximum and (state.srcCharCode[matchEnd] == 0x60): # /* ` */
3847
matchEnd += 1
3948

40-
if matchEnd - matchStart == len(marker):
49+
closerLength = matchEnd - matchStart
50+
51+
if closerLength == openerLength:
52+
# Found matching closer length.
4153
if not silent:
4254
token = state.push("code_inline", "code", 0)
4355
token.markup = marker
@@ -51,7 +63,13 @@ def backtick(state: StateInline, silent: bool) -> bool:
5163
state.pos = matchEnd
5264
return True
5365

66+
# Some different length found, put it in cache as upper limit of where closer can be found
67+
state.backticks[closerLength] = matchStart
68+
69+
# Scanned through the end, didn't find anything
70+
state.backticksScanned = True
71+
5472
if not silent:
5573
state.pending += marker
56-
state.pos += len(marker)
74+
state.pos += openerLength
5775
return True

markdown_it/rules_inline/state_inline.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ def __init__(
7272
# Stack of delimiter lists for upper level tags
7373
self._prev_delimiters: List[List[Delimiter]] = []
7474

75+
# backticklength => last seen position
76+
self.backticks: Dict[int, int] = {}
77+
self.backticksScanned = False
78+
7579
def __repr__(self):
7680
return (
7781
f"{self.__class__.__name__}"

tests/test_port/fixtures/commonmark_extras.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,14 @@ Coverage. Unpaired nested backtick (silent mode)
330330
.
331331

332332

333+
Coverage. Should continue scanning after closing "```" despite cache
334+
.
335+
```aaa``bbb``ccc```ddd``eee``
336+
.
337+
<p><code>aaa``bbb``ccc</code>ddd<code>eee</code></p>
338+
.
339+
340+
333341
Coverage. Entities.
334342
.
335343
*&*

0 commit comments

Comments
 (0)