diff options
author | 百地 希留耶 <65301509+KiruyaMomochi@users.noreply.github.com> | 2023-07-22 21:55:12 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-22 08:55:12 -0500 |
commit | 90ac0ef66e3458dd022b78825637dd0d927e36ec (patch) | |
tree | 89ca0722413b7d1c96c6c16e5b2901ac4befe193 /lib/spack/llnl | |
parent | 66e85ae39a99be74087f8b65df7bcadf6dff0d98 (diff) | |
download | spack-90ac0ef66e3458dd022b78825637dd0d927e36ec.tar.gz spack-90ac0ef66e3458dd022b78825637dd0d927e36ec.tar.bz2 spack-90ac0ef66e3458dd022b78825637dd0d927e36ec.tar.xz spack-90ac0ef66e3458dd022b78825637dd0d927e36ec.zip |
Implement fish completion (#29549)
* commands: provide more information to Command
* fish: Add script to generate fish completion
* fish: auto prepend `spack` command to avoid duplication
* fish: impove completion generation code readability
* commands: replace match-case with if-else
* fish: fix optspec variable name prefix
* fish: fix return value in get_optspecs
* fish: fix return value in get_optspecs
* format: split long line and trim trailing space
* bugfix: replace f-string with interpolation
* fish: compete more specs and some fixes
* fish: complete hash spec starts with /
* fish: improve compatibility
* style: trim trailing whitespace
* commands: add fish to update args and update tests
* commands: add fish completion file
* style: merge imports
* fish: source completion in setup-env
* fish: caret only completes dependencies
* fish: make sure we always get same order of output
* fish: spack activate
only show installed packages that have extensions
* fish: update completion file
* fish: make dict keys sorted
* Blacken code
* Fix bad merge
* Undo style changes to setup-env.fish
* Fix unit tests
* Style fix
* Compatible with fish_indent
* Use list for stability of order
* Sort one more place
* Sort more things
* Sorting unneeded
* Unsort
* Print difference
* Style fix
* Help messages need quotes
* Arguments to -a must be quoted
* Update types
* Update types
* Update types
* Add type hints
* Change order of positionals
* Always expand help
* Remove shared base class
* Fix type hints
* Remove platform-specific choices
* First line of help only
* Remove unused maps
* Remove suppress
* Remove debugging comments
* Better quoting
* Fish completions have no double dash
* Remove test for deleted class
* Fix grammar in header file
* Use single quotes in most places
* Better support for remainder nargs
* No magic strings
* * and + can also complete multiple
* lower case, no period
---------
Co-authored-by: Adam J. Stewart <ajstewart426@gmail.com>
Diffstat (limited to 'lib/spack/llnl')
-rw-r--r-- | lib/spack/llnl/util/argparsewriter.py | 167 |
1 files changed, 44 insertions, 123 deletions
diff --git a/lib/spack/llnl/util/argparsewriter.py b/lib/spack/llnl/util/argparsewriter.py index dfd602bb34..60a404abd4 100644 --- a/lib/spack/llnl/util/argparsewriter.py +++ b/lib/spack/llnl/util/argparsewriter.py @@ -9,7 +9,7 @@ import io import re import sys from argparse import ArgumentParser -from typing import IO, Optional, Sequence, Tuple +from typing import IO, Any, Iterable, List, Optional, Sequence, Tuple, Union class Command: @@ -25,9 +25,9 @@ class Command: prog: str, description: Optional[str], usage: str, - positionals: Sequence[Tuple[str, str]], - optionals: Sequence[Tuple[Sequence[str], str, str]], - subcommands: Sequence[Tuple[ArgumentParser, str]], + positionals: List[Tuple[str, Optional[Iterable[Any]], Union[int, str, None], str]], + optionals: List[Tuple[Sequence[str], List[str], str, Union[int, str, None], str]], + subcommands: List[Tuple[ArgumentParser, str, str]], ) -> None: """Initialize a new Command instance. @@ -96,13 +96,30 @@ class ArgparseWriter(argparse.HelpFormatter, abc.ABC): if action.option_strings: flags = action.option_strings dest_flags = fmt._format_action_invocation(action) - help = self._expand_help(action) if action.help else "" - help = help.replace("\n", " ") - optionals.append((flags, dest_flags, help)) + nargs = action.nargs + help = ( + self._expand_help(action) + if action.help and action.help != argparse.SUPPRESS + else "" + ) + help = help.split("\n")[0] + + if action.choices is not None: + dest = [str(choice) for choice in action.choices] + else: + dest = [action.dest] + + optionals.append((flags, dest, dest_flags, nargs, help)) elif isinstance(action, argparse._SubParsersAction): for subaction in action._choices_actions: subparser = action._name_parser_map[subaction.dest] - subcommands.append((subparser, subaction.dest)) + help = ( + self._expand_help(subaction) + if subaction.help and action.help != argparse.SUPPRESS + else "" + ) + help = help.split("\n")[0] + subcommands.append((subparser, subaction.dest, help)) # Look for aliases of the form 'name (alias, ...)' if self.aliases and isinstance(subaction.metavar, str): @@ -111,12 +128,22 @@ class ArgparseWriter(argparse.HelpFormatter, abc.ABC): aliases = match.group(2).split(", ") for alias in aliases: subparser = action._name_parser_map[alias] - subcommands.append((subparser, alias)) + help = ( + self._expand_help(subaction) + if subaction.help and action.help != argparse.SUPPRESS + else "" + ) + help = help.split("\n")[0] + subcommands.append((subparser, alias, help)) else: args = fmt._format_action_invocation(action) - help = self._expand_help(action) if action.help else "" - help = help.replace("\n", " ") - positionals.append((args, help)) + help = ( + self._expand_help(action) + if action.help and action.help != argparse.SUPPRESS + else "" + ) + help = help.split("\n")[0] + positionals.append((args, action.choices, action.nargs, help)) return Command(prog, description, usage, positionals, optionals, subcommands) @@ -146,7 +173,7 @@ class ArgparseWriter(argparse.HelpFormatter, abc.ABC): cmd = self.parse(parser, prog) self.out.write(self.format(cmd)) - for subparser, prog in cmd.subcommands: + for subparser, prog, help in cmd.subcommands: self._write(subparser, prog, level=level + 1) def write(self, parser: ArgumentParser) -> None: @@ -205,13 +232,13 @@ class ArgparseRstWriter(ArgparseWriter): if cmd.positionals: string.write(self.begin_positionals()) - for args, help in cmd.positionals: + for args, choices, nargs, help in cmd.positionals: string.write(self.positional(args, help)) string.write(self.end_positionals()) if cmd.optionals: string.write(self.begin_optionals()) - for flags, dest_flags, help in cmd.optionals: + for flags, dest, dest_flags, nargs, help in cmd.optionals: string.write(self.optional(dest_flags, help)) string.write(self.end_optionals()) @@ -338,7 +365,7 @@ class ArgparseRstWriter(ArgparseWriter): """ return "" - def begin_subcommands(self, subcommands: Sequence[Tuple[ArgumentParser, str]]) -> str: + def begin_subcommands(self, subcommands: List[Tuple[ArgumentParser, str, str]]) -> str: """Table with links to other subcommands. Arguments: @@ -355,114 +382,8 @@ class ArgparseRstWriter(ArgparseWriter): """ - for cmd, _ in subcommands: + for cmd, _, _ in subcommands: prog = re.sub(r"^[^ ]* ", "", cmd.prog) string += " * :ref:`{0} <{1}>`\n".format(prog, cmd.prog.replace(" ", "-")) return string + "\n" - - -class ArgparseCompletionWriter(ArgparseWriter): - """Write argparse output as shell programmable tab completion functions.""" - - def format(self, cmd: Command) -> str: - """Return the string representation of a single node in the parser tree. - - Args: - cmd: Parsed information about a command or subcommand. - - Returns: - String representation of this subcommand. - """ - - assert cmd.optionals # we should always at least have -h, --help - assert not (cmd.positionals and cmd.subcommands) # one or the other - - # We only care about the arguments/flags, not the help messages - positionals: Tuple[str, ...] = () - if cmd.positionals: - positionals, _ = zip(*cmd.positionals) - optionals, _, _ = zip(*cmd.optionals) - subcommands: Tuple[str, ...] = () - if cmd.subcommands: - _, subcommands = zip(*cmd.subcommands) - - # Flatten lists of lists - optionals = [x for xx in optionals for x in xx] - - return ( - self.start_function(cmd.prog) - + self.body(positionals, optionals, subcommands) - + self.end_function(cmd.prog) - ) - - def start_function(self, prog: str) -> str: - """Return the syntax needed to begin a function definition. - - Args: - prog: Program name. - - Returns: - Function definition beginning. - """ - name = prog.replace("-", "_").replace(" ", "_") - return "\n_{0}() {{".format(name) - - def end_function(self, prog: str) -> str: - """Return the syntax needed to end a function definition. - - Args: - prog: Program name - - Returns: - Function definition ending. - """ - return "}\n" - - def body( - self, positionals: Sequence[str], optionals: Sequence[str], subcommands: Sequence[str] - ) -> str: - """Return the body of the function. - - Args: - positionals: List of positional arguments. - optionals: List of optional arguments. - subcommands: List of subcommand parsers. - - Returns: - Function body. - """ - return "" - - def positionals(self, positionals: Sequence[str]) -> str: - """Return the syntax for reporting positional arguments. - - Args: - positionals: List of positional arguments. - - Returns: - Syntax for positional arguments. - """ - return "" - - def optionals(self, optionals: Sequence[str]) -> str: - """Return the syntax for reporting optional flags. - - Args: - optionals: List of optional arguments. - - Returns: - Syntax for optional flags. - """ - return "" - - def subcommands(self, subcommands: Sequence[str]) -> str: - """Return the syntax for reporting subcommands. - - Args: - subcommands: List of subcommand parsers. - - Returns: - Syntax for subcommand parsers - """ - return "" |