1"""
2Base classes for prompt_toolkit lexers.
3"""
4
5from __future__ import annotations
6
7from abc import ABCMeta, abstractmethod
8from typing import Callable, Hashable
9
10from prompt_toolkit.document import Document
11from prompt_toolkit.formatted_text.base import StyleAndTextTuples
12
13__all__ = [
14 "Lexer",
15 "SimpleLexer",
16 "DynamicLexer",
17]
18
19
20class Lexer(metaclass=ABCMeta):
21 """
22 Base class for all lexers.
23 """
24
25 @abstractmethod
26 def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]:
27 """
28 Takes a :class:`~prompt_toolkit.document.Document` and returns a
29 callable that takes a line number and returns a list of
30 ``(style_str, text)`` tuples for that line.
31
32 XXX: Note that in the past, this was supposed to return a list
33 of ``(Token, text)`` tuples, just like a Pygments lexer.
34 """
35
36 def invalidation_hash(self) -> Hashable:
37 """
38 When this changes, `lex_document` could give a different output.
39 (Only used for `DynamicLexer`.)
40 """
41 return id(self)
42
43
44class SimpleLexer(Lexer):
45 """
46 Lexer that doesn't do any tokenizing and returns the whole input as one
47 token.
48
49 :param style: The style string for this lexer.
50 """
51
52 def __init__(self, style: str = "") -> None:
53 self.style = style
54
55 def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]:
56 lines = document.lines
57
58 def get_line(lineno: int) -> StyleAndTextTuples:
59 "Return the tokens for the given line."
60 try:
61 return [(self.style, lines[lineno])]
62 except IndexError:
63 return []
64
65 return get_line
66
67
68class DynamicLexer(Lexer):
69 """
70 Lexer class that can dynamically returns any Lexer.
71
72 :param get_lexer: Callable that returns a :class:`.Lexer` instance.
73 """
74
75 def __init__(self, get_lexer: Callable[[], Lexer | None]) -> None:
76 self.get_lexer = get_lexer
77 self._dummy = SimpleLexer()
78
79 def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]:
80 lexer = self.get_lexer() or self._dummy
81 return lexer.lex_document(document)
82
83 def invalidation_hash(self) -> Hashable:
84 lexer = self.get_lexer() or self._dummy
85 return id(lexer)