Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
 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> &gt; </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))
Esempio n. 4
0
    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
Esempio n. 5
0
 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))
Esempio n. 6
0
    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 []
Esempio n. 7
0
    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,
            )
Esempio n. 8
0
    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)
Esempio n. 9
0
    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)
Esempio n. 10
0
    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
Esempio n. 11
0
    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)
Esempio n. 12
0
    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)
Esempio n. 14
0
    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 []
Esempio n. 15
0
    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
Esempio n. 16
0
    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)
Esempio n. 17
0
    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")
Esempio n. 18
0
    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