1from __future__ import annotations 
    2 
    3from typing import Callable, Iterable, Mapping, Pattern, Sequence 
    4 
    5from prompt_toolkit.completion import CompleteEvent, Completer, Completion 
    6from prompt_toolkit.document import Document 
    7from prompt_toolkit.formatted_text import AnyFormattedText 
    8 
    9__all__ = [ 
    10    "WordCompleter", 
    11] 
    12 
    13 
    14class WordCompleter(Completer): 
    15    """ 
    16    Simple autocompletion on a list of words. 
    17 
    18    :param words: List of words or callable that returns a list of words. 
    19    :param ignore_case: If True, case-insensitive completion. 
    20    :param meta_dict: Optional dict mapping words to their meta-text. (This 
    21        should map strings to strings or formatted text.) 
    22    :param WORD: When True, use WORD characters. 
    23    :param sentence: When True, don't complete by comparing the word before the 
    24        cursor, but by comparing all the text before the cursor. In this case, 
    25        the list of words is just a list of strings, where each string can 
    26        contain spaces. (Can not be used together with the WORD option.) 
    27    :param match_middle: When True, match not only the start, but also in the 
    28                         middle of the word. 
    29    :param pattern: Optional compiled regex for finding the word before 
    30        the cursor to complete. When given, use this regex pattern instead of 
    31        default one (see document._FIND_WORD_RE) 
    32    """ 
    33 
    34    def __init__( 
    35        self, 
    36        words: Sequence[str] | Callable[[], Sequence[str]], 
    37        ignore_case: bool = False, 
    38        display_dict: Mapping[str, AnyFormattedText] | None = None, 
    39        meta_dict: Mapping[str, AnyFormattedText] | None = None, 
    40        WORD: bool = False, 
    41        sentence: bool = False, 
    42        match_middle: bool = False, 
    43        pattern: Pattern[str] | None = None, 
    44    ) -> None: 
    45        assert not (WORD and sentence) 
    46 
    47        self.words = words 
    48        self.ignore_case = ignore_case 
    49        self.display_dict = display_dict or {} 
    50        self.meta_dict = meta_dict or {} 
    51        self.WORD = WORD 
    52        self.sentence = sentence 
    53        self.match_middle = match_middle 
    54        self.pattern = pattern 
    55 
    56    def get_completions( 
    57        self, document: Document, complete_event: CompleteEvent 
    58    ) -> Iterable[Completion]: 
    59        # Get list of words. 
    60        words = self.words 
    61        if callable(words): 
    62            words = words() 
    63 
    64        # Get word/text before cursor. 
    65        if self.sentence: 
    66            word_before_cursor = document.text_before_cursor 
    67        else: 
    68            word_before_cursor = document.get_word_before_cursor( 
    69                WORD=self.WORD, pattern=self.pattern 
    70            ) 
    71 
    72        if self.ignore_case: 
    73            word_before_cursor = word_before_cursor.lower() 
    74 
    75        def word_matches(word: str) -> bool: 
    76            """True when the word before the cursor matches.""" 
    77            if self.ignore_case: 
    78                word = word.lower() 
    79 
    80            if self.match_middle: 
    81                return word_before_cursor in word 
    82            else: 
    83                return word.startswith(word_before_cursor) 
    84 
    85        for a in words: 
    86            if word_matches(a): 
    87                display = self.display_dict.get(a, a) 
    88                display_meta = self.meta_dict.get(a, "") 
    89                yield Completion( 
    90                    text=a, 
    91                    start_position=-len(word_before_cursor), 
    92                    display=display, 
    93                    display_meta=display_meta, 
    94                )