-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds support for auto completion for bash and zsh shells. A new root level command called "completion" has been introduced, and the user can get the auto completion code by running `ark completion bash/zsh`. For bash completion, the built-in GenBashCompletion() from cobra has been used, but for zsh, the built-in GenZshCompletion() is known to cause issues. The workaround has been copied from zsh completion code of kubectl. Signed-off-by: Shubham <shubham@linux.com>
- Loading branch information
Showing
4 changed files
with
256 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
## ark completion | ||
|
||
Output shell completion code for the specified shell (bash or zsh) | ||
|
||
### Synopsis | ||
|
||
|
||
Generate shell completion code. | ||
|
||
Auto completion supports both bash and zsh. Output is to STDOUT. | ||
|
||
Load the ark completion code for bash into the current shell - | ||
source <(ark completion bash) | ||
|
||
Load the ark completion code for zsh into the current shell - | ||
source <(ark completion zsh) | ||
|
||
|
||
``` | ||
ark completion SHELL [flags] | ||
``` | ||
|
||
### Options | ||
|
||
``` | ||
-h, --help help for completion | ||
``` | ||
|
||
### Options inherited from parent commands | ||
|
||
``` | ||
--alsologtostderr log to standard error as well as files | ||
--kubeconfig string Path to the kubeconfig file to use to talk to the Kubernetes apiserver. If unset, try the environment variable KUBECONFIG, as well as in-cluster configuration | ||
--kubecontext string The context to use to talk to the Kubernetes apiserver. If unset defaults to whatever your current-context is (kubectl config current-context) | ||
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) | ||
--log_dir string If non-empty, write log files in this directory | ||
--logtostderr log to standard error instead of files | ||
-n, --namespace string The namespace in which Ark should operate (default "heptio-ark") | ||
--stderrthreshold severity logs at or above this threshold go to stderr (default 2) | ||
-v, --v Level log level for V logs | ||
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging | ||
``` | ||
|
||
### SEE ALSO | ||
* [ark](ark.md) - Back up and restore Kubernetes cluster resources. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
/* | ||
Copyright 2018 the Heptio Ark contributors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package completion | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"bytes" | ||
"github.com/spf13/cobra" | ||
"io" | ||
) | ||
|
||
func NewCommand() *cobra.Command { | ||
c := &cobra.Command{ | ||
Use: "completion SHELL", | ||
Short: "Output shell completion code for the specified shell (bash or zsh)", | ||
Long: `Generate shell completion code. | ||
Auto completion supports both bash and zsh. Output is to STDOUT. | ||
Load the ark completion code for bash into the current shell - | ||
source <(ark completion bash) | ||
Load the ark completion code for zsh into the current shell - | ||
source <(ark completion zsh) | ||
`, | ||
Args: cobra.ExactArgs(1), | ||
ValidArgs: []string{"bash", "zsh"}, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
shell := args[0] | ||
switch shell { | ||
case "bash": | ||
cmd.Root().GenBashCompletion(os.Stdout) | ||
case "zsh": | ||
genZshCompletion(os.Stdout, cmd.Root()) | ||
default: | ||
fmt.Printf("Invalid shell specified, specify bash or zsh\n") | ||
os.Exit(1) | ||
} | ||
}, | ||
} | ||
|
||
return c | ||
} | ||
|
||
func genZshCompletion(out io.Writer, ark *cobra.Command) { | ||
// The following code has been copied from | ||
// https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/completion.go | ||
|
||
zshHead := "#compdef ark\n" | ||
|
||
out.Write([]byte(zshHead)) | ||
|
||
zshInitialization := ` | ||
__ark_bash_source() { | ||
alias shopt=':' | ||
alias _expand=_bash_expand | ||
alias _complete=_bash_comp | ||
emulate -L sh | ||
setopt kshglob noshglob braceexpand | ||
source "$@" | ||
} | ||
__ark_type() { | ||
# -t is not supported by zsh | ||
if [ "$1" == "-t" ]; then | ||
shift | ||
# fake Bash 4 to disable "complete -o nospace". Instead | ||
# "compopt +-o nospace" is used in the code to toggle trailing | ||
# spaces. We don't support that, but leave trailing spaces on | ||
# all the time | ||
if [ "$1" = "__ark_compopt" ]; then | ||
echo builtin | ||
return 0 | ||
fi | ||
fi | ||
type "$@" | ||
} | ||
__ark_compgen() { | ||
local completions w | ||
completions=( $(compgen "$@") ) || return $? | ||
# filter by given word as prefix | ||
while [[ "$1" = -* && "$1" != -- ]]; do | ||
shift | ||
shift | ||
done | ||
if [[ "$1" == -- ]]; then | ||
shift | ||
fi | ||
for w in "${completions[@]}"; do | ||
if [[ "${w}" = "$1"* ]]; then | ||
echo "${w}" | ||
fi | ||
done | ||
} | ||
__ark_compopt() { | ||
true # don't do anything. Not supported by bashcompinit in zsh | ||
} | ||
__ark_ltrim_colon_completions() | ||
{ | ||
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then | ||
# Remove colon-word prefix from COMPREPLY items | ||
local colon_word=${1%${1##*:}} | ||
local i=${#COMPREPLY[*]} | ||
while [[ $((--i)) -ge 0 ]]; do | ||
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} | ||
done | ||
fi | ||
} | ||
__ark_get_comp_words_by_ref() { | ||
cur="${COMP_WORDS[COMP_CWORD]}" | ||
prev="${COMP_WORDS[${COMP_CWORD}-1]}" | ||
words=("${COMP_WORDS[@]}") | ||
cword=("${COMP_CWORD[@]}") | ||
} | ||
__ark_filedir() { | ||
local RET OLD_IFS w qw | ||
__ark_debug "_filedir $@ cur=$cur" | ||
if [[ "$1" = \~* ]]; then | ||
# somehow does not work. Maybe, zsh does not call this at all | ||
eval echo "$1" | ||
return 0 | ||
fi | ||
OLD_IFS="$IFS" | ||
IFS=$'\n' | ||
if [ "$1" = "-d" ]; then | ||
shift | ||
RET=( $(compgen -d) ) | ||
else | ||
RET=( $(compgen -f) ) | ||
fi | ||
IFS="$OLD_IFS" | ||
IFS="," __ark_debug "RET=${RET[@]} len=${#RET[@]}" | ||
for w in ${RET[@]}; do | ||
if [[ ! "${w}" = "${cur}"* ]]; then | ||
continue | ||
fi | ||
if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then | ||
qw="$(__ark_quote "${w}")" | ||
if [ -d "${w}" ]; then | ||
COMPREPLY+=("${qw}/") | ||
else | ||
COMPREPLY+=("${qw}") | ||
fi | ||
fi | ||
done | ||
} | ||
__ark_quote() { | ||
if [[ $1 == \'* || $1 == \"* ]]; then | ||
# Leave out first character | ||
printf %q "${1:1}" | ||
else | ||
printf %q "$1" | ||
fi | ||
} | ||
autoload -U +X bashcompinit && bashcompinit | ||
# use word boundary patterns for BSD or GNU sed | ||
LWORD='[[:<:]]' | ||
RWORD='[[:>:]]' | ||
if sed --help 2>&1 | grep -q GNU; then | ||
LWORD='\<' | ||
RWORD='\>' | ||
fi | ||
__ark_convert_bash_to_zsh() { | ||
sed \ | ||
-e 's/declare -F/whence -w/' \ | ||
-e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \ | ||
-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \ | ||
-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \ | ||
-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \ | ||
-e "s/${LWORD}_filedir${RWORD}/__ark_filedir/g" \ | ||
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__ark_get_comp_words_by_ref/g" \ | ||
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__ark_ltrim_colon_completions/g" \ | ||
-e "s/${LWORD}compgen${RWORD}/__ark_compgen/g" \ | ||
-e "s/${LWORD}compopt${RWORD}/__ark_compopt/g" \ | ||
-e "s/${LWORD}declare${RWORD}/builtin declare/g" \ | ||
-e "s/\\\$(type${RWORD}/\$(__ark_type/g" \ | ||
<<'BASH_COMPLETION_EOF' | ||
` | ||
out.Write([]byte(zshInitialization)) | ||
|
||
buf := new(bytes.Buffer) | ||
ark.GenBashCompletion(buf) | ||
out.Write(buf.Bytes()) | ||
|
||
zshTail := ` | ||
BASH_COMPLETION_EOF | ||
} | ||
__ark_bash_source <(__ark_convert_bash_to_zsh) | ||
_complete ark 2>/dev/null | ||
` | ||
out.Write([]byte(zshTail)) | ||
} |