second commit
This commit is contained in:
236
env/lib/python3.11/site-packages/markdown_it/rules_block/table.py
vendored
Normal file
236
env/lib/python3.11/site-packages/markdown_it/rules_block/table.py
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
# GFM table, https://github.github.com/gfm/#tables-extension-
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from ..common.utils import charStrAt, isStrSpace
|
||||
from .state_block import StateBlock
|
||||
|
||||
headerLineRe = re.compile(r"^:?-+:?$")
|
||||
enclosingPipesRe = re.compile(r"^\||\|$")
|
||||
|
||||
|
||||
def getLine(state: StateBlock, line: int) -> str:
|
||||
pos = state.bMarks[line] + state.tShift[line]
|
||||
maximum = state.eMarks[line]
|
||||
|
||||
# return state.src.substr(pos, max - pos)
|
||||
return state.src[pos:maximum]
|
||||
|
||||
|
||||
def escapedSplit(string: str) -> list[str]:
|
||||
result: list[str] = []
|
||||
pos = 0
|
||||
max = len(string)
|
||||
isEscaped = False
|
||||
lastPos = 0
|
||||
current = ""
|
||||
ch = charStrAt(string, pos)
|
||||
|
||||
while pos < max:
|
||||
if ch == "|":
|
||||
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 == "\\"
|
||||
pos += 1
|
||||
|
||||
ch = charStrAt(string, pos)
|
||||
|
||||
result.append(current + string[lastPos:])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def table(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
|
||||
tbodyLines = None
|
||||
|
||||
# should have at least two lines
|
||||
if startLine + 2 > endLine:
|
||||
return False
|
||||
|
||||
nextLine = startLine + 1
|
||||
|
||||
if state.sCount[nextLine] < state.blkIndent:
|
||||
return False
|
||||
|
||||
if state.is_code_block(nextLine):
|
||||
return False
|
||||
|
||||
# first character of the second line should be '|', '-', ':',
|
||||
# and no other characters are allowed but spaces;
|
||||
# basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp
|
||||
|
||||
pos = state.bMarks[nextLine] + state.tShift[nextLine]
|
||||
if pos >= state.eMarks[nextLine]:
|
||||
return False
|
||||
first_ch = state.src[pos]
|
||||
pos += 1
|
||||
if first_ch not in ("|", "-", ":"):
|
||||
return False
|
||||
|
||||
if pos >= state.eMarks[nextLine]:
|
||||
return False
|
||||
second_ch = state.src[pos]
|
||||
pos += 1
|
||||
if second_ch not in ("|", "-", ":") and not isStrSpace(second_ch):
|
||||
return False
|
||||
|
||||
# if first character is '-', then second character must not be a space
|
||||
# (due to parsing ambiguity with list)
|
||||
if first_ch == "-" and isStrSpace(second_ch):
|
||||
return False
|
||||
|
||||
while pos < state.eMarks[nextLine]:
|
||||
ch = state.src[pos]
|
||||
|
||||
if ch not in ("|", "-", ":") and not isStrSpace(ch):
|
||||
return False
|
||||
|
||||
pos += 1
|
||||
|
||||
lineText = getLine(state, startLine + 1)
|
||||
|
||||
columns = lineText.split("|")
|
||||
aligns = []
|
||||
for i in range(len(columns)):
|
||||
t = columns[i].strip()
|
||||
if not t:
|
||||
# allow empty columns before and after table, but not in between columns;
|
||||
# e.g. allow ` |---| `, disallow ` ---||--- `
|
||||
if i == 0 or i == len(columns) - 1:
|
||||
continue
|
||||
else:
|
||||
return False
|
||||
|
||||
if not headerLineRe.search(t):
|
||||
return False
|
||||
if charStrAt(t, len(t) - 1) == ":":
|
||||
aligns.append("center" if charStrAt(t, 0) == ":" else "right")
|
||||
elif charStrAt(t, 0) == ":":
|
||||
aligns.append("left")
|
||||
else:
|
||||
aligns.append("")
|
||||
|
||||
lineText = getLine(state, startLine).strip()
|
||||
if "|" not in lineText:
|
||||
return False
|
||||
if state.is_code_block(startLine):
|
||||
return False
|
||||
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 should be exactly the same (the rest of the rows can differ)
|
||||
columnCount = len(columns)
|
||||
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]
|
||||
|
||||
token = state.push("thead_open", "thead", 1)
|
||||
token.map = [startLine, startLine + 1]
|
||||
|
||||
token = state.push("tr_open", "tr", 1)
|
||||
token.map = [startLine, startLine + 1]
|
||||
|
||||
for i in range(len(columns)):
|
||||
token = state.push("th_open", "th", 1)
|
||||
if aligns[i]:
|
||||
token.attrs = {"style": "text-align:" + aligns[i]}
|
||||
|
||||
token = state.push("inline", "", 0)
|
||||
# note in markdown-it this map was removed in v12.0.0 however, we keep it,
|
||||
# since it is helpful to propagate to children tokens
|
||||
token.map = [startLine, startLine + 1]
|
||||
token.content = columns[i].strip()
|
||||
token.children = []
|
||||
|
||||
token = state.push("th_close", "th", -1)
|
||||
|
||||
token = state.push("tr_close", "tr", -1)
|
||||
token = state.push("thead_close", "thead", -1)
|
||||
|
||||
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 lineText:
|
||||
break
|
||||
if state.is_code_block(nextLine):
|
||||
break
|
||||
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)
|
||||
if aligns[i]:
|
||||
token.attrs = {"style": "text-align:" + aligns[i]}
|
||||
|
||||
token = state.push("inline", "", 0)
|
||||
# note in markdown-it this map was removed in v12.0.0 however, we keep it,
|
||||
# since it is helpful to propagate to children tokens
|
||||
token.map = [nextLine, nextLine + 1]
|
||||
try:
|
||||
token.content = columns[i].strip() if columns[i] else ""
|
||||
except IndexError:
|
||||
token.content = ""
|
||||
token.children = []
|
||||
|
||||
token = state.push("td_close", "td", -1)
|
||||
|
||||
token = state.push("tr_close", "tr", -1)
|
||||
|
||||
nextLine += 1
|
||||
|
||||
if tbodyLines:
|
||||
token = state.push("tbody_close", "tbody", -1)
|
||||
tbodyLines[1] = nextLine
|
||||
|
||||
token = state.push("table_close", "table", -1)
|
||||
|
||||
tableLines[1] = nextLine
|
||||
state.parentType = oldParentType
|
||||
state.line = nextLine
|
||||
return True
|
Reference in New Issue
Block a user