mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
refactor(completion): name path arguments explicitly and share firstArg
The six commands that take a file or directory argument set their completer directly instead of a Use-line parser wrapping every command, the first-argument-only wrappers become one firstArg helper, and only the bash completion help keeps a custom text. Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
22 files changed
+75
-212
No files matched your search
+36
-89
@@ -519,9 +519,9 @@ func actionScope(scope func() services.ActionScope, t target) services.ActionSco
|
||||
// pathFlags take local file paths, like every flag whose name ends in -file.
|
||||
var pathFlags = []string{"asset", "attachment", "file", "input", "ssh-key"}
|
||||
|
||||
// completeDefaults completes paths only where a command takes them: path flags, flags marked with
|
||||
// MarkFlagFilename, and positional arguments named file or directory in Use. Everything else without a
|
||||
// completer completes nothing, by the root's default directive, rather than file names.
|
||||
// completeDefaults completes paths only where a command takes them: path flags and flags marked
|
||||
// with MarkFlagFilename. Everything else without a completer completes nothing, by the root's
|
||||
// default directive, rather than file names.
|
||||
func completeDefaults(cmd *cobra.Command) {
|
||||
for _, flags := range []*pflag.FlagSet{cmd.Flags(), cmd.PersistentFlags()} {
|
||||
flags.VisitAll(func(flag *pflag.Flag) {
|
||||
@@ -536,23 +536,6 @@ func completeDefaults(cmd *cobra.Command) {
|
||||
}
|
||||
})
|
||||
}
|
||||
if slots := argSlots(cmd.Use); len(slots) > 0 {
|
||||
complete := cmd.ValidArgsFunction
|
||||
cmd.ValidArgsFunction = func(c *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
slot := slots[min(len(args), len(slots)-1)]
|
||||
switch {
|
||||
case len(args) >= len(slots) && !slot.variadic:
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
case slot.kind == "file":
|
||||
return completeFiles(c, args, toComplete)
|
||||
case slot.kind == "directory":
|
||||
return completeDirectories(c, args, toComplete)
|
||||
case complete != nil:
|
||||
return complete(c, args, toComplete)
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
for _, child := range cmd.Commands() {
|
||||
completeDefaults(child)
|
||||
}
|
||||
@@ -568,89 +551,53 @@ func completeDirectories(*cobra.Command, []string, string) ([]cobra.Completion,
|
||||
return nil, cobra.ShellCompDirectiveFilterDirs
|
||||
}
|
||||
|
||||
// argSlot is one positional argument that a Use line declares, such as <number>, [directory] or
|
||||
// <file>...; kind is "file" or "directory" for path arguments.
|
||||
type argSlot struct {
|
||||
kind string
|
||||
variadic bool
|
||||
}
|
||||
|
||||
// argSlots parses the positional arguments of a Use line.
|
||||
func argSlots(use string) []argSlot {
|
||||
_, rest, _ := strings.Cut(use, " ")
|
||||
var slots []argSlot
|
||||
depth, start := 0, 0
|
||||
for i, r := range rest {
|
||||
switch r {
|
||||
case '<', '[', '{':
|
||||
if depth == 0 {
|
||||
start = i
|
||||
}
|
||||
depth++
|
||||
case '>', ']', '}':
|
||||
if depth--; depth != 0 {
|
||||
continue
|
||||
}
|
||||
slot := argSlot{variadic: strings.HasPrefix(rest[i+1:], "...") || strings.HasSuffix(rest[start:i], "...")}
|
||||
for name := range strings.FieldsFuncSeq(rest[start:i+1], func(r rune) bool { return strings.ContainsRune("<>[]|. ", r) }) {
|
||||
switch {
|
||||
case name == "file" || strings.HasSuffix(name, "-file"):
|
||||
slot.kind = "file"
|
||||
case name == "directory" || name == "dir":
|
||||
slot.kind = "directory"
|
||||
}
|
||||
}
|
||||
slots = append(slots, slot)
|
||||
// firstArg completes only the first argument with complete.
|
||||
func firstArg(complete cobra.CompletionFunc) cobra.CompletionFunc {
|
||||
return func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
if len(args) > 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return complete(cmd, args, toComplete)
|
||||
}
|
||||
return slots
|
||||
}
|
||||
|
||||
// completionInstallHelp explains how to load the completion scripts, including with macOS's bash 3.2
|
||||
// and in zsh without compinit.
|
||||
// pathArgs completes the arguments before position n with complete, and paths with path from
|
||||
// position n on: every later argument when variadic, else only the one at n.
|
||||
func pathArgs(n int, variadic bool, path, complete cobra.CompletionFunc) cobra.CompletionFunc {
|
||||
return func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
switch {
|
||||
case len(args) < n:
|
||||
return complete(cmd, args, toComplete)
|
||||
case len(args) == n || variadic:
|
||||
return path(cmd, args, toComplete)
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
|
||||
// completionInstallHelp explains how to load the bash script with macOS's bash 3.2, which cannot
|
||||
// source <(tea completion bash); cobra's help covers the other shells.
|
||||
func completionInstallHelp(root *cobra.Command) {
|
||||
completion, _, err := root.Find([]string{"completion"})
|
||||
if err != nil || completion == root {
|
||||
return
|
||||
}
|
||||
completion.Short = "Generate shell completion scripts"
|
||||
completion.Long = `Generate shell completion scripts for tea.
|
||||
|
||||
Completion offers commands, flags, flag values such as --state or --json fields, and values
|
||||
from the selected instance, such as repositories, issue numbers and labels. See
|
||||
tea completion <shell> --help for how to load the script in your shell.`
|
||||
help := map[string][2]string{
|
||||
"bash": {`Generate the completion script for bash.
|
||||
|
||||
The script needs the bash-completion package. On macOS, install bash-completion with
|
||||
Homebrew for the system bash 3.2, or bash-completion@2 for a Homebrew bash, and load it
|
||||
as its install notes say.
|
||||
|
||||
To load completions in every new shell, add the eval line below to ~/.bashrc (on macOS,
|
||||
~/.bash_profile). Use eval: bash 3.2, the default on macOS, cannot source <(tea completion bash).`, `# Load completions in this shell, or in every shell from ~/.bashrc
|
||||
$ eval "$(tea completion bash)"
|
||||
# Or install the script for every user (Linux, then macOS)
|
||||
$ tea completion bash > /etc/bash_completion.d/tea
|
||||
$ tea completion bash > "$(brew --prefix)/etc/bash_completion.d/tea"`},
|
||||
"zsh": {`Generate the completion script for zsh.
|
||||
|
||||
Completion must be initialized before the script loads: add these lines to ~/.zshrc,
|
||||
keeping an existing compinit call instead of the first line. Without compinit, zsh
|
||||
reports "command not found: compdef".`, `$ autoload -U compinit && compinit
|
||||
$ source <(tea completion zsh)
|
||||
# Or install the script into a directory of your fpath once
|
||||
$ tea completion zsh > "${fpath[1]}/_tea"`},
|
||||
"fish": {`Generate the completion script for fish.
|
||||
|
||||
To load completions in every new shell, install the script once.`, `$ tea completion fish > ~/.config/fish/completions/tea.fish`},
|
||||
"powershell": {`Generate the completion script for PowerShell.
|
||||
|
||||
To load completions in every new shell, add this line to your PowerShell profile.`, `$ tea completion powershell | Out-String | Invoke-Expression`},
|
||||
examples := map[string]string{
|
||||
"bash": `$ eval "$(tea completion bash)"`,
|
||||
"zsh": `$ source <(tea completion zsh)`,
|
||||
"fish": `$ tea completion fish > ~/.config/fish/completions/tea.fish`,
|
||||
"powershell": `$ tea completion powershell | Out-String | Invoke-Expression`,
|
||||
}
|
||||
for _, shell := range completion.Commands() {
|
||||
shell.Long, shell.Example = help[shell.Name()][0], help[shell.Name()][1]
|
||||
shell.Example = examples[shell.Name()]
|
||||
if flag := shell.Flags().Lookup("no-descriptions"); flag != nil {
|
||||
flag.Usage = "Disable completion descriptions"
|
||||
}
|
||||
}
|
||||
bash, _, _ := completion.Find([]string{"bash"})
|
||||
bash.Long = `Generate the completion script for bash. It needs the bash-completion package.
|
||||
|
||||
To load completions in every new shell, add the eval line below to ~/.bashrc (on macOS,
|
||||
~/.bash_profile); bash 3.2, the default on macOS, cannot source <(tea completion bash).`
|
||||
}
|
||||
Reference in new issue
Block a user