Skip to content

Commit

Permalink
👌 Throw an error if 3rd party plugin doesn't increment or counters (p…
Browse files Browse the repository at this point in the history
…reviously, markdown-it would likely go into infinite loop instead)
  • Loading branch information
hukkin committed Nov 2, 2024
1 parent c10312e commit 2126aeb
Show file tree
Hide file tree
Showing 13 changed files with 78 additions and 134 deletions.
19 changes: 9 additions & 10 deletions markdown_it/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,25 +91,24 @@ def fromCodePoint(c: int) -> str:
r'\\([!"#$%&\'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])' + "|" + r"&([a-z#][a-z0-9]{1,31});",
re.IGNORECASE,
)
DIGITAL_ENTITY_BASE10_RE = re.compile(r"#([0-9]{1,8})")
DIGITAL_ENTITY_BASE16_RE = re.compile(r"#x([a-f0-9]{1,8})", re.IGNORECASE)
DIGITAL_ENTITY_TEST_RE = re.compile(
r"^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$", re.IGNORECASE
)


def replaceEntityPattern(match: str, name: str) -> str:
"""Convert HTML entity patterns,
see https://spec.commonmark.org/0.30/#entity-references
"""
code = 0

if name in entities:
return entities[name]

code: None | int = None
if pat := DIGITAL_ENTITY_BASE10_RE.fullmatch(name):
code = int(pat.group(1), 10)
elif pat := DIGITAL_ENTITY_BASE16_RE.fullmatch(name):
code = int(pat.group(1), 16)

if code is not None and isValidEntityCode(code):
return fromCodePoint(code)
if name[0] == "#" and DIGITAL_ENTITY_TEST_RE.search(name):
code = int(name[2:], 16) if name[1].lower() == "x" else int(name[1:], 10)
if isValidEntityCode(code):
return fromCodePoint(code)

return match

Expand Down
6 changes: 2 additions & 4 deletions markdown_it/helpers/parse_link_destination.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ def __init__(self) -> None:
self.str = ""


def parseLinkDestination(string: str, pos: int, maximum: int) -> _Result:
lines = 0
start = pos
def parseLinkDestination(string: str, start: int, maximum: int) -> _Result:
pos = start
result = _Result()

if charCodeAt(string, pos) == 0x3C: # /* < */
Expand Down Expand Up @@ -80,7 +79,6 @@ def parseLinkDestination(string: str, pos: int, maximum: int) -> _Result:
return result

result.str = unescapeAll(string[start:pos])
result.lines = lines
result.pos = pos
result.ok = True
return result
4 changes: 2 additions & 2 deletions markdown_it/helpers/parse_link_title.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ def __str__(self) -> str:
return self.str


def parseLinkTitle(string: str, pos: int, maximum: int) -> _Result:
def parseLinkTitle(string: str, start: int, maximum: int) -> _Result:
lines = 0
start = pos
pos = start
result = _Result()

if pos >= maximum:
Expand Down
12 changes: 11 additions & 1 deletion markdown_it/parser_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def __init__(self) -> None:

def tokenize(self, state: StateBlock, startLine: int, endLine: int) -> None:
"""Generate tokens for input range."""
ok = False
rules = self.ruler.getRules("")
line = startLine
maxNesting = state.md.options.maxNesting
Expand All @@ -82,10 +83,19 @@ def tokenize(self, state: StateBlock, startLine: int, endLine: int) -> None:
# - update `state.line`
# - update `state.tokens`
# - return True
prevLine = state.line

for rule in rules:
if rule(state, line, endLine, False):
ok = rule(state, line, endLine, False)
if ok:
if prevLine >= state.line:
raise Exception("block rule didn't increment state.line")
break

# this can only happen if user disables paragraph rule
if not ok:
raise Exception("none of the block rules matched")

# set state.tight if we had an empty line before current tag
# i.e. latest empty line should not count
state.tight = not hasEmptyLines
Expand Down
5 changes: 5 additions & 0 deletions markdown_it/parser_inline.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ def skipToken(self, state: StateInline) -> None:
ok = rule(state, True)
state.level -= 1
if ok:
if pos >= state.pos:
raise Exception("inline rule didn't increment state.pos")
break
else:
# Too much nesting, just skip until the end of the paragraph.
Expand Down Expand Up @@ -117,11 +119,14 @@ def tokenize(self, state: StateInline) -> None:
# - update `state.pos`
# - update `state.tokens`
# - return true
prevPos = state.pos

if state.level < maxNesting:
for rule in rules:
ok = rule(state, False)
if ok:
if prevPos >= state.pos:
raise Exception("inline rule didn't increment state.pos")
break

