Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions cmd/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cmd

import (
"fmt"
"strings"

"github.com/spf13/cobra"
)

const shellPowerShell = "powershell"

func newCompletionCmd() *cobra.Command {
return &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate shell completion scripts",
Args: cobra.ExactArgs(1),
ValidArgs: []string{"bash", "zsh", "fish", shellPowerShell},
RunE: func(cmd *cobra.Command, args []string) error {
out := cmd.OutOrStdout()
root := cmd.Root()

switch shell := strings.ToLower(args[0]); shell {
case "bash":
return root.GenBashCompletion(out)
case "zsh":
return root.GenZshCompletion(out)
case "fish":
return root.GenFishCompletion(out, true)
case shellPowerShell:
return root.GenPowerShellCompletion(out)
default:
return fmt.Errorf("unsupported shell %q (supported: bash, zsh, fish, %s)", args[0], shellPowerShell)
}
},
}
}
48 changes: 48 additions & 0 deletions cmd/completion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cmd

import (
"bytes"
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestCompletionCmd_GeneratesPowerShell(t *testing.T) {
root := &cobra.Command{Use: "app"}
root.AddCommand(newCompletionCmd())

var out bytes.Buffer
root.SetOut(&out)
root.SetErr(&out)
root.SetArgs([]string{"completion", "powershell"})

err := root.Execute()

require.NoError(t, err)
assert.Contains(t, out.String(), "Register-ArgumentCompleter")
}

func TestCompletionCmd_RejectsUnsupportedShell(t *testing.T) {
root := &cobra.Command{Use: "app"}
root.AddCommand(newCompletionCmd())

root.SetArgs([]string{"completion", "xonsh"})

err := root.Execute()

require.Error(t, err)
assert.Contains(t, err.Error(), "unsupported shell")
}

func TestRootRegistersCompletionCommand(t *testing.T) {
root, cleanup := newRootCommand()
defer cleanup()

completionCmd, _, err := root.Find([]string{"completion"})

require.NoError(t, err)
require.NotNil(t, completionCmd)
assert.Equal(t, "completion", completionCmd.Name())
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ Environment:
rootCmd.AddCommand(newRunCmd())
rootCmd.AddCommand(newReportCmd())
rootCmd.AddCommand(newConfigCmd())
rootCmd.AddCommand(newCompletionCmd())

// cleanup releases profiling resources. It is idempotent — safe to call
// multiple times (subsequent calls are no-ops).
Expand Down
12 changes: 12 additions & 0 deletions docs/completions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Shell completions

Generate a completion script for your shell:

```bash
grut completion bash
grut completion zsh
grut completion fish
grut completion powershell
```

Write the generated script to the location your shell expects, then reload the shell.
Loading