| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- import re
- from abc import ABCMeta, abstractmethod
- from typing import (
- TYPE_CHECKING,
- Any,
- Callable,
- Dict,
- Iterable,
- List,
- Match,
- Optional,
- Tuple,
- Type,
- Union,
- )
- if TYPE_CHECKING:
- from ..block_parser import BlockParser
- from ..core import BlockState
- from ..markdown import Markdown
- class DirectiveParser(ABCMeta):
- name = "directive"
- @staticmethod
- @abstractmethod
- def parse_type(m: Match[str]) -> str:
- raise NotImplementedError()
- @staticmethod
- @abstractmethod
- def parse_title(m: Match[str]) -> str:
- raise NotImplementedError()
- @staticmethod
- @abstractmethod
- def parse_content(m: Match[str]) -> str:
- raise NotImplementedError()
- @classmethod
- def parse_tokens(cls, block: "BlockParser", text: str, state: "BlockState") -> Iterable[Dict[str, Any]]:
- if state.depth() >= block.max_nested_level - 1 and cls.name in block.rules:
- rules = list(block.rules)
- rules.remove(cls.name)
- else:
- rules = block.rules
- child = state.child_state(text)
- block.parse(child, rules)
- return child.tokens
- @staticmethod
- def parse_options(m: Match[str]) -> List[Tuple[str, str]]:
- text = m.group("options")
- if not text.strip():
- return []
- options = []
- for line in re.split(r"\n+", text):
- line = line.strip()[1:]
- if not line:
- continue
- i = line.find(":")
- k = line[:i]
- v = line[i + 1 :].strip()
- options.append((k, v))
- return options
- class BaseDirective(metaclass=ABCMeta):
- parser: Type[DirectiveParser]
- directive_pattern: Optional[str] = None
- def __init__(self, plugins: List["DirectivePlugin"]):
- self._methods: Dict[
- str,
- Callable[
- ["BlockParser", Match[str], "BlockState"],
- Union[Dict[str, Any], List[Dict[str, Any]]],
- ],
- ] = {}
- self.__plugins = plugins
- def register(
- self,
- name: str,
- fn: Callable[
- ["BlockParser", Match[str], "BlockState"],
- Union[Dict[str, Any], List[Dict[str, Any]]],
- ],
- ) -> None:
- self._methods[name] = fn
- def parse_method(
- self, block: "BlockParser", m: Match[str], state: "BlockState"
- ) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
- _type = self.parser.parse_type(m)
- method = self._methods.get(_type)
- if method:
- try:
- token = method(block, m, state)
- except ValueError as e:
- token = {"type": "block_error", "raw": str(e)}
- else:
- text = m.group(0)
- token = {
- "type": "block_error",
- "raw": text,
- }
- if isinstance(token, list):
- for tok in token:
- state.append_token(tok)
- else:
- state.append_token(token)
- return token
- @abstractmethod
- def parse_directive(self, block: "BlockParser", m: Match[str], state: "BlockState") -> Optional[int]:
- raise NotImplementedError()
- def register_block_parser(self, md: "Markdown", before: Optional[str] = None) -> None:
- md.block.register(
- self.parser.name,
- self.directive_pattern,
- self.parse_directive,
- before=before,
- )
- def __call__(self, markdown: "Markdown") -> None:
- for plugin in self.__plugins:
- plugin.parser = self.parser
- plugin(self, markdown)
- class DirectivePlugin:
- parser: Type[DirectiveParser]
- def __init__(self) -> None: ...
- def parse_options(self, m: Match[str]) -> List[Tuple[str, str]]:
- return self.parser.parse_options(m)
- def parse_type(self, m: Match[str]) -> str:
- return self.parser.parse_type(m)
- def parse_title(self, m: Match[str]) -> str:
- return self.parser.parse_title(m)
- def parse_content(self, m: Match[str]) -> str:
- return self.parser.parse_content(m)
- def parse_tokens(self, block: "BlockParser", text: str, state: "BlockState") -> Iterable[Dict[str, Any]]:
- return self.parser.parse_tokens(block, text, state)
- def parse(
- self, block: "BlockParser", m: Match[str], state: "BlockState"
- ) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
- raise NotImplementedError()
- def __call__(self, directive: BaseDirective, md: "Markdown") -> None:
- raise NotImplementedError()
|