if ok:
Expand Down
4 changes: 2 additions & 2 deletions markdown_it/port.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
- package: markdown-it/markdown-it
version: 13.0.1
commit: e843acc9edad115cbf8cf85e676443f01658be08
date: May 3, 2022
commit: 49ca65bbef067c7dba63468a48c4aee3048607dc
date: Sep 26, 2023
notes:
- Rename variables that use python built-in names, e.g.
- `max` -> `maximum`
Expand Down
9 changes: 5 additions & 4 deletions markdown_it/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,12 @@ def render(
result = ""

for i, token in enumerate(tokens):
if token.type == "inline":
type = token.type
if type == "inline":
if token.children:
result += self.renderInline(token.children, options, env)
elif token.type in self.rules:
result += self.rules[token.type](tokens, i, options, env)
elif type in self.rules:
result += self.rules[type](tokens, i, options, env)
else:
result += self.renderToken(tokens, i, options, env)

Expand Down Expand Up @@ -217,7 +218,7 @@ def code_inline(
"<code"
+ self.renderAttrs(token)
+ ">"
+ escapeHtml(tokens[idx].content)
+ escapeHtml(token.content)
+ "</code>"
)

Expand Down
84 changes: 9 additions & 75 deletions markdown_it/rules_block/blockquote.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,81 +27,16 @@ def blockquote(state: StateBlock, startLine: int, endLine: int, silent: bool) ->
return False
except IndexError:
return False
pos += 1

# we know that it's going to be a valid blockquote,
# so no point trying to find the end of it in silent mode
if silent:
return True

# set offset past spaces and ">"
initial = offset = state.sCount[startLine] + 1

try:
second_char: str | None = state.src[pos]
except IndexError:
second_char = None

# skip one optional space after '>'
if second_char == " ":
# ' > test '
# ^ -- position start of line here:
pos += 1
initial += 1
offset += 1
adjustTab = False
spaceAfterMarker = True
elif second_char == "\t":
spaceAfterMarker = True

if (state.bsCount[startLine] + offset) % 4 == 3:
# ' >\t test '
# ^ -- position start of line here (tab has width==1)
pos += 1
initial += 1
offset += 1
adjustTab = False
else:
# ' >\t test '
# ^ -- position start of line here + shift bsCount slightly
# to make extra space appear
adjustTab = True

else:
spaceAfterMarker = False

oldBMarks = [state.bMarks[startLine]]
state.bMarks[startLine] = pos

while pos < max:
ch = state.src[pos]

if isStrSpace(ch):
if ch == "\t":
offset += (
4
- (offset + state.bsCount[startLine] + (1 if adjustTab else 0)) % 4
)
else:
offset += 1

else:
break

pos += 1

oldBSCount = [state.bsCount[startLine]]
state.bsCount[startLine] = (
state.sCount[startLine] + 1 + (1 if spaceAfterMarker else 0)
)

lastLineEmpty = pos >= max

oldSCount = [state.sCount[startLine]]
state.sCount[startLine] = offset - initial

oldTShift = [state.tShift[startLine]]
state.tShift[startLine] = pos - state.bMarks[startLine]
oldBMarks = []
oldBSCount = []
oldSCount = []
oldTShift = []

terminatorRules = state.md.block.ruler.getRules("blockquote")

Expand All @@ -127,8 +62,8 @@ def blockquote(state: StateBlock, startLine: int, endLine: int, silent: bool) ->
# - - -
# ```

# for (nextLine = startLine + 1; nextLine < endLine; nextLine++) {
nextLine = startLine + 1
# for (nextLine = startLine; nextLine < endLine; nextLine++) {
nextLine = startLine
while nextLine < endLine:
# check if it's outdented, i.e. it's inside list item and indented
# less than said list item:
Expand All @@ -153,7 +88,7 @@ def blockquote(state: StateBlock, startLine: int, endLine: int, silent: bool) ->
# This line is inside the blockquote.

# set offset past spaces and ">"
initial = offset = state.sCount[nextLine] + 1
initial = state.sCount[nextLine] + 1

try:
next_char: str | None = state.src[pos]
Expand All @@ -166,18 +101,16 @@ def blockquote(state: StateBlock, startLine: int, endLine: int, silent: bool) ->
# ^ -- position start of line here:
pos += 1
initial += 1
offset += 1
adjustTab = False
spaceAfterMarker = True
elif next_char == "\t":
spaceAfterMarker = True

if (state.bsCount[nextLine] + offset) % 4 == 3:
if (state.bsCount[nextLine] + initial) % 4 == 3:
# ' >\t test '
# ^ -- position start of line here (tab has width==1)
pos += 1
initial += 1
offset += 1
adjustTab = False
else:
# ' >\t test '
Expand All @@ -188,6 +121,7 @@ def blockquote(state: StateBlock, startLine: int, endLine: int, silent: bool) ->
else:
spaceAfterMarker = False

offset = initial
oldBMarks.append(state.bMarks[nextLine])
state.bMarks[nextLine] = pos

Expand Down
Loading

0 comments on commit 2126aeb

Please sign in to comment.