Skip to content

Commit e6da227

Browse files
committed
Make autocomplete work
1 parent 075589f commit e6da227

11 files changed

Lines changed: 253 additions & 26 deletions

cmd/completion.go

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*
2+
Copyright 2018 Humio Ltd.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
15+
This file is derived of source code from the Helm Project.
16+
You can find the original source here: https://github.com/helm/helm
17+
18+
The project is released under the same Apache 2.0 license, the original
19+
work is copyright of the Helm Authorer.
20+
*/
21+
22+
package cmd
23+
24+
import (
25+
"bytes"
26+
"fmt"
27+
"io"
28+
"os"
29+
30+
"github.com/spf13/cobra"
31+
)
32+
33+
const completionDesc = `
34+
Generate autocompletions script for Humio for the specified shell (bash or zsh).
35+
36+
This command can generate shell autocompletions. e.g.
37+
38+
$ humio completion bash
39+
40+
Can be sourced as such
41+
42+
$ source <(humio completion bash)
43+
`
44+
45+
var (
46+
completionShells = map[string]func(out io.Writer, cmd *cobra.Command) error{
47+
"bash": runCompletionBash,
48+
"zsh": runCompletionZsh,
49+
}
50+
)
51+
52+
func newCompletionCmd() *cobra.Command {
53+
out := os.Stdout
54+
55+
shells := []string{}
56+
for s := range completionShells {
57+
shells = append(shells, s)
58+
}
59+
60+
cmd := &cobra.Command{
61+
Use: "completion SHELL",
62+
Short: "Generate autocompletions script for the specified shell (bash or zsh)",
63+
Long: completionDesc,
64+
RunE: func(cmd *cobra.Command, args []string) error {
65+
return runCompletion(out, cmd, args)
66+
},
67+
ValidArgs: shells,
68+
}
69+
70+
return cmd
71+
}
72+
73+
func runCompletion(out io.Writer, cmd *cobra.Command, args []string) error {
74+
if len(args) == 0 {
75+
return fmt.Errorf("shell not specified")
76+
}
77+
if len(args) > 1 {
78+
return fmt.Errorf("too many arguments, expected only the shell type")
79+
}
80+
run, found := completionShells[args[0]]
81+
if !found {
82+
return fmt.Errorf("unsupported shell type %q", args[0])
83+
}
84+
85+
return run(out, cmd)
86+
}
87+
88+
func runCompletionBash(out io.Writer, cmd *cobra.Command) error {
89+
return cmd.Root().GenBashCompletion(out)
90+
}
91+
92+
func runCompletionZsh(out io.Writer, cmd *cobra.Command) error {
93+
zshInitialization := `#compdef humio
94+
95+
__humio_bash_source() {
96+
alias shopt=':'
97+
alias _expand=_bash_expand
98+
alias _complete=_bash_comp
99+
emulate -L sh
100+
setopt kshglob noshglob braceexpand
101+
source "$@"
102+
}
103+
__humio_type() {
104+
# -t is not supported by zsh
105+
if [ "$1" == "-t" ]; then
106+
shift
107+
# fake Bash 4 to disable "complete -o nospace". Instead
108+
# "compopt +-o nospace" is used in the code to toggle trailing
109+
# spaces. We don't support that, but leave trailing spaces on
110+
# all the time
111+
if [ "$1" = "__humio_compopt" ]; then
112+
echo builtin
113+
return 0
114+
fi
115+
fi
116+
type "$@"
117+
}
118+
__humio_compgen() {
119+
local completions w
120+
completions=( $(compgen "$@") ) || return $?
121+
# filter by given word as prefix
122+
while [[ "$1" = -* && "$1" != -- ]]; do
123+
shift
124+
shift
125+
done
126+
if [[ "$1" == -- ]]; then
127+
shift
128+
fi
129+
for w in "${completions[@]}"; do
130+
if [[ "${w}" = "$1"* ]]; then
131+
echo "${w}"
132+
fi
133+
done
134+
}
135+
__humio_compopt() {
136+
true # don't do anything. Not supported by bashcompinit in zsh
137+
}
138+
__humio_declare() {
139+
if [ "$1" == "-F" ]; then
140+
whence -w "$@"
141+
else
142+
builtin declare "$@"
143+
fi
144+
}
145+
__humio_ltrim_colon_completions()
146+
{
147+
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
148+
# Remove colon-word prefix from COMPREPLY items
149+
local colon_word=${1%${1##*:}}
150+
local i=${#COMPREPLY[*]}
151+
while [[ $((--i)) -ge 0 ]]; do
152+
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
153+
done
154+
fi
155+
}
156+
__humio_get_comp_words_by_ref() {
157+
cur="${COMP_WORDS[COMP_CWORD]}"
158+
prev="${COMP_WORDS[${COMP_CWORD}-1]}"
159+
words=("${COMP_WORDS[@]}")
160+
cword=("${COMP_CWORD[@]}")
161+
}
162+
__humio_filedir() {
163+
local RET OLD_IFS w qw
164+
__debug "_filedir $@ cur=$cur"
165+
if [[ "$1" = \~* ]]; then
166+
# somehow does not work. Maybe, zsh does not call this at all
167+
eval echo "$1"
168+
return 0
169+
fi
170+
OLD_IFS="$IFS"
171+
IFS=$'\n'
172+
if [ "$1" = "-d" ]; then
173+
shift
174+
RET=( $(compgen -d) )
175+
else
176+
RET=( $(compgen -f) )
177+
fi
178+
IFS="$OLD_IFS"
179+
IFS="," __debug "RET=${RET[@]} len=${#RET[@]}"
180+
for w in ${RET[@]}; do
181+
if [[ ! "${w}" = "${cur}"* ]]; then
182+
continue
183+
fi
184+
if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
185+
qw="$(__humio_quote "${w}")"
186+
if [ -d "${w}" ]; then
187+
COMPREPLY+=("${qw}/")
188+
else
189+
COMPREPLY+=("${qw}")
190+
fi
191+
fi
192+
done
193+
}
194+
__humio_quote() {
195+
if [[ $1 == \'* || $1 == \"* ]]; then
196+
# Leave out first character
197+
printf %q "${1:1}"
198+
else
199+
printf %q "$1"
200+
fi
201+
}
202+
autoload -U +X bashcompinit && bashcompinit
203+
# use word boundary patterns for BSD or GNU sed
204+
LWORD='[[:<:]]'
205+
RWORD='[[:>:]]'
206+
if sed --help 2>&1 | grep -q GNU; then
207+
LWORD='\<'
208+
RWORD='\>'
209+
fi
210+
__humio_convert_bash_to_zsh() {
211+
sed \
212+
-e 's/declare -F/whence -w/' \
213+
-e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \
214+
-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
215+
-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
216+
-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
217+
-e "s/${LWORD}_filedir${RWORD}/__humio_filedir/g" \
218+
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__humio_get_comp_words_by_ref/g" \
219+
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__humio_ltrim_colon_completions/g" \
220+
-e "s/${LWORD}compgen${RWORD}/__humio_compgen/g" \
221+
-e "s/${LWORD}compopt${RWORD}/__humio_compopt/g" \
222+
-e "s/${LWORD}declare${RWORD}/__humio_declare/g" \
223+
-e "s/\\\$(type${RWORD}/\$(__humio_type/g" \
224+
<<'BASH_COMPLETION_EOF'
225+
`
226+
out.Write([]byte(zshInitialization))
227+
228+
buf := new(bytes.Buffer)
229+
cmd.Root().GenBashCompletion(buf)
230+
out.Write(buf.Bytes())
231+
232+
zshTail := `
233+
BASH_COMPLETION_EOF
234+
}
235+
__humio_bash_source <(__humio_convert_bash_to_zsh)
236+
`
237+
out.Write([]byte(zshTail))
238+
return nil
239+
}

