class CommandAutoSuggest(AutoSuggest): """Atomix command/resource/action auto-suggester.""" def __init__(self, commands): self.commands = commands self.history_suggester = AutoSuggestFromHistory() def get_suggestion(self, cli, buffer, document): in_quote = False for word in document.text.split(' '): if not in_quote and word.startswith('"'): in_quote = True elif in_quote and word.endswith('"'): in_quote = False # If the last word is in a quote, don't suggest anything. if in_quote: return None args = shlex.split(document.text) if len(args) == 0: return None args = list(args) if document.text.endswith(' '): args.append('') command_name = args.pop(0) if len(args) == 0: for name in sorted(self.commands.commands): if name.startswith(command_name): return Suggestion(name[len(command_name):]) try: command = self.commands.command(command_name) except UnknownCommand: return None suggestion = command.suggest_tail(*args) if suggestion is None: return self.history_suggester.get_suggestion(cli, buffer, document) return Suggestion(suggestion)
class PromptToolkitCompleter(Completer): """Simple prompt_toolkit Completer object. It just redirects requests to normal Xonsh completer. """ def __init__(self, completer, ctx, shell): """Takes instance of xonsh.completer.Completer, the xonsh execution context, and the shell instance itself. """ self.completer = completer self.ctx = ctx self.shell = shell self.hist_suggester = AutoSuggestFromHistory() def get_completions(self, document, complete_event): """Returns a generator for list of completions.""" env = XSH.env should_complete = complete_event.completion_requested or env.get( "UPDATE_COMPLETIONS_ON_KEYPRESS") # Only generate completions when the user hits tab. if not should_complete or self.completer is None: return # generate actual completions line = document.current_line endidx = document.cursor_position_col line_ex = XSH.aliases.expand_alias(line, endidx) begidx = line[:endidx].rfind(" ") + 1 if line[:endidx].rfind( " ") >= 0 else 0 prefix = line[begidx:endidx] expand_offset = len(line_ex) - len(line) multiline_text = document.text cursor_index = document.cursor_position if line != line_ex: line_start = cursor_index - len( document.current_line_before_cursor) multiline_text = (multiline_text[:line_start] + line_ex + multiline_text[line_start + len(line):]) cursor_index += expand_offset # get normal completions completions, plen = self.completer.complete( prefix, line_ex, begidx + expand_offset, endidx + expand_offset, self.ctx, multiline_text=multiline_text, cursor_index=cursor_index, ) # completions from auto suggest sug_comp = None if env.get("AUTO_SUGGEST") and env.get("AUTO_SUGGEST_IN_COMPLETIONS"): sug_comp = self.suggestion_completion(document, line) if sug_comp is None: pass elif len(completions) == 0: completions = (sug_comp, ) else: completions = set(completions) completions.discard(sug_comp) completions = (sug_comp, ) + tuple(sorted(completions)) # 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 if sug_comp is None: pre = min(document.cursor_position_col - begidx, len(c_prefix)) else: pre = len(c_prefix) for comp in completions: # do not display quote if isinstance(comp, RichCompletion): # ptk doesn't render newlines. This can be removed once it is supported. desc = (comp.description.replace(os.linesep, " ") if comp.description else None) yield Completion( comp, -comp.prefix_len if comp.prefix_len is not None else -plen, display=comp.display or comp[pre:].strip("'\""), display_meta=desc, style=comp.style or "", ) elif isinstance(comp, Completion): yield comp else: disp = comp[pre:].strip("'\"") yield Completion(comp, -plen, display=disp) def suggestion_completion(self, document, line): """Provides a completion based on the current auto-suggestion.""" app = self.shell.prompter.app sug = self.hist_suggester.get_suggestion(app.current_buffer, document) if sug is None: return None comp, _, _ = sug.text.partition(" ") _, _, prev = line.rpartition(" ") return prev + comp def reserve_space(self): """Adjust the height for showing autocompletion menu.""" app = get_app() render = app.renderer window = None try: # this raises error sometimes when COMPLETE_WHILE_TYPE is enabled window = app.layout.current_window except Exception: pass if window and window.render_info: h = window.render_info.content_height r = XSH.env.get("COMPLETIONS_MENU_ROWS") size = h + r last_h = render._last_screen.height if render._last_screen else 0 last_h = max(render._min_available_height, last_h) if last_h < size: if render._last_screen: render._last_screen.height = size
class PromptToolkitCompleter(Completer): """Simple prompt_toolkit Completer object. It just redirects requests to normal Xonsh completer. """ def __init__(self, completer, ctx, shell): """Takes instance of xonsh.completer.Completer, the xonsh execution context, and the shell instance itself. """ self.completer = completer self.ctx = ctx self.shell = shell self.hist_suggester = AutoSuggestFromHistory() def get_completions(self, document, complete_event): """Returns a generator for list of completions.""" env = builtins.__xonsh_env__ should_complete = (complete_event.completion_requested or env.get('UPDATE_COMPLETIONS_ON_KEYPRESS')) # Only generate completions when the user hits tab. if not should_complete or self.completer is None: return # 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 sug_comp = None if env.get('AUTO_SUGGEST') and env.get('AUTO_SUGGEST_IN_COMPLETIONS'): sug_comp = self.suggestion_completion(document, line) if sug_comp is None: pass elif len(completions) == 0: completions = (sug_comp, ) else: completions = set(completions) completions.discard(sug_comp) completions = (sug_comp, ) + tuple(sorted(completions)) # 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 if sug_comp is None: pre = min(document.cursor_position_col - begidx, len(c_prefix)) else: pre = len(c_prefix) for comp in completions: # do not display quote disp = comp[pre:].strip('\'"') yield Completion(comp, -l, display=disp) def suggestion_completion(self, document, line): """Provides a completion based on the current auto-suggestion.""" cli = self.shell.prompter.cli sug = self.hist_suggester.get_suggestion(cli, cli.current_buffer, document) if sug is None: return None comp, _, _ = sug.text.partition(' ') _, _, prev = line.rpartition(' ') return prev + comp def reserve_space(self): cli = builtins.__xonsh_shell__.shell.prompter.cli window = cli.application.layout.children[0].content.children[1] if window and window.render_info: h = window.render_info.content_height r = builtins.__xonsh_env__.get('COMPLETIONS_MENU_ROWS') size = h + r def comp_height(cli): # If there is an autocompletion menu to be shown, make sure that o # layout has at least a minimal height in order to display it. if not cli.is_done: return LayoutDimension(min=size) else: return LayoutDimension() window._height = comp_height
class PromptToolkitCompleter(Completer): """Simple prompt_toolkit Completer object. It just redirects requests to normal Xonsh completer. """ def __init__(self, completer, ctx, shell): """Takes instance of xonsh.completer.Completer, the xonsh execution context, and the shell instance itself. """ self.completer = completer self.ctx = ctx self.shell = shell self.hist_suggester = AutoSuggestFromHistory() def get_completions(self, document, complete_event): """Returns a generator for list of completions.""" env = builtins.__xonsh__.env should_complete = complete_event.completion_requested or env.get( "UPDATE_COMPLETIONS_ON_KEYPRESS" ) # Only generate completions when the user hits tab. if not should_complete or self.completer is None: return # 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 sug_comp = None if env.get("AUTO_SUGGEST") and env.get("AUTO_SUGGEST_IN_COMPLETIONS"): sug_comp = self.suggestion_completion(document, line) if sug_comp is None: pass elif len(completions) == 0: completions = (sug_comp,) else: completions = set(completions) completions.discard(sug_comp) completions = (sug_comp,) + tuple(sorted(completions)) # 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 if sug_comp is None: pre = min(document.cursor_position_col - begidx, len(c_prefix)) else: pre = len(c_prefix) for comp in completions: # do not display quote disp = comp[pre:].strip("'\"") yield Completion(comp, -l, display=disp) def suggestion_completion(self, document, line): """Provides a completion based on the current auto-suggestion.""" app = self.shell.prompter.app sug = self.hist_suggester.get_suggestion(app.current_buffer, document) if sug is None: return None comp, _, _ = sug.text.partition(" ") _, _, prev = line.rpartition(" ") return prev + comp def reserve_space(self): """Adjust the height for showing autocompletion menu.""" app = get_app() render = app.renderer window = app.layout.container.children[0].content.children[1].content if window and window.render_info: h = window.render_info.content_height r = builtins.__xonsh__.env.get("COMPLETIONS_MENU_ROWS") size = h + r last_h = render._last_screen.height if render._last_screen else 0 last_h = max(render._min_available_height, last_h) if last_h < size: if render._last_screen: render._last_screen.height = size
class PromptToolkitCompleter(Completer): """Simple prompt_toolkit Completer object. It just redirects requests to normal Xonsh completer. """ def __init__(self, completer, ctx, shell): """Takes instance of xonsh.completer.Completer, the xonsh execution context, and the shell instance itself. """ self.completer = completer self.ctx = ctx self.shell = shell self.hist_suggester = AutoSuggestFromHistory() def get_completions(self, document, complete_event): """Returns a generator for list of completions.""" env = builtins.__xonsh_env__ should_complete = ( complete_event.completion_requested or env.get('UPDATE_COMPLETIONS_ON_KEYPRESS') ) # Only generate completions when the user hits tab. if not should_complete or self.completer is None: return # 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 sug_comp = None 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: completions = set(completions) completions.discard(sug_comp) completions = (sug_comp,) + tuple(sorted(completions)) # 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 if sug_comp is None: pre = min(document.cursor_position_col - begidx, len(c_prefix)) else: pre = len(c_prefix) for comp in completions: # do not display quote disp = comp[pre:].strip('\'"') yield Completion(comp, -l, display=disp) def suggestion_completion(self, document, line): """Provides a completion based on the current auto-suggestion.""" cli = self.shell.prompter.cli sug = self.hist_suggester.get_suggestion(cli, cli.current_buffer, document) if sug is None: return None comp, _, _ = sug.text.partition(' ') _, _, prev = line.rpartition(' ') return prev + comp def reserve_space(self): cli = builtins.__xonsh_shell__.shell.prompter.cli window = cli.application.layout.children[0].content.children[1] if window and window.render_info: h = window.render_info.content_height r = builtins.__xonsh_env__.get('COMPLETIONS_MENU_ROWS') size = h + r def comp_height(cli): # If there is an autocompletion menu to be shown, make sure that o # layout has at least a minimal height in order to display it. if not cli.is_done: return LayoutDimension(min=size) else: return LayoutDimension() window._height = comp_height