substitution.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. from markdown_it import MarkdownIt
  2. from markdown_it.rules_block import StateBlock
  3. from markdown_it.rules_inline import StateInline
  4. from mdit_py_plugins.utils import is_code_block
  5. def substitution_plugin(
  6. md: MarkdownIt, start_delimiter: str = "{", end_delimiter: str = "}"
  7. ) -> None:
  8. """A plugin to create substitution tokens.
  9. These, token should be handled by the renderer.
  10. Example::
  11. {{ block }}
  12. a {{ inline }} b
  13. """
  14. def _substitution_inline(state: StateInline, silent: bool) -> bool:
  15. try:
  16. if (
  17. state.src[state.pos] != start_delimiter
  18. or state.src[state.pos + 1] != start_delimiter
  19. ):
  20. return False
  21. except IndexError:
  22. return False
  23. pos = state.pos + 2
  24. found_closing = False
  25. while True:
  26. try:
  27. end = state.src.index(end_delimiter, pos)
  28. except ValueError:
  29. return False
  30. try:
  31. if state.src[end + 1] == end_delimiter:
  32. found_closing = True
  33. break
  34. except IndexError:
  35. return False
  36. pos = end + 2
  37. if not found_closing:
  38. return False
  39. text = state.src[state.pos + 2 : end].strip()
  40. state.pos = end + 2
  41. if silent:
  42. return True
  43. token = state.push("substitution_inline", "span", 0)
  44. token.block = False
  45. token.content = text
  46. token.attrSet("class", "substitution")
  47. token.attrSet("text", text)
  48. token.markup = f"{start_delimiter}{end_delimiter}"
  49. return True
  50. def _substitution_block(
  51. state: StateBlock, startLine: int, endLine: int, silent: bool
  52. ) -> bool:
  53. if is_code_block(state, startLine):
  54. return False
  55. startPos = state.bMarks[startLine] + state.tShift[startLine]
  56. end = state.eMarks[startLine]
  57. lineText = state.src[startPos:end].strip()
  58. try:
  59. if (
  60. lineText[0] != start_delimiter
  61. or lineText[1] != start_delimiter
  62. or lineText[-1] != end_delimiter
  63. or lineText[-2] != end_delimiter
  64. or len(lineText) < 5
  65. ):
  66. return False
  67. except IndexError:
  68. return False
  69. text = lineText[2:-2].strip()
  70. # special case if multiple on same line, e.g. {{a}}{{b}}
  71. if (end_delimiter * 2) in text:
  72. return False
  73. state.line = startLine + 1
  74. if silent:
  75. return True
  76. token = state.push("substitution_block", "div", 0)
  77. token.block = True
  78. token.content = text
  79. token.attrSet("class", "substitution")
  80. token.attrSet("text", text)
  81. token.markup = f"{start_delimiter}{end_delimiter}"
  82. token.map = [startLine, state.line]
  83. return True
  84. md.block.ruler.before("fence", "substitution_block", _substitution_block)
  85. md.inline.ruler.before("escape", "substitution_inline", _substitution_inline)