cmd/ingest_tokens_list.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ import (
2323

2424
func newIngestTokensListCmd() *cobra.Command {
2525
cmd := &cobra.Command{
26-
Use: "list [flags] <repo>",
27-
Aliases: []string{"ls"},
28-
Short: "List all ingest tokens in a repository.",
29-
Args: cobra.ExactArgs(1),
26+
Use: "list [flags] <repo>",
27+
Short: "List all ingest tokens in a repository.",
28+
Args: cobra.ExactArgs(1),
3029
RunE: func(cmd *cobra.Command, args []string) error {
3130

3231
repo := args[0]

cmd/ingest_tokens_remove.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
func newIngestTokensRemoveCmd() *cobra.Command {
2424
cmd := &cobra.Command{
2525
Use: "remove [flags] <repo> <token-name>",
26-
Aliases: []string{"rm"},
2726
Short: "Removes an ingest token.",
2827
Long: `Removes the ingest token with name '<token-name>' from the repository with name '<repo>'.`,
2928
ValidArgs: []string{"repo", "token-name"},

cmd/parsers_list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222

2323
func newParsersListCmd() *cobra.Command {
2424
cmd := cobra.Command{
25-
Use: "list",
25+
Use: "list [flags] <repo>",
2626
Short: "List all installed parsers in a repository.",
2727
Args: cobra.ExactArgs(1),
2828
RunE: func(cmd *cobra.Command, args []string) error {

cmd/parsers_remove.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ import (
2222

2323
func newParsersRemoveCmd() *cobra.Command {
2424
cmd := cobra.Command{
25-
Use: "remove [flags] <repo> <parser>",
26-
Aliases: []string{"rm", "uninstall"},
27-
Short: "Remove (uninstall) a parser from a repository.",
28-
Args: cobra.ExactArgs(2),
25+
Use: "remove [flags] <repo> <parser>",
26+
Short: "Remove (uninstall) a parser from a repository.",
27+
Args: cobra.ExactArgs(2),
2928
RunE: func(cmd *cobra.Command, args []string) error {
3029
repo := args[0]
3130
parser := args[1]

cmd/root.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"path"
2121

2222
"github.com/humio/cli/api"
23-
"github.com/humio/cli/prompt"
2423
homedir "github.com/mitchellh/go-homedir"
2524
"github.com/spf13/cobra"
2625
"github.com/spf13/viper"
@@ -102,6 +101,7 @@ Common Management Commands:
102101
rootCmd.AddCommand(newLoginCmd())
103102
rootCmd.AddCommand(newIngestTokensCmd())
104103
rootCmd.AddCommand(newViewsCmd())
104+
rootCmd.AddCommand(newCompletionCmd())
105105
}
106106

107107
// initConfig reads in config file and ENV variables if set.
@@ -126,11 +126,7 @@ func initConfig() {
126126
viper.AutomaticEnv() // read in environment variables that match
127127

128128
// If a config file is found, read it in.
129-
err := viper.ReadInConfig()
130-
if err == nil {
131-
prompt.Output("Using config file: " + cfgFile)
132-
prompt.Output("Cluster address: " + viper.GetString("address"))
133-
}
129+
viper.ReadInConfig()
134130
}
135131

136132
func NewApiClient(cmd *cobra.Command) *api.Client {

cmd/users.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func printUserTable(user api.User) {
5151
[]string{"Name", user.FullName},
5252
[]string{"Is Root", yesNo(user.IsRoot)},
5353
[]string{"Roles", strings.Join(userRoleNames(user), ", ")},
54+
[]string{"Email", user.Email},
5455
[]string{"Created At", user.CreatedAt},
5556
[]string{"Country Code", user.CountryCode},
5657
[]string{"Company", user.Company},

cmd/users_list.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,7 @@ import (
2424
func newUsersListCmd() *cobra.Command {
2525
return &cobra.Command{
2626
Use: "list",
27-
Short: "A brief description of your command",
28-
Long: `A longer description that spans multiple lines and likely contains examples
29-
and usage of using your command. For example:
30-
31-
Cobra is a CLI library for Go that empowers applications.
32-
This application is a tool to generate the needed files
33-
to quickly create a Cobra application.`,
27+
Short: "Lists all users. [Root Only]",
3428
RunE: func(cmd *cobra.Command, args []string) error {
3529
client := NewApiClient(cmd)
3630

cmd/users_remove.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func newUsersRemoveCmd() *cobra.Command {
2424

2525
cmd := cobra.Command{
2626
Use: "remove",
27-
Short: "Remove a user",
27+
Short: "Remove a user [Root Only]",
2828
Args: cobra.ExactArgs(1),
2929
RunE: func(cmd *cobra.Command, args []string) error {
3030

cmd/users_show.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
func newUsersShowCmd() *cobra.Command {
2424
cmd := cobra.Command{
2525
Use: "show [flags] <username>",
26-
Short: "Show details about a user.",
26+
Short: "Show details about a user [Root Only]",
2727
Args: cobra.ExactArgs(1),
2828
RunE: func(cmd *cobra.Command, args []string) error {
2929
username := args[0]

0 commit comments

Comments
 (0)