from __future__ import annotations from typing import Callable, Iterable, Mapping, Pattern from prompt_toolkit.completion import CompleteEvent, Completer, Completion from prompt_toolkit.document import Document from prompt_toolkit.formatted_text import AnyFormattedText __all__ = [ "WordCompleter", ] class WordCompleter(Completer): """ Simple autocompletion on a list of words. :param words: List of words or callable that returns a list of words. :param ignore_case: If True, case-insensitive completion. :param meta_dict: Optional dict mapping words to their meta-text. (This should map strings to strings or formatted text.) :param WORD: When True, use WORD characters. :param sentence: When True, don't complete by comparing the word before the cursor, but by comparing all the text before the cursor. In this case, the list of words is just a list of strings, where each string can contain spaces. (Can not be used together with the WORD option.) :param match_middle: When True, match not only the start, but also in the middle of the word. :param pattern: Optional compiled regex for finding the word before the cursor to complete. When given, use this regex pattern instead of default one (see document._FIND_WORD_RE) """ def __init__( self, words: list[str] | Callable[[], list[str]], ignore_case: bool = False, display_dict: Mapping[str, AnyFormattedText] | None = None, meta_dict: Mapping[str, AnyFormattedText] | None = None, WORD: bool = False, sentence: bool = False, match_middle: bool = False, pattern: Pattern[str] | None = None, ) -> None: assert not (WORD and sentence) self.words = words self.ignore_case = ignore_case self.display_dict = display_dict or {} self.meta_dict = meta_dict or {} self.WORD = WORD self.sentence = sentence self.match_middle = match_middle self.pattern = pattern def get_completions( self, document: Document, complete_event: CompleteEvent ) -> Iterable[Completion]: # Get list of words. words = self.words if callable(words): words = words() # Get word/text before cursor. if self.sentence: word_before_cursor = document.text_before_cursor else: word_before_cursor = document.get_word_before_cursor( WORD=self.WORD, pattern=self.pattern ) if self.ignore_case: word_before_cursor = word_before_cursor.lower() def word_matches(word: str) -> bool: """True when the word before the cursor matches.""" if self.ignore_case: word = word.lower() if self.match_middle: return word_before_cursor in word else: return word.startswith(word_before_cursor) for a in words: if word_matches(a): display = self.display_dict.get(a, a) display_meta = self.meta_dict.get(a, "") yield Completion( text=a, start_position=-len(word_before_cursor), display=display, display_meta=display_meta, )