Spaces:
Sleeping
Sleeping
first-space
/
first-space-venv
/lib
/python3.12
/site-packages
/prompt_toolkit
/completion
/filesystem.py
from __future__ import annotations | |
import os | |
from typing import Callable, Iterable | |
from prompt_toolkit.completion import CompleteEvent, Completer, Completion | |
from prompt_toolkit.document import Document | |
__all__ = [ | |
"PathCompleter", | |
"ExecutableCompleter", | |
] | |
class PathCompleter(Completer): | |
""" | |
Complete for Path variables. | |
:param get_paths: Callable which returns a list of directories to look into | |
when the user enters a relative path. | |
:param file_filter: Callable which takes a filename and returns whether | |
this file should show up in the completion. ``None`` | |
when no filtering has to be done. | |
:param min_input_len: Don't do autocompletion when the input string is shorter. | |
""" | |
def __init__( | |
self, | |
only_directories: bool = False, | |
get_paths: Callable[[], list[str]] | None = None, | |
file_filter: Callable[[str], bool] | None = None, | |
min_input_len: int = 0, | |
expanduser: bool = False, | |
) -> None: | |
self.only_directories = only_directories | |
self.get_paths = get_paths or (lambda: ["."]) | |
self.file_filter = file_filter or (lambda _: True) | |
self.min_input_len = min_input_len | |
self.expanduser = expanduser | |
def get_completions( | |
self, document: Document, complete_event: CompleteEvent | |
) -> Iterable[Completion]: | |
text = document.text_before_cursor | |
# Complete only when we have at least the minimal input length, | |
# otherwise, we can too many results and autocompletion will become too | |
# heavy. | |
if len(text) < self.min_input_len: | |
return | |
try: | |
# Do tilde expansion. | |
if self.expanduser: | |
text = os.path.expanduser(text) | |
# Directories where to look. | |
dirname = os.path.dirname(text) | |
if dirname: | |
directories = [ | |
os.path.dirname(os.path.join(p, text)) for p in self.get_paths() | |
] | |
else: | |
directories = self.get_paths() | |
# Start of current file. | |
prefix = os.path.basename(text) | |
# Get all filenames. | |
filenames = [] | |
for directory in directories: | |
# Look for matches in this directory. | |
if os.path.isdir(directory): | |
for filename in os.listdir(directory): | |
if filename.startswith(prefix): | |
filenames.append((directory, filename)) | |
# Sort | |
filenames = sorted(filenames, key=lambda k: k[1]) | |
# Yield them. | |
for directory, filename in filenames: | |
completion = filename[len(prefix) :] | |
full_name = os.path.join(directory, filename) | |
if os.path.isdir(full_name): | |
# For directories, add a slash to the filename. | |
# (We don't add them to the `completion`. Users can type it | |
# to trigger the autocompletion themselves.) | |
filename += "/" | |
elif self.only_directories: | |
continue | |
if not self.file_filter(full_name): | |
continue | |
yield Completion( | |
text=completion, | |
start_position=0, | |
display=filename, | |
) | |
except OSError: | |
pass | |
class ExecutableCompleter(PathCompleter): | |
""" | |
Complete only executable files in the current path. | |
""" | |
def __init__(self) -> None: | |
super().__init__( | |
only_directories=False, | |
min_input_len=1, | |
get_paths=lambda: os.environ.get("PATH", "").split(os.pathsep), | |
file_filter=lambda name: os.access(name, os.X_OK), | |
expanduser=True, | |
) | |