summaryrefslogtreecommitdiff
path: root/lib/spack/llnl
diff options
context:
space:
mode:
author百地 希留耶 <65301509+KiruyaMomochi@users.noreply.github.com>2023-07-22 21:55:12 +0800
committerGitHub <noreply@github.com>2023-07-22 08:55:12 -0500
commit90ac0ef66e3458dd022b78825637dd0d927e36ec (patch)
tree89ca0722413b7d1c96c6c16e5b2901ac4befe193 /lib/spack/llnl
parent66e85ae39a99be74087f8b65df7bcadf6dff0d98 (diff)
downloadspack-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.py167
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 ""