_base.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import re
  2. from abc import ABCMeta, abstractmethod
  3. from typing import (
  4. TYPE_CHECKING,
  5. Any,
  6. Callable,
  7. Dict,
  8. Iterable,
  9. List,
  10. Match,
  11. Optional,
  12. Tuple,
  13. Type,
  14. Union,
  15. )
  16. if TYPE_CHECKING:
  17. from ..block_parser import BlockParser
  18. from ..core import BlockState
  19. from ..markdown import Markdown
  20. class DirectiveParser(ABCMeta):
  21. name = "directive"
  22. @staticmethod
  23. @abstractmethod
  24. def parse_type(m: Match[str]) -> str:
  25. raise NotImplementedError()
  26. @staticmethod
  27. @abstractmethod
  28. def parse_title(m: Match[str]) -> str:
  29. raise NotImplementedError()
  30. @staticmethod
  31. @abstractmethod
  32. def parse_content(m: Match[str]) -> str:
  33. raise NotImplementedError()
  34. @classmethod
  35. def parse_tokens(cls, block: "BlockParser", text: str, state: "BlockState") -> Iterable[Dict[str, Any]]:
  36. if state.depth() >= block.max_nested_level - 1 and cls.name in block.rules:
  37. rules = list(block.rules)
  38. rules.remove(cls.name)
  39. else:
  40. rules = block.rules
  41. child = state.child_state(text)
  42. block.parse(child, rules)
  43. return child.tokens
  44. @staticmethod
  45. def parse_options(m: Match[str]) -> List[Tuple[str, str]]:
  46. text = m.group("options")
  47. if not text.strip():
  48. return []
  49. options = []
  50. for line in re.split(r"\n+", text):
  51. line = line.strip()[1:]
  52. if not line:
  53. continue
  54. i = line.find(":")
  55. k = line[:i]
  56. v = line[i + 1 :].strip()
  57. options.append((k, v))
  58. return options
  59. class BaseDirective(metaclass=ABCMeta):
  60. parser: Type[DirectiveParser]
  61. directive_pattern: Optional[str] = None
  62. def __init__(self, plugins: List["DirectivePlugin"]):
  63. self._methods: Dict[
  64. str,
  65. Callable[
  66. ["BlockParser", Match[str], "BlockState"],
  67. Union[Dict[str, Any], List[Dict[str, Any]]],
  68. ],
  69. ] = {}
  70. self.__plugins = plugins
  71. def register(
  72. self,
  73. name: str,
  74. fn: Callable[
  75. ["BlockParser", Match[str], "BlockState"],
  76. Union[Dict[str, Any], List[Dict[str, Any]]],
  77. ],
  78. ) -> None:
  79. self._methods[name] = fn
  80. def parse_method(
  81. self, block: "BlockParser", m: Match[str], state: "BlockState"
  82. ) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
  83. _type = self.parser.parse_type(m)
  84. method = self._methods.get(_type)
  85. if method:
  86. try:
  87. token = method(block, m, state)
  88. except ValueError as e:
  89. token = {"type": "block_error", "raw": str(e)}
  90. else:
  91. text = m.group(0)
  92. token = {
  93. "type": "block_error",
  94. "raw": text,
  95. }
  96. if isinstance(token, list):
  97. for tok in token:
  98. state.append_token(tok)
  99. else:
  100. state.append_token(token)
  101. return token
  102. @abstractmethod
  103. def parse_directive(self, block: "BlockParser", m: Match[str], state: "BlockState") -> Optional[int]:
  104. raise NotImplementedError()
  105. def register_block_parser(self, md: "Markdown", before: Optional[str] = None) -> None:
  106. md.block.register(
  107. self.parser.name,
  108. self.directive_pattern,
  109. self.parse_directive,
  110. before=before,
  111. )
  112. def __call__(self, markdown: "Markdown") -> None:
  113. for plugin in self.__plugins:
  114. plugin.parser = self.parser
  115. plugin(self, markdown)
  116. class DirectivePlugin:
  117. parser: Type[DirectiveParser]
  118. def __init__(self) -> None: ...
  119. def parse_options(self, m: Match[str]) -> List[Tuple[str, str]]:
  120. return self.parser.parse_options(m)
  121. def parse_type(self, m: Match[str]) -> str:
  122. return self.parser.parse_type(m)
  123. def parse_title(self, m: Match[str]) -> str:
  124. return self.parser.parse_title(m)
  125. def parse_content(self, m: Match[str]) -> str:
  126. return self.parser.parse_content(m)
  127. def parse_tokens(self, block: "BlockParser", text: str, state: "BlockState") -> Iterable[Dict[str, Any]]:
  128. return self.parser.parse_tokens(block, text, state)
  129. def parse(
  130. self, block: "BlockParser", m: Match[str], state: "BlockState"
  131. ) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
  132. raise NotImplementedError()
  133. def __call__(self, directive: BaseDirective, md: "Markdown") -> None:
  134. raise NotImplementedError()