def footnote_ref(state: StateInline, silent: bool): """Process footnote references ([^...])""" maximum = state.posMax start = state.pos # should be at least 4 chars - "[^x]" if start + 3 > maximum: return False if "footnotes" not in state.env or "refs" not in state.env["footnotes"]: 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] == 0x0A: return False if state.srcCharCode[pos] == 0x5D: # /* ] */ break pos += 1 if pos == start + 2: # no empty footnote labels return False if pos >= maximum: return False pos += 1 label = state.src[start + 2:pos - 1] if (":" + label) not in state.env["footnotes"]["refs"]: return False if not silent: if "list" not in state.env["footnotes"]: state.env["footnotes"]["list"] = {} if state.env["footnotes"]["refs"][":" + label] < 0: footnoteId = len(state.env["footnotes"]["list"]) state.env["footnotes"]["list"][footnoteId] = { "label": label, "count": 0 } state.env["footnotes"]["refs"][":" + label] = footnoteId else: footnoteId = state.env["footnotes"]["refs"][":" + label] footnoteSubId = state.env["footnotes"]["list"][footnoteId]["count"] state.env["footnotes"]["list"][footnoteId]["count"] += 1 token = state.push("footnote_ref", "", 0) token.meta = {"id": footnoteId, "subId": footnoteSubId, "label": label} state.pos = pos state.posMax = maximum return True
def myst_role(state: StateInline, silent: bool): try: if state.srcCharCode[state.pos - 1] == 0x5C: # /* \ */ # escaped (this could be improved in the case of edge case '\\{') return False except IndexError: pass match = PATTERN.search(state.src[state.pos:]) if not match: return False state.pos += match.end() if not silent: token = state.push("myst_role", "", 0) token.meta = {"name": match.group(1)} token.content = match.group(3) return True
def footnote_inline(state: StateInline, silent: bool): """Process inline footnotes (^[...])""" maximum = state.posMax start = state.pos if start + 2 >= maximum: return False if state.srcCharCode[start] != 0x5E: # /* ^ */ return False if state.srcCharCode[start + 1] != 0x5B: # /* [ */ return False labelStart = start + 2 labelEnd = parseLinkLabel(state, start + 1) # parser failed to find ']', so it's not a valid note if labelEnd < 0: return False # We found the end of the link, and know for a fact it's a valid link # so all that's left to do is to call tokenizer. # if not silent: refs = state.env.setdefault("footnotes", {}).setdefault("list", {}) footnoteId = len(refs) tokens = [] state.md.inline.parse(state.src[labelStart:labelEnd], state.md, state.env, tokens) token = state.push("footnote_ref", "", 0) token.meta = {"id": footnoteId} refs[footnoteId] = { "content": state.src[labelStart:labelEnd], "tokens": tokens } state.pos = labelEnd + 1 state.posMax = maximum return True
def myst_role(state: StateInline, silent: bool): # check name match = VALID_NAME_PATTERN.match(state.src[state.pos:]) if not match: return False name = match.group(1) # check for starting backslash escape try: if state.srcCharCode[state.pos - 1] == 0x5C: # /* \ */ # escaped (this could be improved in the case of edge case '\\{') return False except IndexError: pass # scan opening tick length start = pos = state.pos + match.end() try: while state.src[pos] == "`": pos += 1 except IndexError: return False tick_length = pos - start if not tick_length: return False # search for closing ticks match = re.search("`" * tick_length, state.src[pos + 1:]) if not match: return False content = state.src[pos:pos + match.start() + 1].replace("\n", " ") if not silent: token = state.push("myst_role", "", 0) token.meta = {"name": name} token.content = content state.pos = pos + match.end() + 1 return True
def _substitution_inline(state: StateInline, silent: bool): try: if (state.srcCharCode[state.pos] != start_char or state.srcCharCode[state.pos + 1] != start_char): return False except IndexError: return False pos = state.pos + 2 found_closing = False while True: try: end = state.srcCharCode.index(end_char, pos) except ValueError: return False try: if state.srcCharCode[end + 1] == end_char: found_closing = True break except IndexError: return False pos = end + 2 if not found_closing: return False text = state.src[state.pos + 2:end].strip() state.pos = end + 2 if silent: return True token = state.push("substitution_inline", "span", 0) token.block = False token.content = text token.attrSet("class", "substitution") token.attrSet("text", text) token.markup = f"{start_delimiter}{end_delimiter}" return True
def _math_inline_dollar(state: StateInline, silent: bool) -> bool: """Inline dollar rule. - Initial check: - check if first character is a $ - check if the first character is escaped - check if the next character is a space (if not allow_space) - check if the next character is a digit (if not allow_digits) - Advance one, if allow_double - Find closing (advance one, if allow_double) - Check closing: - check if the previous character is a space (if not allow_space) - check if the next character is a digit (if not allow_digits) - Check empty content """ # TODO options: # even/odd backslash escaping if state.srcCharCode[state.pos] != 0x24: # /* $ */ return False if not allow_space: # whitespace not allowed straight after opening $ try: if isWhiteSpace(state.srcCharCode[state.pos + 1]): return False except IndexError: return False if not allow_digits: # digit not allowed straight before opening $ try: if state.src[state.pos - 1].isdigit(): return False except IndexError: pass if is_escaped(state, state.pos): return False try: is_double = allow_double and state.srcCharCode[state.pos + 1] == 0x24 except IndexError: return False # find closing $ pos = state.pos + 1 + (1 if is_double else 0) found_closing = False while not found_closing: try: end = state.srcCharCode.index(0x24, pos) except ValueError: return False if is_escaped(state, end): pos = end + 1 continue try: if is_double and not state.srcCharCode[end + 1] == 0x24: pos = end + 1 continue except IndexError: return False if is_double: end += 1 found_closing = True if not found_closing: return False if not allow_space: # whitespace not allowed straight before closing $ try: if isWhiteSpace(state.srcCharCode[end - 1]): return False except IndexError: return False if not allow_digits: # digit not allowed straight after closing $ try: if state.src[end + 1].isdigit(): return False except IndexError: pass text = (state.src[state.pos + 2:end - 1] if is_double else state.src[state.pos + 1:end]) # ignore empty if not text: return False if not silent: token = state.push( "math_inline_double" if is_double else "math_inline", "math", 0) token.content = text token.markup = "$$" if is_double else "$" state.pos = end + 1 return True
def _math_inline_dollar(state: StateInline, silent: bool): # TODO options: # even/odd backslash escaping # allow $$ blocks if state.srcCharCode[state.pos] != 0x24: # /* $ */ return False if not allow_space: # whitespace not allowed straight after opening $ try: if isWhiteSpace(state.srcCharCode[state.pos + 1]): return False except IndexError: return False if not allow_digits: # digit not allowed straight before opening $ try: if state.src[state.pos - 1].isdigit(): return False except IndexError: pass if is_escaped(state, state.pos): return False # find closing $ pos = state.pos + 1 found_closing = False while True: try: end = state.srcCharCode.index(0x24, pos) except ValueError: return False if is_escaped(state, end): pos = end + 1 else: found_closing = True break if not found_closing: return False if not allow_space: # whitespace not allowed straight before closing $ try: if isWhiteSpace(state.srcCharCode[end - 1]): return False except IndexError: return False if not allow_digits: # digit not allowed straight after closing $ try: if state.src[end + 1].isdigit(): return False except IndexError: pass text = state.src[state.pos + 1 : end] # ignore empty if not text: return False if not silent: token = state.push("math_inline", "math", 0) token.content = text token.markup = "$" state.pos = end + 1 return True