From 50f385938e098a69ec93b3490ff68442ec3d6524 Mon Sep 17 00:00:00 2001 From: Haim Ashkenazi Date: Fri, 2 Mar 2018 08:42:52 +0200 Subject: [PATCH] zsh-completion: added support for filename globbing. --- zsh_completions.go | 11 +++++++---- zsh_completions.md | 4 +--- zsh_completions_test.go | 23 +++++++++++++---------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/zsh_completions.go b/zsh_completions.go index 4170346..2e0d3e3 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -137,7 +137,7 @@ func genFlagEntryForSingleOptionFlag(f *pflag.Flag) string { } extras = genZshFlagEntryExtras(f) - return fmt.Sprintf(`"%s%s[%s]%s"`, multiMark, option, f.Usage, extras) + return fmt.Sprintf(`'%s%s[%s]%s'`, multiMark, option, f.Usage, extras) } func genFlagEntryForMultiOptionFlag(f *pflag.Flag) string { @@ -148,19 +148,22 @@ func genFlagEntryForMultiOptionFlag(f *pflag.Flag) string { curlyMultiMark = "\\*" } - options = fmt.Sprintf(`"(%s-%s %s--%s)"{%s-%s,%s--%s}`, + options = fmt.Sprintf(`'(%s-%s %s--%s)'{%s-%s,%s--%s}`, parenMultiMark, f.Shorthand, parenMultiMark, f.Name, curlyMultiMark, f.Shorthand, curlyMultiMark, f.Name) extras = genZshFlagEntryExtras(f) - return fmt.Sprintf(`%s"[%s]%s"`, options, f.Usage, extras) + return fmt.Sprintf(`%s'[%s]%s'`, options, f.Usage, extras) } func genZshFlagEntryExtras(f *pflag.Flag) string { var extras string - _, pathSpecified := f.Annotations[BashCompFilenameExt] + globs, pathSpecified := f.Annotations[BashCompFilenameExt] if pathSpecified { extras = ":filename:_files" + for _, g := range globs { + extras = extras + fmt.Sprintf(` -g "%s"`, g) + } } else if f.NoOptDefVal == "" { extras = ":" // allow option variable without assisting } diff --git a/zsh_completions.md b/zsh_completions.md index bfedcf1..c218179 100644 --- a/zsh_completions.md +++ b/zsh_completions.md @@ -9,8 +9,7 @@ The generated completion script should be put somewhere in your `$fpath` named * Completion for all non-hidden subcommands using their `.Short` description. * Completion for all non-hidden flags using the following rules: * Filename completion works by marking the flag with `cmd.MarkFlagFilename...` - family of commands. However, it will ignore specific extensions requested by - this command (see about what's not supported yet below). + family of commands. * The requirement for argument to the flag is decided by the `.NoOptDefVal` flag value - if it's empty then completion will expect an argument. * Flags of one of the various `*Arrary` and `*Slice` types supports multiple @@ -19,7 +18,6 @@ The generated completion script should be put somewhere in your `$fpath` named ### What's not yet Supported * Positional argument completion are not supported yet. -* Filename completion ignores extension specification. * Custom completion scripts are not supported yet (We should probably create zsh specific one, doesn't make sense to re-use the bash one as the functions will be different). diff --git a/zsh_completions_test.go b/zsh_completions_test.go index ba2a3bf..c5199ed 100644 --- a/zsh_completions_test.go +++ b/zsh_completions_test.go @@ -29,7 +29,7 @@ func TestGenZshCompletion(t *testing.T) { return r }(), expectedExpressions: []string{ - `(?s)function _mycommand {\s+_arguments \\\s+"--debug\[description\]".*--help.*}`, + `(?s)function _mycommand {\s+_arguments \\\s+'--debug\[description\]'.*--help.*}`, "#compdef _mycommand mycommand", }, }, @@ -45,7 +45,7 @@ func TestGenZshCompletion(t *testing.T) { return r }(), expectedExpressions: []string{ - `"\(-d --debug\)"{-d,--debug}"\[debug description\]"`, + `'\(-d --debug\)'{-d,--debug}'\[debug description\]'`, }, }, { @@ -72,15 +72,15 @@ func TestGenZshCompletion(t *testing.T) { }(), expectedExpressions: []string{ `commands=\(\n\s+"help:.*\n\s+"subcmd1:.*\n\s+"subcmd2:.*\n\s+\)`, - `_arguments \\\n.*"--debug\[description]"`, - `_arguments -C \\\n.*"--debug\[description]"`, + `_arguments \\\n.*'--debug\[description]'`, + `_arguments -C \\\n.*'--debug\[description]'`, `function _rootcmd_subcmd1 {`, `function _rootcmd_subcmd1 {`, - `_arguments \\\n.*"\(-o --option\)"{-o,--option}"\[option description]:" \\\n`, + `_arguments \\\n.*'\(-o --option\)'{-o,--option}'\[option description]:' \\\n`, }, }, { - name: "filename completion", + name: "filename completion with and without globs", root: func() *Command { var file string r := &Command{ @@ -90,10 +90,13 @@ func TestGenZshCompletion(t *testing.T) { } r.Flags().StringVarP(&file, "config", "c", file, "config file") r.MarkFlagFilename("config") + r.Flags().String("output", "", "output file") + r.MarkFlagFilename("output", "*.log", "*.txt") return r }(), expectedExpressions: []string{ - `\n +"\(-c --config\)"{-c,--config}"\[config file]:filename:_files"`, + `\n +'\(-c --config\)'{-c,--config}'\[config file]:filename:_files'`, + `:_files -g "\*.log" -g "\*.txt"`, }, }, { @@ -105,8 +108,8 @@ func TestGenZshCompletion(t *testing.T) { return r }(), expectedExpressions: []string{ - `"\*--option\[options]`, - `"\(\*-d \*--debug\)"{\\\*-d,\\\*--debug}`, + `'\*--option\[options]`, + `'\(\*-d \*--debug\)'{\\\*-d,\\\*--debug}`, }, }, { @@ -117,7 +120,7 @@ func TestGenZshCompletion(t *testing.T) { return r }(), expectedExpressions: []string{ - `"\*--verbose\[verbosity level]"`, + `'\*--verbose\[verbosity level]'`, }, skip: "BoolSlice behaves strangely both with NoOptDefVal and type (identifies as bool)", },