def autocomplete(self, line): """Given a line, return a list of suggestions.""" current_length = len(line) self._current_line = line if current_length == 1 and self._last_position > 1: # Reset state. This is likely from a user completing # a previous command. self.reset() elif current_length < self._last_position: # The user has hit backspace. We'll need to check # the current words. return self._handle_backspace() elif not line: return [] elif current_length != self._last_position + 1: return self._complete_from_full_parse() # This position is important. We only update the _last_position # after we've checked the special cases above where that value # matters. self._last_position = len(line) if line and not line.strip(): # Special case, the user hits a space on a new line so # we autocomplete all the top level commands. return self._current['commands'] last_word = line.split()[-1] if last_word in self.arg_metadata or last_word in self._global_options: # The last thing we completed was an argument, record # this as self.last_arg self.last_option = last_word if line[-1] == ' ': # At this point the user has autocompleted a command # or an argument and has hit space. If they've # just completed a command, we need to change the # current context and traverse into the subcommand. # "ec2 " # ^--here, need to traverse into "ec2" # # Otherwise: # "ec2 --no-validate-ssl " # ^-- here, stay on "ec2" context. if not last_word.startswith('-'): next_command = self._current['children'].get(last_word) if next_command is not None: self._current = next_command self._current_name = last_word self.cmd_path.append(self._current_name) elif last_word in self.arg_metadata and \ self.arg_metadata[last_word]['example']: # Then this is an arg with a shorthand example so we'll # suggest that example. return [self.arg_metadata[last_word]['example']] # Even if we don't change context, we still want to # autocomplete all the commands for the current context # in either of the above two cases. return self._current['commands'][:] elif last_word.startswith('-'): # TODO: cache this for the duration of the current context. # We don't need to recompute this until the args are # different. all_args = self._get_all_args() if self.match_fuzzy: return fuzzy_search(last_word, all_args) else: return substring_search(last_word, all_args) if self.match_fuzzy: return fuzzy_search(last_word, self._current['commands']) else: return substring_search(last_word, self._current['commands'])
def test_subsequences(search, corpus, expected): actual = substring_search(search, corpus) assert actual == expected