def get_completions(self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: # Get list of words. words = self.words if callable(words): words = words() # Get word/text before cursor. if self.sentence: word_before_cursor = document.text_before_cursor else: word_before_cursor = document.get_word_before_cursor( WORD=self.WORD, pattern=self.pattern) if self.ignore_case: word_before_cursor = word_before_cursor.lower() def word_matches(word: str) -> bool: """ True when the word before the cursor matches. """ if self.ignore_case: word = word.lower() if self.match_middle: return word_before_cursor in word else: return word.startswith(word_before_cursor) for a in words: if word_matches(a): display_meta = self.meta_dict.get(a, "") yield Completion(a, -len(word_before_cursor), display_meta=display_meta)
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Completion: """ The main method that gets called by prompt-toolkit to determine which completions to show. This :param document: :param complete_event: :return: """ # get the stuff we have typed so far word_before_cursor = document.get_word_before_cursor() # if this is an os command, we can't complete anything if document.text.startswith('!'): return commands = self.find_completions(document) # if there are no commands return if len(commands) <= 0: return # sort alphabetically commands = collections.OrderedDict(sorted(list(commands.items()), key=lambda t: t[0])) # loop the commands that we have determined to be useful # based on the current text input and populate a 'meta' field # if one exists. for cmd, extra in commands.items(): meta = extra['meta'] if type(extra) is dict and 'meta' in extra else None # finally, yield the generator for completions yield Completion(cmd, -(len(word_before_cursor)), display_meta=meta)
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Completion: """ Get Completions This will function will provide the completions for param types and function name :param document: :param complete_event: :return: """ word = document.get_word_before_cursor() if document.find_previous_word_ending() is None: for _command in safe_commands: # note: force lower() to function as ignore_case. if _command.startswith(word.lower()): if _command in safe_commands_arguments: safe_command = safe_commands_arguments[_command] safe_argument_color = safe_color_arguments.get( safe_command, 'default') display = HTML('<b><ansired> > </ansired>%s</b> <' + safe_argument_color + '>%s</' + safe_argument_color + '>') % (_command, safe_command) yield Completion(_command, start_position=-len(word), display=display, display_meta=meta.get(_command))
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: text = document.get_word_before_cursor(True) # 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(completion, 0, display=filename) except OSError: pass
def get_completions(self, document: Document, _: CompleteEvent) -> Iterable[Completion]: """Yield successive REPL completions for Prompt Toolkit.""" word_before_cursor = document.get_word_before_cursor( pattern=_DELIMITED_WORD_PATTERN) completions = runtime.repl_completions(word_before_cursor) or () for completion in completions: yield Completion(completion, start_position=-len(word_before_cursor))
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: parts = shlex.split(document.text.lstrip(" ")) suffix = document.text[len(document.text.rstrip(" ")):] if suffix: parts.append(suffix) with open("/tmp/xxx", "a") as wf: print(document, "@", parts, "@", complete_event, file=wf) # Get word/text before cursor. word_before_cursor = document.get_word_before_cursor(WORD=True, pattern=None) # display_meta = self.meta_dict.get(a, "") display_meta = None if not parts: for a in ["send", "recv"]: yield Completion(a, -len(word_before_cursor), display_meta=display_meta) elif len(parts) == 1 and not parts[0].startswith("-"): for a in ["send", "recv"]: if a.startswith(parts[0]): yield Completion(a, -len(word_before_cursor), display_meta=display_meta) elif parts[-1].startswith("@"): suffix = parts[-1][1:] if "/" in suffix: dirpath = pathlib.Path(suffix).parent else: dirpath = pathlib.Path(".") for a in dirpath.iterdir(): if not suffix or suffix in str(a): yield Completion(f"@{a}", -len(word_before_cursor), display_meta=display_meta) elif parts[-1].startswith("-"): for a in ["--name", "--data"]: if a.startswith(parts[-1]): yield Completion(a, -len(word_before_cursor), display_meta=display_meta) elif not parts[-1].strip() and parts[-2] == "--data": yield Completion( "@", -len(word_before_cursor), display_meta=display_meta, ) else: return []
def _get_fuzzy_completions( self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: word_before_cursor = document.get_word_before_cursor( pattern=re.compile(self._get_pattern())) # Get completions document2 = Document( text=document.text[:document.cursor_position - len(word_before_cursor)], cursor_position=document.cursor_position - len(word_before_cursor), ) completions = list( self.completer.get_completions(document2, complete_event)) fuzzy_matches: List[_FuzzyMatch] = [] pat = ".*?".join(map(re.escape, word_before_cursor)) pat = "(?=({0}))".format( pat) # lookahead regex to manage overlapping matches regex = re.compile(pat, re.IGNORECASE) for compl in completions: matches = list(regex.finditer(compl.text)) if matches: # Prefer the match, closest to the left, then shortest. best = min(matches, key=lambda m: (m.start(), len(m.group(1)))) fuzzy_matches.append( _FuzzyMatch(len(best.group(1)), best.start(), compl)) def sort_key(fuzzy_match: "_FuzzyMatch") -> Tuple[int, int]: "Sort by start position, then by the length of the match." return fuzzy_match.start_pos, fuzzy_match.match_length fuzzy_matches = sorted(fuzzy_matches, key=sort_key) for match in fuzzy_matches: # Include these completions, but set the correct `display` # attribute and `start_position`. yield Completion( text=match.completion.text, start_position=match.completion.start_position - len(word_before_cursor), display_meta=match.completion.display_meta, display=self._get_display(match, word_before_cursor), style=match.completion.style, )
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Completion: commands = {} # get the stuff we have typed so far before_cursor = document.get_word_before_cursor() commands.update(self.find_completions(document)) # if there are commands, show them if len(commands) > 0: commands = collections.OrderedDict( sorted(list(commands.items()), key=lambda t: t[0])) for cmd, extra in commands.items(): desc = extra['desc'] if type( extra) is dict and 'desc' in extra else None # finally, yield the generator for completions yield Completion(cmd, -(len(before_cursor)), display_meta=desc)
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: with open("/tmp/xxx", "a") as wf: print(document, document.text, complete_event, file=wf) # Get word/text before cursor. if self.sentence: word_before_cursor = document.text_before_cursor else: word_before_cursor = document.get_word_before_cursor(WORD=True, pattern=None) # display_meta = self.meta_dict.get(a, "") display_meta = None for a in ["--name", "--data"]: yield Completion(a, -len(word_before_cursor), display_meta=display_meta)
def find_completions(self, document: Document) -> dict: tokens = get_tokens(document.text) suggestions = {} current = self.commands for token in tokens: candidate = token.lower() if candidate in list(current.keys()): # if there are sub commands, grab them if 'cmds' in current[candidate]: current = current[candidate]['cmds'] else: return {} if current and len(current) > 0: for k, _ in current.items(): # fuzzy-ish matching when part of a word is in a suggestion if document.get_word_before_cursor().lower() in k.lower(): suggestions[k] = current[k] return suggestions
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: # Get list of words. words = self.words if callable(words): words = words() # Get word/text before cursor. if self.sentence: word_before_cursor = document.text_before_cursor else: word_before_cursor = document.get_word_before_cursor( WORD=True, pattern=self.pattern) if self.ignore_case: word_before_cursor = word_before_cursor.lower() def word_matches(word: str, meta_data=None) -> bool: """ True when the word before the cursor matches. """ if self.ignore_case: word = word.lower() raw = get_raw_text(meta_data) if self.match_middle: return (word_before_cursor in word and word_before_cursor != word) \ or raw is not None and word_before_cursor in raw else: return ( word.startswith(word_before_cursor) and word_before_cursor != word ) or raw is not None and raw.startswith(word_before_cursor) for a in words: display_meta = self.meta_dict.get(a, None) if word_matches(a, display_meta): if " " in a: aa = a.replace("\"", '\\"') yield Completion(f'"{aa}"', display=a, display_meta=display_meta) else: yield Completion(a, -len(word_before_cursor), display_meta=display_meta)
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: # Get list of words. words = self.words if callable(words): words = words() # Get word/text before cursor. if self.sentence: word_before_cursor = document.text_before_cursor else: word_before_cursor = document.get_word_before_cursor( WORD=self.WORD, pattern=self.pattern) if self.ignore_case: word_before_cursor = word_before_cursor.lower() def word_matches(word: str) -> bool: """ True when the word before the cursor matches. """ if self.ignore_case: word = word.lower() if self.match_middle: return word_before_cursor in word else: return word.startswith(word_before_cursor) for a in words: if word_matches(a): text = '' if self.help_info: if a in self.help_info: try: text = self.help_info[a]['help'] except: # 忽略错误信息 pass yield Completion(a, -len(word_before_cursor), display=HTML('<b>' + a + '</b>---' + text + ''))
def _get_fuzzy_completions( self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: word_before_cursor = document.get_word_before_cursor( pattern=re.compile(self._get_pattern())) # Get completions document2 = Document( text=document.text[:document.cursor_position - len(word_before_cursor)], cursor_position=document.cursor_position - len(word_before_cursor)) completions = list(self.completer.get_completions(document2, complete_event)) fuzzy_matches: List[_FuzzyMatch] = [] pat = '.*?'.join(map(re.escape, word_before_cursor)) pat = '(?=({0}))'.format(pat) # lookahead regex to manage overlapping matches regex = re.compile(pat, re.IGNORECASE) for compl in completions: matches = list(regex.finditer(compl.text)) if matches: # Prefer the match, closest to the left, then shortest. best = min(matches, key=lambda m: (m.start(), len(m.group(1)))) fuzzy_matches.append(_FuzzyMatch(len(best.group(1)), best.start(), compl)) def sort_key(fuzzy_match: '_FuzzyMatch') -> Tuple[int, int]: " Sort by start position, then by the length of the match. " return fuzzy_match.start_pos, fuzzy_match.match_length fuzzy_matches = sorted(fuzzy_matches, key=sort_key) for match in fuzzy_matches: # Include these completions, but set the correct `display` # attribute and `start_position`. yield Completion( match.completion.text, start_position=match.completion.start_position - len(word_before_cursor), display_meta=match.completion.display_meta, display=self._get_display(match, word_before_cursor), style=match.completion.style)
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: parts = shlex.split(document.text) suffix = document.text[len(document.text.rstrip(" ")):] if suffix: parts.append(suffix) with open("/tmp/xxx", "a") as wf: print(document, "@", parts, "@", complete_event, file=wf) # Get word/text before cursor. if self.sentence: word_before_cursor = document.text_before_cursor else: word_before_cursor = document.get_word_before_cursor(WORD=True, pattern=None) # display_meta = self.meta_dict.get(a, "") display_meta = None if parts[-1].startswith("@"): for a in pathlib.Path(parts[-1][1:]).iterdir(): yield Completion(f"@{a}", -len(word_before_cursor), display_meta=display_meta) elif parts[-1] == "--name": yield Completion("--name ", -len(word_before_cursor), display_meta=display_meta) elif parts[-1] == "--data": yield Completion("--data ", -len(word_before_cursor), display_meta=display_meta) elif not document.text or parts[-1].startswith("-"): for a in ["--name", "--data"]: yield Completion(a, -len(word_before_cursor), display_meta=display_meta) else: return []
def find_completions(self, document: Document) -> dict: """ Find tab completions from the commands repository. Completions are returned based on tokens extracted from the command text received by prompt_toolkit. A dictionary is then walked, matching a token to a nested dictionary until no more dictionaries are available. The resultant dictionary then becomes the suggestions for tab completion. Some commands may have 'dynamic' completions, such as file system related commands. They are defined with a 'dynamic' key, and the method defined as the value for this key is executed to get completions. :param document: :return: """ # extract tokens from the document similar to # how a shell invocation would have been done. # we will also cleanup flags that come in the form # of --flag so that multiples can be suggested. tokens = [token for token in get_tokens(document.text) if not token.startswith('--')] # extract the flags in the received tokens. This list # will be used to remove suggested flags from those # already present in the command. flags = [flag for flag in get_tokens(document.text) if flag.startswith('--')] # start with the current suggestions dictionary being # all commands current_suggestions = self.COMMANDS # when the tokens are extracted, we are expecting something in # the format of: # command sub_command sub_sub_command # so, lets use that and search the the COMMAND dictionary for # the last dictionary with a correct suggestion for token in tokens: candidate = token.lower() if candidate in list(current_suggestions.keys()): # if there are sub commands, grab them if 'commands' in current_suggestions[candidate]: current_suggestions = current_suggestions[candidate]['commands'] # dynamic commands change based on the current status of the # environment, so, call the method defined elif 'dynamic' in current_suggestions[candidate]: current_suggestions = current_suggestions[candidate]['dynamic']() # make --flags in the 'flags' key tab completable elif 'flags' in current_suggestions[candidate]: current_suggestions = { flag: '' for flag in current_suggestions[candidate]['flags'] if flag not in flags } # in this case, there are probably no sub commands, so return # an empty dictionary else: return {} suggestions = {} # once we have the deepest suggestions dictionary in the # current_suggestions variable, loop through and check for # 'sorta' matched versions if current_suggestions and len(current_suggestions) > 0: for k, _ in current_suggestions.items(): # fuzzy-ish matching when part of a word is in a suggestion if document.get_word_before_cursor().lower() in k.lower(): suggestions[k] = current_suggestions[k] return suggestions
def get_completions( self, document: Document, complete_event: CompleteEvent) -> t.Iterable[Completion]: # TODO: Problem with completing positionals. Solve argument resolution to know in which positional. try: # Split document. text = document.text_before_cursor.lstrip() stripped_len = len(document.text_before_cursor) - len(text) if text.endswith('-h') or text.endswith('--help'): return # If there is a space, check for the first term, and use a # subcompleter. if " " in text: first_term = text.split()[0] completer = self.options.get(first_term) # If we have a sub completer, use this for the completions. if isinstance(completer, Completer): remaining_text = text[len(first_term):].lstrip() move_cursor = len(text) - len( remaining_text) + stripped_len new_document = Document( remaining_text, cursor_position=document.cursor_position - move_cursor, ) for c in completer.get_completions(new_document, complete_event): yield c # we reached the bottom subcommand. Parse to see if we have to autocomplete an argument or its value else: options = {} params = {} dest_args = {} if not completer: return for arg in completer: if isinstance(arg, dict): arg = [arg] elif isinstance(arg, list): pass else: # to pass command function in dict command definition continue for a in arg: if a.get('argument').startswith('-'): options.update({a.get('argument'): a}) else: params.update({a.get('argument'): a}) if 'dest' in a: dest_args.update({a.get('dest'): a}) else: dest_args.update( {a.get('argument').lstrip('-'): a}) try: words = shlex.split(text) except: return if len(words ) > 0 and words[-1] in options and text.endswith( words[-1]): completer = DshellWordCompleter( words=list(options.keys())) for c in completer.get_completions( document, complete_event): yield c for p in params: if 'choices' in params[p]: completer = DshellWordCompleter( params[p].get("choices")) elif 'completer' in params[p]: completer = params[p].get('completer') else: parser = create_parser( completer, GuessArgumentParser(allow_abbrev=False)) finder = "F:I.N:D.E:R" if document.char_before_cursor == ' ': text = document.text + finder current_word = finder else: text = document.text current_word = document.get_word_before_cursor( WORD=True) namespace = parser.parse_args(shlex.split(text)[1:]) values = dict(namespace._get_kwargs()) # find key related to current_word for k, v in values.items(): if is_iterable_not_string(v): if current_word in v: break else: if v == current_word: break else: k = None v = None # special case for DictAction for dest, arg_def in dest_args.items(): if 'action' in arg_def and arg_def[ 'action'] == DictAction and values[dest]: for k, v in values[dest].items(): # target if k == current_word: resp = ntwrk.get( 'api_1_0.orchestrationresource', view_data={ 'orchestration_id': values['orchestration_id'] }) if resp.ok: needed_target = resp.msg['target'] completer = DshellWordCompleter([ target for target in needed_target if target not in values[dest].keys() ]) for c in completer.get_completions( document, complete_event): yield c return elif current_word in v: completer = arg_def.get( 'completer', None) for c in completer.get_completions( document, complete_event): if c.text not in v: yield c if len(v) == 0 or len( v) == 1 and v[0] == finder: return k = None v = None # get source value if k: nargs = dest_args[k].get('nargs') if nargs and not isinstance(nargs, int): if k not in params and document.char_before_cursor == ' ': if '--' not in words: # next argument may be a positional parameter or an optional argument # if nargs '+' means that at least 1 item must be provided if not (nargs == '+' and v and len(v) - 1 == 0) and k not in params: completer = DshellWordCompleter( words=list(options.keys())) for c in completer.get_completions( document, complete_event): yield c if nargs == '*' or (nargs == '+' and v and len(v) - 1 > 0): yield Completion('--') else: for p in params: if 'choices' in params[p]: completer = DshellWordCompleter( params[p].get("choices")) elif 'completer' in params[p]: completer = params[p].get( 'completer') for c in completer.get_completions( document, complete_event): yield c break elif k in params and document.char_before_cursor == ' ': completer = DshellWordCompleter( words=list(options.keys())) for c in completer.get_completions( document, complete_event): yield c # cursor is in params (not option) it may set an optional parameter elif k in params and '--' not in words: completer = DshellWordCompleter( words=list(options.keys())) for c in completer.get_completions( document, complete_event): yield c if k in dest_args: if 'choices' in dest_args[k]: completer = DshellWordCompleter( dest_args[k].get("choices")) for c in completer.get_completions( document, complete_event): if (v and c.text not in v) or v is None: yield c completer = dest_args[k].get('completer', None) else: completer = None if completer: if isinstance(completer, ResourceCompleter): kwargs = dict(var_filters=values) else: kwargs = {} for c in completer.get_completions( document, complete_event, **kwargs): if (v and c.text not in v) or v is None: yield c else: completer = DshellWordCompleter( words=list(options.keys())) for c in completer.get_completions( document, complete_event): yield c for p in params: if getattr(namespace, p) is None: if 'choices' in params[p]: completer = DshellWordCompleter( params[p].get("choices")) elif 'completer' in params[p]: completer = params[p].get('completer') for c in completer.get_completions( document, complete_event): yield c # No space in the input: behave exactly like `WordCompleter`. else: completer = DshellWordCompleter(list(self.options.keys()), ignore_case=self.ignore_case) for c in completer.get_completions(document, complete_event): yield c except Exception as e: dprint(e)
def get_completions(self, document: Document, complete_event: CompleteEvent) -> Iterable[Completion]: parser = self.parser word_before_cursor = document.get_word_before_cursor() text = document.text_before_cursor.lstrip() if self.ignore_case: text = text.lower() buff = StringIO(text) reader = csv.reader(buff, delimiter=' ') args = None for line in reader: args = line break if args is None or len(args) == 0: return def remove_parent_args(args, parser): cur_cmd_pos = -1 cur_parser = parser for i, arg in enumerate(args): # 遍历命令行数组,找到最近一个parser subparser = PromptArgumentParser.get_subparser_by_command_( cur_parser, arg) if subparser is not None: cur_cmd_pos = i cur_parser = subparser elif cur_cmd_pos == -1: return args, parser else: if cur_cmd_pos == len(args) - 1: return [], cur_parser return args[(cur_cmd_pos + 1):], cur_parser return args, parser # 在当前命令有子命令的时候移除父命令,保留子命令 current_args, current_parser = remove_parent_args(args, parser) if len(current_args) == 0: return command = current_args[0] # 可能是命令 if len(current_args) == 1: subparsers = current_parser.get_subparser_by_command(command, like=True) if subparsers is not None: info_list: List[Dict[str, str]] = list() max_text_width = 7 for c, a in subparsers.items(): help_msg = '' if isinstance(a, PromptArgumentParser): help_msg = PromptArgumentParser.get_help( current_parser, c) if len(help_msg) > max_text_width: max_text_width = len(help_msg) info_dict = {'text': c, 'help': help_msg} info_list.append(info_dict) for info in info_list: display = fill_right(info['text'], max_text_width) + ' ' + info['help'] yield Completion(info['text'], -len(word_before_cursor), display=display, style='fg:blue', selected_style="fg:white bg:blue") # 检查上一次值,如果为参数则提示输入值 cur_text = current_args[-1] if len(current_args) > 1: last_text = current_args[-2] if last_text.startswith('-'): yield Completion('', -len(word_before_cursor), '<input option value>') # 获得已经使用过的参数,避免重复出现 exists_opts = [] for arg in current_args: if arg.startswith('-'): exists_opts.append(arg) # TODO 判断路径,返回PathCompleter # TODO 判断参数类型,显示提示 # 获取所有可用的参数 opt_groups = current_parser.get_parser_opts(cur_text) completions_dict = [] max_text_width = 0 for opt_group in opt_groups: opt_strings = opt_group.get('opt_strings') help_info = opt_group.get('help_info') for opt in opt_strings: if opt not in ['-h', '--help'] and opt not in exists_opts: text_width = get_cwidth(opt) if text_width > max_text_width: max_text_width = text_width completions_dict.append({ 'text': opt, 'display': help_info }) else: break for c in completions_dict: text = c.get('text') display = fill_right(text, max_text_width) + ' ' + c.get('display') yield Completion(text, -len(cur_text), display=display, style='fg:blue', selected_style="fg:white bg:blue")
def get_completions( self, document: Document, complete_event: CompleteEvent ) -> Iterable[Completion]: cmd_line = document.text word_before_cursor = document.get_word_before_cursor() token_before_cursor = document.get_word_before_cursor(pattern=_compiled_word_re) parse_results, unparsed_text, _, parse_status = parse_cmd_line(cmd_line) # Determine if we are in the command name, in which case we can fall back on # the CommandEngine for finding potential command names or aliases. stripped_cmd_line = cmd_line.strip() if parse_status == ParseState.NONE and stripped_cmd_line: # There is non-whitespace, and the parser still fails. The line is # inherently malformed, so any further completions would just build on that. return elif not stripped_cmd_line or word_before_cursor == parse_results.command: yield from self._get_command_completions(token_before_cursor) return # Figure out what command we are working with. try: command: FrozenCommand = self._command_engine[parse_results.command] except KeyError: # Not a real command name, so we can't provide completions for it. return # Determine the last incomplete token. last_token: IncompleteToken = last_incomplete_token(document, unparsed_text) args = [x for x in parse_results.positionals] kwargs, _ = command.resolved_kwarg_names(parse_results.kv.asDict()) # Check if we want to avoid binding this argument, since we might not know if # really a positional argument or actually an incomplete keyword argument. if args and last_token.is_ambiguous_arg and last_token.value == args[-1]: args.pop() # Determine what would be the unbound arguments if we attempted to bind the # current state of the arguments to the command's underlying coroutine. These # are our options for future argument-based completions. unbound_arguments = command.get_unbound_arguments(*args, **kwargs) unbound_kw_args = [x for x in unbound_arguments if not x.is_pos_only] unbound_pos_args = [x for x in unbound_arguments if not x.is_kw_only] could_be_key_or_pos_value = ( last_token.is_ambiguous_arg and ( token_before_cursor == last_token.key or (not word_before_cursor and not last_token.key) ) ) # Yield keyword argument name completions. if could_be_key_or_pos_value: yield from self._get_kw_arg_name_completions( last_token.key, unbound_kw_args ) # Yield possible values for the next positional argument. if unbound_pos_args and (could_be_key_or_pos_value or last_token.is_pos_arg): next_pos_arg = unbound_pos_args[0] yield from self._get_completions_for_arg( next_pos_arg, document, complete_event ) # Yield possible values for the current keyword argument. kwarg_name = last_token.key if last_token.is_kw_arg: try: matching_kw_arg = command[kwarg_name] yield from self._get_completions_for_arg( matching_kw_arg, document, complete_event ) except NoSuchArgumentError: pass