Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch up with markdown-it v12.0.4 #110

Closed
wants to merge 11 commits into from
1 change: 0 additions & 1 deletion markdown_it/common/html_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
"main",
"menu",
"menuitem",
"meta",
"nav",
"noframes",
"ol",
Expand Down
2 changes: 1 addition & 1 deletion markdown_it/common/html_re.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

close_tag = "<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>"
comment = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->"
processing = "<[?].*?[?]>"
processing = "<[?][\\s\\S]*?[?]>"
declaration = "<![A-Z]+\\s+[^>]*>"
cdata = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>"

Expand Down
6 changes: 6 additions & 0 deletions markdown_it/helpers/parse_link_destination.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def parseLinkDestination(string: str, pos: int, maximum: int) -> _Result:
code = charCodeAt(string, pos)
if code == 0x0A: # /* \n */)
return result
if code == 0x3C: # / * < * /
return result
if code == 0x3E: # /* > */) {
result.pos = pos + 1
result.str = unescapeAll(string[start + 1 : pos])
Expand Down Expand Up @@ -55,11 +57,15 @@ def parseLinkDestination(string: str, pos: int, maximum: int) -> _Result:
break

if code == 0x5C and pos + 1 < maximum:
if charCodeAt(string, pos + 1) == 0x20:
break
pos += 2
continue

if code == 0x28: # /* ( */)
level += 1
if level > 32:
return result

if code == 0x29: # /* ) */)
if level == 0:
Expand Down
2 changes: 2 additions & 0 deletions markdown_it/helpers/parse_link_title.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def parseLinkTitle(string: str, pos: int, maximum: int) -> _Result:
result.str = title
result.ok = True
return result
elif code == 0x28 and marker == 0x29: # /* ( */ /* ) */
return result
elif code == 0x0A:
lines += 1
elif code == 0x5C and pos + 1 < maximum: # /* \ */
Expand Down
4 changes: 2 additions & 2 deletions markdown_it/port.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
- package: markdown-it/markdown-it
commit: 331ae117e09115366db12b517ca526b1b7fee1e8
date: Sep 14, 2020
commit: 7b8969ce5cb2edc54f2c1aa39a85a3a08076337d
date: Dec 20, 2020
notes:
- Rename variables that use python built-in names, e.g.
- `max` -> `maximum`
Expand Down
15 changes: 10 additions & 5 deletions markdown_it/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,22 +219,26 @@ def fence(self, tokens: Sequence[Token], idx: int, options, env) -> str:
token = tokens[idx]
info = unescapeAll(token.info).strip() if token.info else ""
langName = ""
langAttrs = ""

if info:
langName = info.split()[0]
arr = info.split(maxsplit=1)
langName = arr[0]
if len(arr) == 2:
langAttrs = arr[1]

if options.highlight:
highlighted = options.highlight(token.content, langName) or escapeHtml(
token.content
)
highlighted = options.highlight(
token.content, langName, langAttrs
) or escapeHtml(token.content)
else:
highlighted = escapeHtml(token.content)

if highlighted.startswith("<pre"):
return highlighted + "\n"

# If language exists, inject class gently, without modifying original token.
# May be, one day we will add .clone() for token and simplify this part, but
# May be, one day we will add .deepClone() for token and simplify this part, but
# now we prefer to keep things local.
if info:
i = token.attrIndex("class")
Expand All @@ -243,6 +247,7 @@ def fence(self, tokens: Sequence[Token], idx: int, options, env) -> str:
if i < 0:
tmpAttrs.append(["class", options.langPrefix + langName])
else:
tmpAttrs[i] = tmpAttrs[i][:]
tmpAttrs[i][1] += " " + options.langPrefix + langName

# Fake token just to render attributes
Expand Down
99 changes: 57 additions & 42 deletions markdown_it/rules_block/table.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# GFM table, non-standard
# GFM table, https://github.github.com/gfm/#tables-extension-
import re

from .state_block import StateBlock
Expand All @@ -10,7 +10,7 @@


def getLine(state: StateBlock, line: int):
pos = state.bMarks[line] + state.blkIndent
pos = state.bMarks[line] + state.tShift[line]
maximum = state.eMarks[line]

# return state.src.substr(pos, max - pos)
Expand All @@ -21,48 +21,35 @@ def escapedSplit(string):
result = []
pos = 0
max = len(string)
escapes = 0
isEscaped = False
lastPos = 0
backTicked = False
lastBackTick = 0
current = ""
ch = charCodeAt(string, pos)

while pos < max:
if ch == 0x60: # /* ` */
if backTicked:
# make \` close code sequence, but not open it;
# the reason is: `\` is correct code block
backTicked = False
lastBackTick = pos
elif escapes % 2 == 0:
backTicked = True
lastBackTick = pos
# /* | */
elif ch == 0x7C and (escapes % 2 == 0) and not backTicked:
result.append(string[lastPos:pos])
lastPos = pos + 1

if ch == 0x5C: # /* \ */
escapes += 1
else:
escapes = 0
if ch == 0x7C: # /* | */
if not isEscaped:
# pipe separating cells, '|'
result.append(current + string[lastPos:pos])
current = ""
lastPos = pos + 1
else:
# escaped pipe, '\|'
current += string[lastPos : pos - 1]
lastPos = pos

isEscaped = ch == 0x5C # /* \ */
pos += 1

# If there was an un-closed backtick, go back to just after
# the last backtick, but as if it was a normal character
if pos == max and backTicked:
backTicked = False
pos = lastBackTick + 1

