Skip to content

Commit

Permalink
feat(cmd): added command to generate basic cmd autocompletion scripts (
Browse files Browse the repository at this point in the history
…#1240)

* feat(cmd): added command to generate basic cmd autocompletion scripts

* feat(cmd): expanded completion to datasets for log and get operators

* feat(cmd): slight refactoring for cleanup & better readibility

* feat(cmd): adding completion to commands depending only on qri list

* feat(cmd): completion for cmds depending on qri list, search and for file related flags

* feat(cmd): auto-completion cleanup
  • Loading branch information
Arqu authored Apr 6, 2020
1 parent ddb7713 commit 0845289
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ the name of the peer that originally added the dataset. You must have
}

cmd.Flags().StringVar(&o.LinkDir, "link", "", "path to directory to link dataset to")
cmd.MarkFlagFilename("link")
cmd.Flags().BoolVar(&o.LogsOnly, "logs-only", false, "only fetch logs, skipping HEAD data")

return cmd
Expand Down
286 changes: 286 additions & 0 deletions cmd/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
package cmd

import (
"bytes"
"fmt"
"io"

"github.com/qri-io/ioes"
"github.com/spf13/cobra"
)

// NewAutocompleteCommand creates a new `qri complete` cobra command that prints autocomplete scripts
func NewAutocompleteCommand(_ Factory, ioStreams ioes.IOStreams) *cobra.Command {
o := &AutocompleteOptions{IOStreams: ioStreams}
cmd := &cobra.Command{
Use: "completion [bash|zsh]",
Short: "generate shell auto-completion scripts",
Long: `Completion generates auto-completion scripts for Bash or Zsh
which you can then source in your terminal or save to your profile to have it
run on each terminal session.`,
Example: ` # load auto-completion for a single session
$ source <(qri completion [bash|zsh])
#configure your bash/zsh shell to load completions by adding to your bashrc/zshrc:
# ~/.bashrc or ~/.zshrc
$ source <(qri completion [bash|zsh])
# alternatively you can pipe the output to a local script and
# reference that as the source for faster loading.
`,
Annotations: map[string]string{
"group": "other",
},
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return o.Run(cmd, args)
},
ValidArgs: []string{"bash", "zsh"},
}

return cmd
}

// AutocompleteOptions encapsulates completion options
type AutocompleteOptions struct {
ioes.IOStreams
}

// Run executes the completion command
func (o *AutocompleteOptions) Run(cmd *cobra.Command, args []string) (err error) {
if len(args) == 0 {
return fmt.Errorf("shell not specified")
}
if len(args) > 1 {
return fmt.Errorf("too many arguments, expected only the shell type")
}
if args[0] == "bash" {
cmd.Parent().GenBashCompletion(o.Out)
}
if args[0] == "zsh" {
zshBody := bytes.Buffer{}
cmd.Parent().GenBashCompletion(&zshBody)
io.WriteString(o.Out, zshHead)
o.Out.Write(zshBody.Bytes())
io.WriteString(o.Out, zshTail)
}
return nil
}

