Merge pull request #2218 from xonsh/ghost-in-the-sh

Complete based on autosuggestion
This commit is contained in:
Morten Enemark Lund 2017-02-19 14:03:54 +01:00 committed by GitHub
commit 11593f80ac
3 changed files with 74 additions and 38 deletions

14
news/ghost-in-the-sh.rst Normal file
View file

@ -0,0 +1,14 @@
**Added:** None
**Changed:**
* The prompt toolkit shell's first completion will now be the
current token from the auto-suggetion, if available.
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

View file

@ -5,6 +5,7 @@ import builtins
from prompt_toolkit.layout.dimension import LayoutDimension from prompt_toolkit.layout.dimension import LayoutDimension
from prompt_toolkit.completion import Completer, Completion from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
class PromptToolkitCompleter(Completer): class PromptToolkitCompleter(Completer):
@ -13,54 +14,75 @@ class PromptToolkitCompleter(Completer):
It just redirects requests to normal Xonsh completer. It just redirects requests to normal Xonsh completer.
""" """
def __init__(self, completer, ctx): def __init__(self, completer, ctx, shell):
"""Takes instance of xonsh.completer.Completer and dict with context.""" """Takes instance of xonsh.completer.Completer, the xonsh execution
context, and the shell instance itself.
"""
self.completer = completer self.completer = completer
self.ctx = ctx self.ctx = ctx
self.shell = shell
self.hist_suggester = AutoSuggestFromHistory()
def get_completions(self, document, complete_event): def get_completions(self, document, complete_event):
"""Returns a generator for list of completions.""" """Returns a generator for list of completions."""
env = builtins.__xonsh_env__
should_complete = ( should_complete = (
complete_event.completion_requested or complete_event.completion_requested or
builtins.__xonsh_env__.get('UPDATE_COMPLETIONS_ON_KEYPRESS') env.get('UPDATE_COMPLETIONS_ON_KEYPRESS')
) )
# Only generate completions when the user hits tab. # Only generate completions when the user hits tab.
if should_complete: if not should_complete or self.completer is None:
if self.completer is None: return
yield from [] # generate actual completions
line = document.current_line.lstrip()
line_ex = builtins.aliases.expand_alias(line)
endidx = document.cursor_position_col
begidx = (line[:endidx].rfind(' ') + 1
if line[:endidx].rfind(' ') >= 0 else 0)
prefix = line[begidx:endidx]
expand_offset = len(line_ex) - len(line)
# get normal completions
completions, l = self.completer.complete(prefix, line_ex,
begidx + expand_offset,
endidx + expand_offset,
self.ctx)
# completions from auto suggest
if env.get('AUTO_SUGGEST'):
sug_comp = self.suggestion_completion(document, line)
if sug_comp is None:
pass
elif len(completions) == 0:
completions = (sug_comp,)
else: else:
line = document.current_line.lstrip() completions = (sug_comp,) + completions
line_ex = builtins.aliases.expand_alias(line) # reserve space, if needed.
if len(completions) <= 1:
pass
elif len(os.path.commonprefix(completions)) <= len(prefix):
self.reserve_space()
# Find common prefix (strip quoting)
c_prefix = os.path.commonprefix([a.strip('\'"') for a in completions])
# Find last split symbol, do not trim the last part
while c_prefix:
if c_prefix[-1] in r'/\.:@,':
break
c_prefix = c_prefix[:-1]
# yield completions
for comp in completions:
# do not display quote
disp = comp.strip('\'"')[len(c_prefix):]
yield Completion(comp, -l, display=disp)
endidx = document.cursor_position_col def suggestion_completion(self, document, line):
begidx = (line[:endidx].rfind(' ') + 1 """Provides a completion based on the current auto-suggestion."""
if line[:endidx].rfind(' ') >= 0 else 0) cli = self.shell.prompter.cli
prefix = line[begidx:endidx] sug = self.hist_suggester.get_suggestion(cli, cli.current_buffer, document)
expand_offset = len(line_ex) - len(line) if sug is None:
return None
completions, l = self.completer.complete(prefix, comp, _, _ = sug.text.partition(' ')
line_ex, _, _, prev = line.rpartition(' ')
begidx + expand_offset, return prev + comp
endidx + expand_offset,
self.ctx)
if len(completions) <= 1:
pass
elif len(os.path.commonprefix(completions)) <= len(prefix):
self.reserve_space()
# Find common prefix (strip quoting)
c_prefix = os.path.commonprefix([a.strip('\'"')
for a in completions])
# Find last split symbol, do not trim the last part
while c_prefix:
if c_prefix[-1] in r'/\.:@,':
break
c_prefix = c_prefix[:-1]
for comp in completions:
# do not display quote
disp = comp.strip('\'"')[len(c_prefix):]
yield Completion(comp, -l, display=disp)
def reserve_space(self): def reserve_space(self):
cli = builtins.__xonsh_shell__.shell.prompter.cli cli = builtins.__xonsh_shell__.shell.prompter.cli

View file

@ -38,7 +38,7 @@ class PromptToolkitShell(BaseShell):
super().__init__(**kwargs) super().__init__(**kwargs)
self.prompter = Prompter() self.prompter = Prompter()
self.history = PromptToolkitHistory() self.history = PromptToolkitHistory()
self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx) self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx, self)
key_bindings_manager_args = { key_bindings_manager_args = {
'enable_auto_suggest_bindings': True, 'enable_auto_suggest_bindings': True,
'enable_search': True, 'enable_search': True,