def footnote_def(state: StateBlock, startLine: int, endLine: int, silent: bool): """Process footnote block definition""" start = state.bMarks[startLine] + state.tShift[startLine] maximum = state.eMarks[startLine] # line should be at least 5 chars - "[^x]:" if start + 4 > maximum: return False if state.srcCharCode[start] != 0x5B: # /* [ */ return False if state.srcCharCode[start + 1] != 0x5E: # /* ^ */ return False pos = start + 2 while pos < maximum: if state.srcCharCode[pos] == 0x20: return False if state.srcCharCode[pos] == 0x5D: # /* ] */ break pos += 1 if pos == start + 2: # no empty footnote labels return False pos += 1 if pos + 1 >= maximum or state.srcCharCode[pos] != 0x3A: # /* : */ return False if silent: return True pos += 1 label = state.src[start + 2:pos - 2] state.env.setdefault("footnotes", {}).setdefault("refs", {})[":" + label] = -1 open_token = Token("footnote_reference_open", "", 1) open_token.meta = {"label": label} open_token.level = state.level state.level += 1 state.tokens.append(open_token) oldBMark = state.bMarks[startLine] oldTShift = state.tShift[startLine] oldSCount = state.sCount[startLine] oldParentType = state.parentType posAfterColon = pos initial = offset = (state.sCount[startLine] + pos - (state.bMarks[startLine] + state.tShift[startLine])) while pos < maximum: ch = state.srcCharCode[pos] if isSpace(ch): if ch == 0x09: offset += 4 - offset % 4 else: offset += 1 else: break pos += 1 state.tShift[startLine] = pos - posAfterColon state.sCount[startLine] = offset - initial state.bMarks[startLine] = posAfterColon state.blkIndent += 4 state.parentType = "footnote" if state.sCount[startLine] < state.blkIndent: state.sCount[startLine] += state.blkIndent state.md.block.tokenize(state, startLine, endLine, True) state.parentType = oldParentType state.blkIndent -= 4 state.tShift[startLine] = oldTShift state.sCount[startLine] = oldSCount state.bMarks[startLine] = oldBMark open_token.map = [startLine, state.line] token = Token("footnote_reference_close", "", -1) state.level -= 1 token.level = state.level state.tokens.append(token) return True
def footnote_tail(state: StateBlock, *args, **kwargs): """Post-processing step, to move footnote tokens to end of the token stream. Also removes un-referenced tokens. """ insideRef = False refTokens = {} if "footnotes" not in state.env: return current = [] tok_filter = [] for tok in state.tokens: if tok.type == "footnote_reference_open": insideRef = True current = [] currentLabel = tok.meta["label"] tok_filter.append(False) continue if tok.type == "footnote_reference_close": insideRef = False # prepend ':' to avoid conflict with Object.prototype members refTokens[":" + currentLabel] = current tok_filter.append(False) continue if insideRef: current.append(tok) tok_filter.append((not insideRef)) state.tokens = [t for t, f in zip(state.tokens, tok_filter) if f] if "list" not in state.env.get("footnotes", {}): return foot_list = state.env["footnotes"]["list"] token = Token("footnote_block_open", "", 1) state.tokens.append(token) for i, foot_note in foot_list.items(): token = Token("footnote_open", "", 1) token.meta = {"id": i, "label": foot_note.get("label", None)} # TODO propagate line positions of original foot note # (but don't store in token.map, because this is used for scroll syncing) state.tokens.append(token) if "tokens" in foot_note: tokens = [] token = Token("paragraph_open", "p", 1) token.block = True tokens.append(token) token = Token("inline", "", 0) token.children = foot_note["tokens"] token.content = foot_note["content"] tokens.append(token) token = Token("paragraph_close", "p", -1) token.block = True tokens.append(token) elif "label" in foot_note: tokens = refTokens[":" + foot_note["label"]] state.tokens.extend(tokens) if state.tokens[len(state.tokens) - 1].type == "paragraph_close": lastParagraph = state.tokens.pop() else: lastParagraph = None t = (foot_note["count"] if (("count" in foot_note) and (foot_note["count"] > 0)) else 1) j = 0 while j < t: token = Token("footnote_anchor", "", 0) token.meta = { "id": i, "subId": j, "label": foot_note.get("label", None) } state.tokens.append(token) j += 1 if lastParagraph: state.tokens.append(lastParagraph) token = Token("footnote_close", "", -1) state.tokens.append(token) token = Token("footnote_block_close", "", -1) state.tokens.append(token)