|
from __future__ import annotations |
|
|
|
from typing import Callable, Iterable, Mapping, Pattern, Sequence |
|
|
|
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: Sequence[str] | Callable[[], Sequence[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]: |
|
|
|
words = self.words |
|
if callable(words): |
|
words = words() |
|
|
|
|
|
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, |
|
) |
|
|