| | 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 |
| | ) |
| |
|
| | 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 |
| |
|
| | string = state.src[pos : maximum + 1] |
| |
|
| | |
| | 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: |
| | if (lineContent := getNextLine(state, nextLine)) is not None: |
| | string += lineContent |
| | maximum = len(string) |
| | nextLine += 1 |
| | elif ch == 0x5C: |
| | pos += 1 |
| | if ( |
| | pos < maximum |
| | and charCodeAt(string, pos) == 0x0A |
| | and (lineContent := getNextLine(state, nextLine)) is not None |
| | ): |
| | string += lineContent |
| | maximum = len(string) |
| | nextLine += 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: |
| | if (lineContent := getNextLine(state, nextLine)) is not None: |
| | string += lineContent |
| | maximum = len(string) |
| | nextLine += 1 |
| | elif isSpace(ch): |
| | pass |
| | else: |
| | break |
| | pos += 1 |
| |
|
| | |
| | |
| | destRes = state.md.helpers.parseLinkDestination(string, pos, maximum) |
| | if not destRes.ok: |
| | return False |
| |
|
| | href = state.md.normalizeLink(destRes.str) |
| | if not state.md.validateLink(href): |
| | return False |
| |
|
| | pos = destRes.pos |
| |
|
| | |
| | destEndPos = pos |
| | destEndLineNo = nextLine |
| |
|
| | |
| | |
| | start = pos |
| | while pos < maximum: |
| | ch = charCodeAt(string, pos) |
| | if ch == 0x0A: |
| | if (lineContent := getNextLine(state, nextLine)) is not None: |
| | string += lineContent |
| | maximum = len(string) |
| | nextLine += 1 |
| | elif isSpace(ch): |
| | pass |
| | else: |
| | break |
| | pos += 1 |
| |
|
| | |
| | |
| | titleRes = state.md.helpers.parseLinkTitle(string, pos, maximum, None) |
| | while titleRes.can_continue: |
| | if (lineContent := getNextLine(state, nextLine)) is None: |
| | break |
| | string += lineContent |
| | pos = maximum |
| | maximum = len(string) |
| | nextLine += 1 |
| | titleRes = state.md.helpers.parseLinkTitle(string, pos, maximum, titleRes) |
| |
|
| | if pos < maximum and start != pos and titleRes.ok: |
| | title = titleRes.str |
| | pos = titleRes.pos |
| | else: |
| | title = "" |
| | pos = destEndPos |
| | nextLine = 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 |
| | nextLine = 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 = nextLine |
| |
|
| | |
| | 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], |
| | } |
| | ) |
| |
|
| | return True |
| |
|
| |
|
| | def getNextLine(state: StateBlock, nextLine: int) -> None | str: |
| | endLine = state.lineMax |
| |
|
| | if nextLine >= endLine or state.isEmpty(nextLine): |
| | |
| | return None |
| |
|
| | isContinuation = False |
| |
|
| | |
| | |
| | if state.is_code_block(nextLine): |
| | isContinuation = True |
| |
|
| | |
| | if state.sCount[nextLine] < 0: |
| | isContinuation = True |
| |
|
| | if not isContinuation: |
| | terminatorRules = state.md.block.ruler.getRules("reference") |
| | oldParentType = state.parentType |
| | state.parentType = "reference" |
| |
|
| | |
| | terminate = False |
| | for terminatorRule in terminatorRules: |
| | if terminatorRule(state, nextLine, endLine, True): |
| | terminate = True |
| | break |
| |
|
| | state.parentType = oldParentType |
| |
|
| | if terminate: |
| | |
| | return None |
| |
|
| | pos = state.bMarks[nextLine] + state.tShift[nextLine] |
| | maximum = state.eMarks[nextLine] |
| |
|
| | |
| | return state.src[pos : maximum + 1] |
| |
|