I am creating a REPL tool for my project that (simplified for clarity) either directly executes entered commands or (if a command ".x some/path/to/file" is entered) reads and executes them from file. My question is related to auto-completing the user input (using prompt_toolkit).
I have something like (minimum executable example):
import prompt_toolkit
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.document import Document
from prompt_toolkit.contrib.completers import PathCompleter
class CommandCompleter(Completer):
def __init__(self):
self.path_completer = PathCompleter()
self.commands = [".x", "command1", "command2"]
def get_completions(self, document, complete_event):
if document.text.startswith(".x "):
sub_doc = Document(document.text[3:])
yield from (Completion(cmd.text, -document.cursor_position)
# ???????? ?????????????????????????
for cmd
in self.path_completer.get_completions(sub_doc, complete_event))
# ???????
else:
yield from (Completion(cmd, -document.cursor_position)
for cmd in self.commands
if cmd.startswith(document.text))
if __name__ == "__main__":
while True:
other_args = {}
input = prompt_toolkit.prompt(">>> ", completer=CommandCompleter(), **other_args)
# Do something with input (omitted)
The second if-branch (for commands) works correctly but I don't know how to properly call the PathCompleter.get_completions()
method and reconstruct the Completion
objects from its result (where the ???'s are) in the first branch. The trick is that I am using the completion only for a part of the input and various sub-stringing, position calculations etc. did not (yet) lead to the satisfactory behaviour (i.e. offering the paths and constructing the correct input line).
I will definitely go on searching but if anyone knows how to rewrite this, it would be very useful.
Note: yield from self.path_completer.get_completions(document, complete_event)
would be used if the whole input would be just the path (and this works correctly).
Probably the following should fix it:
sub_doc = Document(document.text[3:])
yield from (Completion(completion.text, completion.start_position, display=completion.display)
for completion
in self.path_completer.get_completions(sub_doc, complete_event))
completion.text
contains the text that is going to be inserted;completion.start_position
contains the place where the text is going to be inserted, relative to the cursor position (in this particular example we can take the value from the nested completer). completion.display
is the value displayed in the pop-up menu. (In this case, the whole filename, rather than only the inserted string.Feel free to open a GitHub issue if you have any more questions.