diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-29 04:29:52 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-29 04:29:52 +0000 |
commit | fcb2f10732db61d216e2105c8154486f66b3e3ff (patch) | |
tree | efda929db4b1543eecc583e3b7d9c0bad4cd86a6 /mdit_py_plugins/attrs/index.py | |
parent | Initial commit. (diff) | |
download | mdit-py-plugins-upstream.tar.xz mdit-py-plugins-upstream.zip |
Adding upstream version 0.3.3.upstream/0.3.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mdit_py_plugins/attrs/index.py')
-rw-r--r-- | mdit_py_plugins/attrs/index.py | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/mdit_py_plugins/attrs/index.py b/mdit_py_plugins/attrs/index.py new file mode 100644 index 0000000..1adea3f --- /dev/null +++ b/mdit_py_plugins/attrs/index.py @@ -0,0 +1,123 @@ +from typing import List, Optional + +from markdown_it import MarkdownIt +from markdown_it.rules_inline import StateInline +from markdown_it.token import Token + +from .parse import ParseError, parse + + +def attrs_plugin( + md: MarkdownIt, + *, + after=("image", "code_inline", "link_close", "span_close"), + spans=False, + span_after="link", +): + """Parse inline attributes that immediately follow certain inline elements:: + + ![alt](https://image.com){#id .a b=c} + + This syntax is inspired by + `Djot spans + <https://htmlpreview.github.io/?https://github.com/jgm/djot/blob/master/doc/syntax.html#inline-attributes>`_. + + Inside the curly braces, the following syntax is possible: + + - `.foo` specifies foo as a class. + Multiple classes may be given in this way; they will be combined. + - `#foo` specifies foo as an identifier. + An element may have only one identifier; + if multiple identifiers are given, the last one is used. + - `key="value"` or `key=value` specifies a key-value attribute. + Quotes are not needed when the value consists entirely of + ASCII alphanumeric characters or `_` or `:` or `-`. + Backslash escapes may be used inside quoted values. + - `%` begins a comment, which ends with the next `%` or the end of the attribute (`}`). + + Multiple attribute blocks are merged. + + :param md: The MarkdownIt instance to modify. + :param after: The names of inline elements after which attributes may be specified. + This plugin does not support attributes after emphasis, strikethrough or text elements, + which all require post-parse processing. + :param spans: If True, also parse attributes after spans of text, encapsulated by `[]`. + Note Markdown link references take precedence over this syntax. + :param span_after: The name of an inline rule after which spans may be specified. + """ + + def _attr_rule(state: StateInline, silent: bool): + if state.pending or not state.tokens: + return False + token = state.tokens[-1] + if token.type not in after: + return False + try: + new_pos, attrs = parse(state.src[state.pos :]) + except ParseError: + return False + token_index = _find_opening(state.tokens, len(state.tokens) - 1) + if token_index is None: + return False + state.pos += new_pos + 1 + if not silent: + attr_token = state.tokens[token_index] + if "class" in attrs and "class" in token.attrs: + attrs["class"] = f"{attr_token.attrs['class']} {attrs['class']}" + attr_token.attrs.update(attrs) + return True + + if spans: + md.inline.ruler.after(span_after, "span", _span_rule) + md.inline.ruler.push("attr", _attr_rule) + + +def _find_opening(tokens: List[Token], index: int) -> Optional[int]: + """Find the opening token index, if the token is closing.""" + if tokens[index].nesting != -1: + return index + level = 0 + while index >= 0: + level += tokens[index].nesting + if level == 0: + return index + index -= 1 + return None + + +def _span_rule(state: StateInline, silent: bool): + if state.srcCharCode[state.pos] != 0x5B: # /* [ */ + return False + + maximum = state.posMax + labelStart = state.pos + 1 + labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, False) + + # parser failed to find ']', so it's not a valid span + if labelEnd < 0: + return False + + pos = labelEnd + 1 + + # check not at end of inline + if pos >= maximum: + return False + + try: + new_pos, attrs = parse(state.src[pos:]) + except ParseError: + return False + + pos += new_pos + 1 + + if not silent: + state.pos = labelStart + state.posMax = labelEnd + token = state.push("span_open", "span", 1) + token.attrs = attrs + state.md.inline.tokenize(state) + token = state.push("span_close", "span", -1) + + state.pos = pos + state.posMax = maximum + return True |