const (
bashCompletionFunc = `
__qri_parse_list()
{
local qri_output out
if qri_output=$(qri list --format=simple --no-prompt --no-color 2>/dev/null); then
out=($(echo "${qri_output}"))
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__qri_parse_search()
{
echo 'test'
local qri_output out
if qri_output=$(qri search $1 --format=simple --no-prompt --no-color 2>/dev/null); then
out=($(echo "${qri_output}"))
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__qri_get_datasets()
{
__qri_parse_list
if [[ $? -eq 0 ]]; then
return 0
fi
}
__qri_get_search()
{
__qri_parse_search
if [[ $? -eq 0 ]]; then
return 0
fi
}
__qri_custom_func() {
case ${last_command} in
qri_checkout | qri_export | qri_get | qri_log | qri_logbook | qri_publish | qri_remove | qri_rename | qri_render | qri_save | qri_stats | qri_use | qri_validate | qri_whatchanged)
__qri_get_datasets
return
;;
qri_add | qri_fetch | qri_search)
__qri_get_search
return
;;
*)
;;
esac
}
`

zshHead = `# reference kubectl completion zsh
__qri_bash_source() {
alias shopt=':'
alias _expand=_bash_expand
alias _complete=_bash_comp
emulate -L sh
setopt kshglob noshglob braceexpand
source "$@"
}
__qri_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" = "__qri_compopt" ]; then
echo builtin
return 0
fi
fi
type "$@"
}
__qri_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
}
__qri_compopt() {
true # don't do anything. Not supported by bashcompinit in zsh
}
__qri_declare() {
if [ "$1" == "-F" ]; then
whence -w "$@"
else
builtin declare "$@"
fi
}
__qri_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
}
__qri_get_comp_words_by_ref() {
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[${COMP_CWORD}-1]}"
words=("${COMP_WORDS[@]}")
cword=("${COMP_CWORD[@]}")
}
__qri_filedir() {
# todo(arqu): This is a horrible hack to just return to normal
# path completion, ZSH has issues with correctly handling _filedir in this case.
# Extension filtering is not supported right now
return 1
local RET OLD_IFS w qw
__qri_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="," __qri_debug "RET=${RET[@]} len=${#RET[@]}"
for w in ${RET[@]}; do
if [[ ! "${w}" = "${cur}"* ]]; then
continue
fi
if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
qw="$(__qri_quote "${w}")"
if [ -d "${w}" ]; then
COMPREPLY+=("${qw}/")
else
COMPREPLY+=("${qw}")
fi
fi
done
}
__qri_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
__qri_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}/__qri_filedir/g" \
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__qri_get_comp_words_by_ref/g" \
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__qri_ltrim_colon_completions/g" \
-e "s/${LWORD}compgen${RWORD}/__qri_compgen/g" \
-e "s/${LWORD}compopt${RWORD}/__qri_compopt/g" \
-e "s/${LWORD}declare${RWORD}/builtin declare/g" \
-e "s/\\\$(type${RWORD}/\$(__qri_type/g" \
<<'BASH_COMPLETION_EOF'
`

zshTail = `
BASH_COMPLETION_EOF
}
__qri_bash_source <(__qri_convert_bash_to_zsh)
`
)
1 change: 1 addition & 0 deletions cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ To export to a specific directory, use the --output flag.`,
}

cmd.Flags().StringVarP(&o.Output, "output", "o", "", "path to write to, default is current directory")
cmd.MarkFlagFilename("output")
cmd.Flags().StringVarP(&o.Format, "format", "f", "", "format for the exported dataset, such as native, json, xlsx. default: json")
cmd.Flags().BoolVarP(&o.Zipped, "zip", "z", false, "export as a zip file")

Expand Down
2 changes: 2 additions & 0 deletions cmd/qri.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ https://github.com/qri-io/qri/issues`,
setNoPrompt(noPrompt)
}
},
BashCompletionFunction: bashCompletionFunc,
}

qriPath, ipfsPath := pf()
Expand All @@ -51,6 +52,7 @@ https://github.com/qri-io/qri/issues`,

cmd.AddCommand(
NewAddCommand(opt, ioStreams),
NewAutocompleteCommand(opt, ioStreams),
NewCheckoutCommand(opt, ioStreams),
NewConfigCommand(opt, ioStreams),
NewConnectCommand(opt, ioStreams),
Expand Down
2 changes: 2 additions & 0 deletions cmd/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ provided, Qri will render the dataset with a default template.`,
}

cmd.Flags().StringVarP(&o.Template, "template", "t", "", "path to template file")
cmd.MarkFlagFilename("template")
cmd.Flags().BoolVarP(&o.UseViz, "viz", "v", false, "whether to use the viz component")
cmd.Flags().StringVarP(&o.Output, "output", "o", "", "path to write output file")
cmd.MarkFlagFilename("output")

return cmd
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ commit message and title to the save.`,
}

cmd.Flags().StringSliceVarP(&o.FilePaths, "file", "f", nil, "dataset or component file (yaml or json)")
cmd.MarkFlagFilename("file", "yaml", "yml", "json")
cmd.Flags().StringVarP(&o.Title, "title", "t", "", "title of commit message for save")
cmd.Flags().StringVarP(&o.Message, "message", "m", "", "commit message for save")
cmd.Flags().StringVarP(&o.BodyPath, "body", "", "", "path to file or url of data to add as dataset contents")
cmd.MarkFlagFilename("body")
cmd.Flags().StringVarP(&o.Recall, "recall", "", "", "restore revisions from dataset history")
// cmd.Flags().BoolVarP(&o.ShowValidation, "show-validation", "s", false, "display a list of validation errors upon adding")
cmd.Flags().StringSliceVar(&o.Secrets, "secrets", nil, "transform secrets as comma separated key,value,key,value,... sequence")
Expand Down
3 changes: 3 additions & 0 deletions cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,11 @@ if these flags are provided.`,
// TODO: restore
// cmd.Flags().StringVarP(&o.URL, "url", "u", "", "url to file to initialize from")
cmd.Flags().StringVarP(&o.BodyFilepath, "body", "b", "", "body file to validate")
cmd.MarkFlagFilename("body")
cmd.Flags().StringVarP(&o.SchemaFilepath, "schema", "", "", "json schema file to use for validation")
cmd.MarkFlagFilename("schema", "json")
cmd.Flags().StringVarP(&o.StructureFilepath, "structure", "", "", "json structure file to use for validation")
cmd.MarkFlagFilename("structure", "json")

return cmd
}
Expand Down

0 comments on commit 0845289

Please sign in to comment.