ch = charCodeAt(string, pos)

result.append(string[lastPos:])
result.append(current + string[lastPos:])

return result


def table(state: StateBlock, startLine: int, endLine: int, silent: bool):
tbodyLines = None

# should have at least two lines
if startLine + 2 > endLine:
Expand Down Expand Up @@ -129,17 +116,28 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool):
return False
if state.sCount[startLine] - state.blkIndent >= 4:
return False
columns = escapedSplit(enclosingPipesRe.sub("", lineText))
columns = escapedSplit(lineText)
if columns and columns[0] == "":
columns.pop(0)
if columns and columns[-1] == "":
columns.pop()

# header row will define an amount of columns in the entire table,
# and align row shouldn't be smaller than that (the rest of the rows can)
# and align row should be exactly the same (the rest of the rows can differ)
columnCount = len(columns)
if columnCount > len(aligns):
if columnCount == 0 or columnCount != len(aligns):
return False

if silent:
return True

oldParentType = state.parentType
state.parentType = "table"

# use 'blockquote' lists for termination because it's
# the most similar to tables
terminatorRules = state.md.block.ruler.getRules("blockquote")

token = state.push("table_open", "table", 1)
token.map = tableLines = [startLine, 0]

Expand All @@ -151,36 +149,49 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool):

for i in range(len(columns)):
token = state.push("th_open", "th", 1)
token.map = [startLine, startLine + 1]
if aligns[i]:
token.attrs = [["style", "text-align:" + aligns[i]]]

token = state.push("inline", "", 0)
token.content = columns[i].strip()
token.map = [startLine, startLine + 1]
token.children = []

token = state.push("th_close", "th", -1)

token = state.push("tr_close", "tr", -1)
token = state.push("thead_close", "thead", -1)

token = state.push("tbody_open", "tbody", 1)
token.map = tbodyLines = [startLine + 2, 0]

nextLine = startLine + 2
while nextLine < endLine:
if state.sCount[nextLine] < state.blkIndent:
break

terminate = False
for i in range(len(terminatorRules)):
if terminatorRules[i](state, nextLine, endLine, True):
terminate = True
break

if terminate:
break
lineText = getLine(state, nextLine).strip()
if "|" not in lineText:
if not lineText:
break
if state.sCount[nextLine] - state.blkIndent >= 4:
break
columns = escapedSplit(enclosingPipesRe.sub("", lineText))
columns = escapedSplit(lineText)
if columns and columns[0] == "":
columns.pop(0)
if columns and columns[-1] == "":
columns.pop()

if nextLine == startLine + 2:
token = state.push("tbody_open", "tbody", 1)
token.map = tbodyLines = [startLine + 2, 0]

token = state.push("tr_open", "tr", 1)
token.map = [nextLine, nextLine + 1]

for i in range(columnCount):
token = state.push("td_open", "td", 1)
token.map = [nextLine, nextLine + 1]
Expand All @@ -201,9 +212,13 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool):

nextLine += 1

token = state.push("tbody_close", "tbody", -1)
if tbodyLines:
token = state.push("tbody_close", "tbody", -1)
tbodyLines[1] = nextLine

token = state.push("table_close", "table", -1)
tableLines[1] = nextLine

tableLines[1] = tbodyLines[1] = nextLine
state.parentType = oldParentType
state.line = nextLine
return True
34 changes: 20 additions & 14 deletions markdown_it/rules_inline/autolink.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from ..common.normalize_url import normalizeLinkText, normalizeLink, validateLink

EMAIL_RE = re.compile(
r"^<([a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>" # noqa: E501
r"^([a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$" # noqa: E501
)
AUTOLINK_RE = re.compile(r"^<([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)>")
AUTOLINK_RE = re.compile(r"^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$")


def autolink(state: StateInline, silent: bool) -> bool:
Expand All @@ -16,15 +16,24 @@ def autolink(state: StateInline, silent: bool) -> bool:
if state.srcCharCode[pos] != 0x3C: # /* < */
return False

tail = state.src[pos:]
start = state.pos
maximum = state.posMax

if ">" not in tail:
return False
while True:
pos += 1
if pos >= maximum:
return False

ch = state.srcCharCode[pos]

linkMatch = AUTOLINK_RE.search(tail)
if linkMatch is not None:
if ch == 0x3C: # /* < */
return False
if ch == 0x3E: # /* > */
break

url = state.src[start + 1 : pos]

url = linkMatch.group(0)[1:-1]
if AUTOLINK_RE.search(url) is not None:
fullUrl = normalizeLink(url)
if not validateLink(fullUrl):
return False
Expand All @@ -42,13 +51,10 @@ def autolink(state: StateInline, silent: bool) -> bool:
token.markup = "autolink"
token.info = "auto"

state.pos += len(linkMatch.group(0))
state.pos += len(url) + 2
return True

emailMatch = EMAIL_RE.search(tail)
if emailMatch is not None:

url = emailMatch.group(0)[1:-1]
if EMAIL_RE.search(url) is not None:
fullUrl = normalizeLink("mailto:" + url)
if not validateLink(fullUrl):
return False
Expand All @@ -66,7 +72,7 @@ def autolink(state: StateInline, silent: bool) -> bool:
token.markup = "autolink"
token.info = "auto"

state.pos += len(emailMatch.group(0))
state.pos += len(url) + 2
return True

return False