You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			163 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			163 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
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()
 |