|
import logging |
|
|
|
from ..common.utils import charCodeAt, isSpace, normalizeReference |
|
from .state_block import StateBlock |
|
|
|
LOGGER = logging.getLogger(__name__) |
|
|
|
|
|
def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) -> bool: |
|
LOGGER.debug( |
|
"entering reference: %s, %s, %s, %s", state, startLine, _endLine, silent |
|
) |
|
|
|
lines = 0 |
|
pos = state.bMarks[startLine] + state.tShift[startLine] |
|
maximum = state.eMarks[startLine] |
|
nextLine = startLine + 1 |
|
|
|
if state.is_code_block(startLine): |
|
return False |
|
|
|
if state.src[pos] != "[": |
|
return False |
|
|
|
|
|
|
|
while pos < maximum: |
|
|
|
if state.src[pos] == "]" and state.src[pos - 1] != "\\": |
|
if pos + 1 == maximum: |
|
return False |
|
if state.src[pos + 1] != ":": |
|
return False |
|
break |
|
pos += 1 |
|
|
|
endLine = state.lineMax |
|
|
|
|
|
terminatorRules = state.md.block.ruler.getRules("reference") |
|
|
|
oldParentType = state.parentType |
|
state.parentType = "reference" |
|
|
|
while nextLine < endLine and not state.isEmpty(nextLine): |
|
|
|
|
|
if state.sCount[nextLine] - state.blkIndent > 3: |
|
nextLine += 1 |
|
continue |
|
|
|
|
|
if state.sCount[nextLine] < 0: |
|
nextLine += 1 |
|
continue |
|
|
|
|
|
terminate = False |
|
for terminatorRule in terminatorRules: |
|
if terminatorRule(state, nextLine, endLine, True): |
|
terminate = True |
|
break |
|
|
|
if terminate: |
|
break |
|
|
|
nextLine += 1 |
|
|
|
string = state.getLines(startLine, nextLine, state.blkIndent, False).strip() |
|
maximum = len(string) |
|
|
|
labelEnd = None |
|
pos = 1 |
|
while pos < maximum: |
|
ch = charCodeAt(string, pos) |
|
if ch == 0x5B: |
|
return False |
|
elif ch == 0x5D: |
|
labelEnd = pos |
|
break |
|
elif ch == 0x0A: |
|
lines += 1 |
|
elif ch == 0x5C: |
|
pos += 1 |
|
if pos < maximum and charCodeAt(string, pos) == 0x0A: |
|
lines += 1 |
|
pos += 1 |
|
|
|
if ( |
|
labelEnd is None or labelEnd < 0 or charCodeAt(string, labelEnd + 1) != 0x3A |
|
): |
|
return False |
|
|
|
|
|
|
|
pos = labelEnd + 2 |
|
while pos < maximum: |
|
ch = charCodeAt(string, pos) |
|
if ch == 0x0A: |
|
lines += 1 |
|
elif isSpace(ch): |
|
pass |
|
else: |
|
break |
|
pos += 1 |
|
|
|
|
|
|
|
res = state.md.helpers.parseLinkDestination(string, pos, maximum) |
|
if not res.ok: |
|
return False |
|
|
|
href = state.md.normalizeLink(res.str) |
|
if not state.md.validateLink(href): |
|
return False |
|
|
|
pos = res.pos |
|
lines += res.lines |
|
|
|
|
|
destEndPos = pos |
|
destEndLineNo = lines |
|
|
|
|
|
|
|
start = pos |
|
while pos < maximum: |
|
ch = charCodeAt(string, pos) |
|
if ch == 0x0A: |
|
lines += 1 |
|
elif isSpace(ch): |
|
pass |
|
else: |
|
break |
|
pos += 1 |
|
|
|
|
|
|
|
res = state.md.helpers.parseLinkTitle(string, pos, maximum) |
|
if pos < maximum and start != pos and res.ok: |
|
title = res.str |
|
pos = res.pos |
|
lines += res.lines |
|
else: |
|
title = "" |
|
pos = destEndPos |
|
lines = destEndLineNo |
|
|
|
|
|
while pos < maximum: |
|
ch = charCodeAt(string, pos) |
|
if not isSpace(ch): |
|
break |
|
pos += 1 |
|
|
|
if pos < maximum and charCodeAt(string, pos) != 0x0A and title: |
|
|
|
|
|
title = "" |
|
pos = destEndPos |
|
lines = destEndLineNo |
|
while pos < maximum: |
|
ch = charCodeAt(string, pos) |
|
if not isSpace(ch): |
|
break |
|
pos += 1 |
|
|
|
if pos < maximum and charCodeAt(string, pos) != 0x0A: |
|
|
|
return False |
|
|
|
label = normalizeReference(string[1:labelEnd]) |
|
if not label: |
|
|
|
return False |
|
|
|
|
|
if silent: |
|
return True |
|
|
|
if "references" not in state.env: |
|
state.env["references"] = {} |
|
|
|
state.line = startLine + lines + 1 |
|
|
|
|
|
if state.md.options.get("inline_definitions", False): |
|
token = state.push("definition", "", 0) |
|
token.meta = { |
|
"id": label, |
|
"title": title, |
|
"url": href, |
|
"label": string[1:labelEnd], |
|
} |
|
token.map = [startLine, state.line] |
|
|
|
if label not in state.env["references"]: |
|
state.env["references"][label] = { |
|
"title": title, |
|
"href": href, |
|
"map": [startLine, state.line], |
|
} |
|
else: |
|
state.env.setdefault("duplicate_refs", []).append( |
|
{ |
|
"title": title, |
|
"href": href, |
|
"label": label, |
|
"map": [startLine, state.line], |
|
} |
|
) |
|
|
|
state.parentType = oldParentType |
|
|
|
return True |
|
|