├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── data ├── templates │ ├── sample.json │ └── sample.tpl └── zgencomp.gif ├── main.go └── main_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | /assets.go 2 | /zgencomp 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | install: 4 | - go get github.com/jessevdk/go-flags 5 | 6 | before_script: 7 | - make install 8 | 9 | script: 10 | - go test -v 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | deps: 2 | go get github.com/jteeuwen/go-bindata/... 3 | 4 | install: deps 5 | go-bindata -o=assets.go ./data/templates/ 6 | go install 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zgencomp 2 | 3 | [![Build Status](https://travis-ci.org/b4b4r07/zgencomp.svg?branch=master)](https://travis-ci.org/b4b4r07/zgencomp "Travis CI") 4 | 5 | 8 | 9 | A generator for Zsh completion function 10 | 11 | ## Description 12 | 13 | `zgencomp` is CLI tool that automatically generates a completion function for Zsh. It generates the function based on the [JSON](http://json.org) file that you wrote about user interface of your command. 14 | 15 | ***DEMO:*** 16 | 17 | [![zgencomp.gif](data/zgencomp.gif)](https://github.com/b4b4r07/zgencomp#installation "b4b4r07/zgencomp") 18 | 19 | ## Features 20 | 21 | - Easily and instantly generatable 22 | - Just edit JSON file 23 | 24 | ## Requirement 25 | 26 | - Go 27 | 28 | ## Usage 29 | 30 | 1. To generate the sample/template JSON file: 31 | 32 | ```console 33 | $ zgencomp -g 34 | ``` 35 | 36 | 2. Edit the JSON file 37 | 38 | 3. To generate the completion function: 39 | 40 | ``` console 41 | $ zgencomp 42 | ``` 43 | 44 | After that you just placed the generated file to `$fpath`. 45 | 46 | **Note:** 47 | 48 | - If `zgencomp -g` is called without arguments, it generates `sample.json`. If you desire the name that is not a `sample.json`, it can be changed: 49 | 50 | ```console 51 | $ zgencomp --generate=mycmd.json 52 | ``` 53 | 54 | - Calling `zgencomp` without any arguments reads `sample.json` in the current directory. To read other json files: 55 | 56 | ```console 57 | $ zgencomp mycmd.json 58 | ``` 59 | 60 | For more information about `zgencomp`, type `zgencomp --help` in your Terminal. 61 | 62 | ## Installation 63 | 64 | Paste the following commands at a Terminal prompt. 65 | 66 | ```console 67 | $ go get github.com/b4b4r07/zgencomp 68 | $ cd $GOPATH/src/github.com/b4b4r07/zgencomp 69 | $ make install 70 | ``` 71 | 72 | ## Just edit json 73 | 74 | After installation, you only edit `json` file. Put the sample json file here. 75 | 76 | **Note:** 77 | I have extended the json file used by `zgencomp`. Thanks to that, you **can write a comment** of JavaScript style within the json file. Please check [here](./data/templates/sample.json "sample.json - GitHub/b4b4r07/zgencomp") for how to set up the json file. 78 | 79 | ```json 80 | { 81 | "command" : "mycmd", 82 | 83 | "properties" : { 84 | "author" : "John Doe", 85 | "license" : "MIT", 86 | "help" : { 87 | "option" : [ 88 | "--help" 89 | ], 90 | 91 | "description" : "Print a brief help message." 92 | }, 93 | "version" : { 94 | "option" : [ 95 | "-V", 96 | "--version" 97 | ], 98 | "description" : "Display version information and exit." 99 | } 100 | }, 101 | 102 | "options" : { 103 | "switch" : [ 104 | { 105 | "option" : [ 106 | "-c", 107 | "--count" 108 | ], 109 | "description" : "Only a count of selected lines is written to standard output.", 110 | "exclusion" : [ 111 | ] 112 | }, 113 | { 114 | "option" : [ 115 | ], 116 | "description" : "" 117 | } 118 | ], 119 | "flag" : [ 120 | { 121 | "option" : [ 122 | "-m", 123 | "--max-count" 124 | ], 125 | "description" : "Stop reading the file after num matches.", 126 | "exclusion" : [ 127 | "-c", 128 | "--count" 129 | ], 130 | "argument" : { 131 | "group" : "", 132 | "type" : "func", 133 | "style" : { 134 | "standard" : ["-m"], 135 | "touch" : [], 136 | "touchable" : [], 137 | "equal" : ["--max--count"], 138 | "equalable" : [] 139 | } 140 | } 141 | }, 142 | { 143 | "option" : [ 144 | ], 145 | "description" : "", 146 | "exclusion" : [ 147 | ], 148 | "argument": { 149 | "group" : "", 150 | "type" : "", 151 | "style" : { 152 | "standard" : [], 153 | "touch" : [], 154 | "touchable" : [], 155 | "equal" : [], 156 | "equalable" : [] 157 | } 158 | } 159 | } 160 | ] 161 | }, 162 | 163 | "arguments" : { 164 | "always" : true, 165 | "type" : "func" 166 | } 167 | } 168 | ``` 169 | 170 | ## Author 171 | 172 | [@b4b4r07](https://twitter.com/b4b4r07) 173 | 174 | ## Licence 175 | 176 | [MIT](https://raw.githubusercontent.com/b4b4r07/dotfiles/master/doc/LICENSE-MIT.txt) 177 | 178 | 185 | -------------------------------------------------------------------------------- /data/templates/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | // Command name you want to complement 3 | "command" : "mycmd", 4 | 5 | // Property for the command 6 | "properties" : { 7 | "author" : "John Doe", 8 | "license" : "MIT", 9 | 10 | 11 | // For the help and version, it is treated as 12 | // one of the attribute information instead of 13 | // the normal options (special option). 14 | // The reason for this is because in accordance 15 | // with the GNU command-line interfaces style. 16 | // cf https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html 17 | "help" : { 18 | // Although not talking limited only this 'help', 19 | // if the "option" does not have the one or more elements, 20 | // that option is not enabled. 21 | // ( Example ) 22 | // "option" : [] => Not available 23 | "option" : [ 24 | "-h", 25 | "--help" 26 | ], 27 | 28 | // Description of commands that are displayed during completion 29 | // ( Example ) 30 | // -h, --help something description 31 | "description" : "show this help and exit" 32 | }, 33 | 34 | // Almost same as "help" 35 | "version" : { 36 | "option" : [ 37 | "-V", 38 | "--version" 39 | ], 40 | "description" : "show version" 41 | } 42 | }, 43 | 44 | // Normal option {{{1 45 | // The normal option consists of flag options that always take arguments 46 | // and switch options that take no arguments. 47 | // (However, the flag option might be omitted arguments. 48 | // This does not mean that no arguments.) 49 | "options" : { 50 | 51 | // Switch options {{{2 52 | "switch" : [ 53 | { 54 | "option" : [ 55 | "-p", 56 | "--plain" 57 | ], 58 | "description" : "show with plain text", 59 | 60 | // The exclusive control of the option 61 | // Normally, there is no need to specify the same options at a time 62 | // eg. `ls -l -l file` 63 | // In addition, optional command has the combination is meaningless 64 | // by specifying the same time. 65 | // For example, ssh -4, -6 option is specified 66 | // to explicitly connect to each IPv4, IPv6 67 | // It is meaningless to specify both this. 68 | // This exclusion item will remove the option that has already 69 | // appeared on the command line and the meaningless option 70 | // from the following completion. 71 | "exclusion" : [ 72 | ] 73 | }, 74 | 75 | // empty 76 | { 77 | "option" : [ 78 | ], 79 | "description" : "" 80 | } 81 | ], 82 | // }}} 83 | 84 | // Flag options {{{2 85 | "flag" : [ 86 | { 87 | "option" : [ 88 | "-A", 89 | "--after-context" 90 | ], 91 | "description" : "Print num lines of trailing context after each match", 92 | 93 | "exclusion" : [ 94 | "-p" 95 | ], 96 | 97 | // Defin arguments that flag option to take. 98 | "argument" : { 99 | // Complement word is grouped when complement 100 | // Set the group name here 101 | // If blank, do not set the group name. 102 | "group" : "", 103 | 104 | // Define the kind of argument that flag option takes 105 | // Select from the following 106 | // "file", "dir", "func" 107 | // 108 | // If you want to specify complement words directly, try to below. 109 | // "type" : [ 110 | // "init", 111 | // "add", 112 | // "commit" 113 | // ], 114 | // 115 | // Take as follows if you want to add a description of the complement word. 116 | // "type" : { 117 | // "init" : "Create an empty Git repository or reinitialize an existing one", 118 | // "add" : "Add file contents to the index", 119 | // "commit" : "Record changes to the repository" 120 | // }, 121 | "type" : "func", 122 | 123 | // Define a way of taking the argument of flag options 124 | // Flag style should be applicable to any of the following 125 | // Flag style is required to apply to any of 126 | // "standard", "touch", "touchable", "equal" or "equalable" 127 | // Example1$B!'(B "touch" : ["-A"] 128 | // Example2$B!'(B "equal" : ["-A", "--after-context"] 129 | // Options that are not affiliated is treated as "standard". 130 | // If the option is belong to more than one style, first style is adapted. 131 | // Example3: "touch" : ["-A"], "equal" : ["-A"] => "touch" 132 | "style" : { 133 | // standard is 134 | // -opt VAL 135 | "standard" : ["-A"], 136 | 137 | // touch is 138 | // -optVAL 139 | "touch" : [], 140 | 141 | // touchable is 142 | // -optVAL or -opt VAL 143 | "touchable" : [], 144 | 145 | // equal is 146 | // -opt=VAL 147 | "equal" : ["--after-context"], 148 | 149 | // equalable is 150 | // -opt=VAL or -opt VAL 151 | "equalable" : [] 152 | } 153 | } 154 | }, 155 | 156 | // empty 157 | { 158 | "option" : [ 159 | ], 160 | "description" : "", 161 | "exclusion" : [ 162 | ], 163 | "argument": { 164 | "group" : "", 165 | "type" : "", 166 | "style" : { 167 | "standard" : [], 168 | "touch" : [], 169 | "touchable" : [], 170 | "equal" : [], 171 | "equalable" : [] 172 | } 173 | } 174 | } 175 | ] 176 | // }}} 177 | }, 178 | // }}} 179 | 180 | // Arguments of the command {{{1 181 | "arguments" : { 182 | // Whether the command requires arguments 183 | // `mkdir` => true 184 | // `cd` => false 185 | "always" : true, 186 | 187 | "after_arg" : false, 188 | 189 | // Almost same as type od flag option 190 | "type" : "func" 191 | } 192 | // }}} 193 | } 194 | // vim: fdm=marker 195 | -------------------------------------------------------------------------------- /data/templates/sample.tpl: -------------------------------------------------------------------------------- 1 | #compdef {{.Command}} 2 | 3 | {{if .Properties.Author}}# Copyright (c) {{dateYear}} {{.Properties.Author}}{{end}} 4 | {{if .Properties.License}}# License: {{.Properties.License}}{{end}} 5 | 6 | function _{{.Command}}() { 7 | local context curcontext=$curcontext state line 8 | typeset -A opt_args 9 | local ret=1 10 | 11 | _arguments -C \ 12 | {{if .Properties.Help.Option -}} 13 | '{{.Properties.Help.Option | dealWithExclusion}}{{.Properties.Help.Option | dealWithOption}}[{{.Properties.Help.Description | dealWithDescription}}]' \ 14 | {{end -}} 15 | {{if .Properties.Version.Option -}} 16 | '{{.Properties.Version.Option | dealWithExclusion}}{{.Properties.Version.Option | dealWithOption}}[{{.Properties.Version.Description | dealWithDescription}}]' \ 17 | {{end -}} 18 | {{range .Options.Switch -}} 19 | '{{.| dealWithSwitchExclusion}}{{.| dealWithSwitchOption}}[{{.Description | dealWithDescription}}]' \ 20 | {{end -}} 21 | {{range .Options.Flag -}} 22 | '{{.| dealWithFlagExclusion}}{{.| dealWithFlagOption}}[{{.Description | dealWithDescription}}]:{{.| setFlagMessage}}:{{.| setAction}}' \ 23 | {{end -}} 24 | {{if .Arguments -}} 25 | '{{if not .Arguments.After_arg}}(-){{end}}*:arguments:{{.Arguments.Type | setAction}}' \ 26 | {{end -}} 27 | && ret=0 28 | 29 | {{if .Arguments -}} 30 | case $state in 31 | {{range .Options.Flag}}{{if .Option | whetherOptionIsEnabled}}{{if .Argument.Type | whetherTypeIsFunc}}{{.| setAction | helperTrimArrowInType}}) 32 | # TODO 33 | ;; 34 | {{end}}{{end}}{{end}}{{if .Arguments.Type | whetherTypeIsFunc}}{{.Arguments.Type | setAction | helperTrimArrowInType}}) 35 | # TODO 36 | ;;{{end}} 37 | esac 38 | {{end -}} 39 | 40 | return ret 41 | } 42 | 43 | _{{.Command}} "$@" 44 | -------------------------------------------------------------------------------- /data/zgencomp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babarot/zgencomp/c6984bd9ce6d5c1164750675216be42b02c3d0b3/data/zgencomp.gif -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "os" 11 | "path" 12 | "regexp" 13 | "strings" 14 | "text/template" 15 | "time" 16 | 17 | flags "github.com/jessevdk/go-flags" 18 | ) 19 | 20 | const ( 21 | JSON = "data/templates/sample.json" 22 | TPL = "data/templates/sample.tpl" 23 | ) 24 | 25 | // type JsonData struct {{{1 26 | type JsonData struct { 27 | Command string `json:"command"` 28 | Properties Properties `json:"properties"` 29 | Options Options `json:"options"` 30 | Arguments *Arguments `json:"arguments"` 31 | } 32 | 33 | type Properties struct { 34 | Author string `json:"author"` 35 | License string `json:"license"` 36 | Help Help `json:"help"` 37 | Version Version `json:"version"` 38 | } 39 | 40 | type Help struct { 41 | Option []string `json:"option"` 42 | Description string `json:"description"` 43 | } 44 | 45 | type Version struct { 46 | Option []string `json:"option"` 47 | Description string `json:"description"` 48 | } 49 | 50 | type Options struct { 51 | Switch []Switch `json:"switch"` 52 | Flag []Flag `json:"flag"` 53 | } 54 | 55 | type Switch struct { 56 | Option []string `json:"option"` 57 | Description string `json:"description"` 58 | Exclusion []string `json:"exclusion"` 59 | } 60 | 61 | type Flag struct { 62 | Option []string `json:"option"` 63 | Description string `json:"description"` 64 | Exclusion []string `json:"exclusion"` 65 | Argument Argument `json:"argument"` 66 | } 67 | 68 | type Argument struct { 69 | Group string `json:"group"` 70 | Type interface{} `json:"type"` 71 | Style map[string][]string `json:"style"` 72 | } 73 | 74 | type Arguments struct { 75 | Always bool `json:"always"` 76 | After_arg bool `json:"after_arg"` 77 | Type interface{} `json:"type"` 78 | } 79 | 80 | //}}} 81 | 82 | // Library {{{1 83 | func isExists(filename string) bool { 84 | _, err := os.Stat(filename) 85 | return err == nil 86 | } 87 | 88 | func stringInSlice(a string, list []string) bool { 89 | for _, b := range list { 90 | if b == a { 91 | return true 92 | } 93 | } 94 | return false 95 | } 96 | 97 | //}}} 98 | 99 | // getter function {{{1 100 | func readJson(f string) (jd JsonData, err error) { 101 | // Read json file if exists 102 | file, err := os.Open(f) 103 | if err != nil { 104 | return 105 | } 106 | defer file.Close() 107 | 108 | var contents string 109 | scanner := bufio.NewScanner(file) 110 | for scanner.Scan() { 111 | if m, _ := regexp.MatchString("^ *\\/\\/", scanner.Text()); !m { 112 | contents += scanner.Text() + "\n" 113 | } 114 | } 115 | err = scanner.Err() 116 | if err != nil { 117 | return 118 | } 119 | 120 | // Deal with json file 121 | //decoder := json.NewDecoder(file) 122 | decoder := json.NewDecoder(strings.NewReader(contents)) 123 | err = decoder.Decode(&jd) 124 | if err != nil { 125 | err = fmt.Errorf("[json file broken]: %s", err) 126 | return 127 | } 128 | 129 | // In case of json file broken 130 | if jd.Command == "" { 131 | err = errors.New("[json file broken]: set command name") 132 | } 133 | 134 | return jd, err 135 | } 136 | 137 | func (jd JsonData) jsonOutput(stream io.Writer) (err error) { 138 | funcMap := template.FuncMap{ 139 | "dealWithOption": dealWithOption, 140 | "dealWithSwitchOption": dealWithSwitchOption, 141 | "dealWithFlagOption": dealWithFlagOption, 142 | "dealWithExclusion": dealWithExclusion, 143 | "dealWithSwitchExclusion": dealWithSwitchExclusion, 144 | "dealWithFlagExclusion": dealWithFlagExclusion, 145 | "dealWithDescription": dealWithDescription, 146 | "dealWithFlagArgumentStyle": dealWithFlagArgumentStyle, 147 | "whetherOptionIsEnabled": whetherOptionIsEnabled, 148 | "whetherTypeIsFunc": whetherTypeIsFunc, 149 | "setFlagMessage": setFlagMessage, 150 | "setAction": setAction, 151 | "helperTrimArrowInType": helperTrimArrowInType, 152 | "dateYear": func() int { now := time.Now(); return now.Year() }, 153 | } 154 | 155 | // Replace ioutil with Asset 156 | //contents, err := ioutil.ReadFile("./sample.tpl") 157 | contents, err := Asset(TPL) 158 | if err != nil { 159 | //log.Fatal(err) 160 | return 161 | } 162 | 163 | tpl := template.Must(template.New("scan").Funcs(funcMap).Parse(string(contents))) 164 | data := JsonData{ 165 | jd.Command, 166 | jd.Properties, 167 | jd.Options, 168 | jd.Arguments, 169 | } 170 | 171 | err = tpl.Execute(stream, data) 172 | if err != nil { 173 | //log.Fatal(err) 174 | return 175 | } 176 | 177 | return 178 | } 179 | 180 | func generateSampleJson(f string) (err error) { 181 | data, err := Asset(JSON) 182 | if err != nil { 183 | return 184 | } 185 | 186 | if isExists(f) { 187 | fmt.Fprintf(os.Stderr, "%s is already exists, overwrite it? [y/N]: ", f) 188 | var ans string 189 | _, err = fmt.Scanf("%s", &ans) 190 | if err != nil { 191 | return 192 | } 193 | 194 | if strings.ToLower(ans) == "y" { 195 | err = os.RemoveAll(f) 196 | if err != nil { 197 | return 198 | } 199 | } else { 200 | os.Exit(0) 201 | } 202 | } 203 | 204 | ioutil.WriteFile(f, data, 0644) 205 | return 206 | } 207 | 208 | //}}} 209 | 210 | type CLI struct { 211 | Help bool `short:"h" long:"help" description:"Show this help and exit"` 212 | Version bool `long:"version" description:"Show version"` 213 | File bool `short:"f" long:"file" description:"Write to file instead of stdout"` 214 | Generate string `short:"g" long:"genrate" description:"Generate" optional:"yes" optional-value:"sample.json"` 215 | } 216 | 217 | const version = "0.2" 218 | 219 | var cli CLI 220 | 221 | func main() { 222 | parser := flags.NewParser(&cli, flags.Default) 223 | parser.Name = "zgencomp" 224 | parser.Usage = "[OPTION]" 225 | 226 | args, _ := parser.Parse() 227 | 228 | // --version 229 | // deal with version 230 | if cli.Version { 231 | fmt.Printf("%s\n", version) 232 | os.Exit(0) 233 | } 234 | 235 | // -g, --genrate 236 | // deal with generating 237 | if cli.Generate != "" { 238 | if err := generateSampleJson(cli.Generate); err != nil { 239 | os.Exit(1) 240 | } 241 | os.Exit(0) 242 | } 243 | 244 | // Reading json file 245 | f := path.Base(JSON) 246 | if len(args) != 0 { 247 | f = args[0] 248 | } 249 | jd, err := readJson(f) 250 | if err != nil { 251 | fmt.Fprintf(os.Stderr, "%v\n", err) 252 | os.Exit(1) 253 | } 254 | 255 | // deal with output json 256 | out := os.Stdout 257 | if cli.File { 258 | f, err := os.Create("_" + jd.Command) 259 | if err != nil { 260 | os.Exit(1) 261 | } 262 | out = f 263 | } 264 | 265 | // Analyzing json and output 266 | err = jd.jsonOutput(out) 267 | if err != nil { 268 | fmt.Fprintf(os.Stderr, "%v\n", err) 269 | os.Exit(1) 270 | } 271 | } 272 | 273 | // dealWithOption {{{ 274 | func dealWithOption(s []string) (ret string) { 275 | if len(s) == 1 { 276 | ret = s[0] 277 | } else { 278 | ret = "'{" + strings.Join(s, ",") + "}'" 279 | } 280 | 281 | return 282 | } 283 | 284 | //}}} 285 | 286 | // dealWithSwitchOption {{{ 287 | func dealWithSwitchOption(s Switch) (ret string) { 288 | ret = dealWithOption(s.Option) 289 | return 290 | } 291 | 292 | //}}} 293 | 294 | // dealWithFlagOption {{{ 295 | func dealWithFlagOption(s Flag) (ret string) { 296 | ret = dealWithOption(dealWithFlagArgumentStyle(s)) 297 | return 298 | } 299 | 300 | //}}} 301 | 302 | // dealWithExclusion {{{ 303 | func dealWithExclusion(s []string) (ret string) { 304 | return "(" + strings.Join(s, " ") + ")" 305 | } 306 | 307 | //}}} 308 | 309 | // dealWithSwitchExclusion {{{ 310 | func dealWithSwitchExclusion(s Switch) (ret string) { 311 | if len(s.Exclusion) == 0 { 312 | ret = strings.Join(s.Option, " ") 313 | ret = "(" + ret + ")" 314 | return 315 | } 316 | 317 | var exclusion string 318 | for _, e := range s.Option { 319 | if stringInSlice(e, s.Exclusion) { 320 | continue 321 | } 322 | exclusion = exclusion + " " + e 323 | } 324 | for _, e := range s.Exclusion { 325 | if stringInSlice(e, s.Option) { 326 | continue 327 | } 328 | exclusion = exclusion + " " + e 329 | } 330 | exclusion = strings.TrimSpace(exclusion) 331 | ret = "(" + exclusion + ")" 332 | 333 | return 334 | } 335 | 336 | //}}} 337 | 338 | // dealWithFlagExclusion {{{ 339 | func dealWithFlagExclusion(s Flag) (ret string) { 340 | if len(s.Exclusion) == 0 { 341 | ret = strings.Join(s.Option, " ") 342 | ret = "(" + ret + ")" 343 | return 344 | } 345 | 346 | var exclusion string 347 | for _, e := range s.Option { 348 | if stringInSlice(e, s.Exclusion) { 349 | continue 350 | } 351 | exclusion = exclusion + " " + e 352 | } 353 | for _, e := range s.Exclusion { 354 | if stringInSlice(e, s.Option) { 355 | continue 356 | } 357 | exclusion = exclusion + " " + e 358 | } 359 | exclusion = strings.TrimSpace(exclusion) 360 | ret = "(" + exclusion + ")" 361 | 362 | return 363 | } 364 | 365 | //}}} 366 | 367 | // dealWithDescription {{{ 368 | func dealWithDescription(s string) (ret string) { 369 | ret = strings.Replace(s, "'", "'\\''", -1) 370 | return 371 | } 372 | 373 | //}}} 374 | 375 | // dealWithFlagArgumentStyle {{{ 376 | func dealWithFlagArgumentStyle(s Flag) (ret []string) { 377 | retMap := make(map[string][]string) 378 | 379 | for k, v := range s.Argument.Style { 380 | // Skip blank value 381 | // map[equal:[--after-context] equalable:[] standard:[-A] touch:[-a] touchable:[]] 382 | // ==> map[standard:[-A] touch:[-a] equal:[--after-context]] 383 | if len(v) == 0 { 384 | continue 385 | } 386 | 387 | // Skip invalid value 388 | // map[standard:[-A] touch:[-a] equal:[--after-context]] 389 | // ==> map[standard:[-A] equal:[--after-context]] 390 | for _, e := range v { 391 | if stringInSlice(e, s.Option) { 392 | retMap[k] = v 393 | } 394 | } 395 | } 396 | 397 | var retSlice []string 398 | for _, e := range s.Option { 399 | if t := helperAddFlagArgumentStyle(retMap, e); t != "" { 400 | retSlice = append(retSlice, t) 401 | } 402 | } 403 | return retSlice 404 | } 405 | 406 | //}}} 407 | 408 | // whetherOptionIsEnabled {{{ 409 | // check whether there is one or more options 410 | func whetherOptionIsEnabled(s []string) (ret bool) { 411 | ret = false 412 | if len(s) != 0 { 413 | ret = true 414 | } 415 | 416 | return 417 | } 418 | 419 | //}}} 420 | 421 | // whetherTypeIsFunc {{{ 422 | // check whether Options.Flag.Argument.Type or Arguments.Type is "func" 423 | func whetherTypeIsFunc(s interface{}) (ret bool) { 424 | switch s.(type) { 425 | case Flag: 426 | s = s.(Flag).Argument.Type 427 | } 428 | switch s.(type) { 429 | case string: 430 | ret = s.(string) == "func" 431 | default: 432 | ret = false 433 | } 434 | return 435 | } 436 | 437 | //}}} 438 | 439 | // setFlagMessage {{{ 440 | func setFlagMessage(s Flag) (ret string) { 441 | ret = s.Argument.Group 442 | if ret == "" { 443 | ret = " " 444 | } 445 | 446 | return 447 | } 448 | 449 | //}}} 450 | 451 | // setAction {{{ 452 | // set action of completion 453 | func setAction(s interface{}) (ret string) { 454 | backup := s 455 | isFlag := false 456 | 457 | switch s.(type) { 458 | case Flag: 459 | s = s.(Flag).Argument.Type 460 | isFlag = true 461 | } 462 | 463 | switch s.(type) { 464 | case string: 465 | // assume "func" and so on 466 | ret = s.(string) 467 | switch ret { 468 | case "func": 469 | var opt string 470 | if isFlag { 471 | if len(backup.(Flag).Option) == 0 { 472 | return 473 | } 474 | opt = backup.(Flag).Option[0] 475 | re, _ := regexp.Compile("^(--?|\\+)") 476 | opt = re.ReplaceAllString(opt, "") 477 | ret = opt + "_func" 478 | } else { 479 | ret = "args" 480 | } 481 | ret = "->" + ret 482 | case "file": 483 | ret = "_files" 484 | case "dir", "directory": 485 | ret = "_files -/" 486 | default: 487 | ret = " " 488 | } 489 | 490 | case []string: 491 | // assume "(word1 word2...)" 492 | ret = strings.Join(s.([]string), " ") 493 | ret = "(" + ret + ")" 494 | 495 | case []interface{}: 496 | // assume "(word1 word2...)" 497 | for _, v := range s.([]interface{}) { 498 | ret = ret + " " + v.(string) 499 | } 500 | ret = "(" + strings.TrimSpace(ret) + ")" 501 | 502 | case map[string]string: 503 | // assume "((word1\:desc1 word2\:desc2...))" 504 | for k, v := range s.(map[string]string) { 505 | ret = ret + k + "\\:" + "\"" + v + "\"" + " " 506 | } 507 | ret = "((" + strings.TrimSpace(ret) + "))" 508 | 509 | case map[string]interface{}: 510 | for k, v := range s.(map[string]interface{}) { 511 | ret = ret + k + "\\:" + "\"" + v.(string) + "\"" + " " 512 | } 513 | ret = "((" + strings.TrimSpace(ret) + "))" 514 | 515 | default: 516 | ret = "[[Parse Error]]" 517 | } 518 | 519 | return 520 | } 521 | 522 | //}}} 523 | 524 | // helperTrimArrowInType {{{ 525 | func helperTrimArrowInType(s string) string { 526 | return strings.TrimLeft(s, "->") 527 | } 528 | 529 | //}}} 530 | 531 | // helperAddFlagArgumentStyle {{{ 532 | func helperAddFlagArgumentStyle(m map[string][]string, s string) (ret string) { 533 | for k, v := range m { 534 | switch k { 535 | case "standard": 536 | for _, e := range v { 537 | if e == s { 538 | ret = strings.Replace(e, e, e, -1) 539 | return 540 | } 541 | } 542 | case "touch": 543 | for _, e := range v { 544 | if e == s { 545 | ret = strings.Replace(e, e, e+"-", -1) 546 | return 547 | } 548 | } 549 | case "touchable": 550 | for _, e := range v { 551 | if e == s { 552 | ret = strings.Replace(e, e, e+"+", -1) 553 | return 554 | } 555 | } 556 | case "equal": 557 | for _, e := range v { 558 | if e == s { 559 | ret = strings.Replace(e, e, e+"=-", -1) 560 | return 561 | } 562 | } 563 | case "equalable": 564 | for _, e := range v { 565 | if e == s { 566 | ret = strings.Replace(e, e, e+"=", -1) 567 | return 568 | } 569 | } 570 | } 571 | } 572 | ret = s 573 | return 574 | } 575 | 576 | //}}} 577 | 578 | //}}} 579 | 580 | // vim: fdm=marker fdc=3 581 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // readJson 8 | func TestReadJson(t *testing.T) { 9 | } 10 | 11 | // jsonOutput 12 | func TestJsonOutput(t *testing.T) { 13 | } 14 | 15 | // generateSampleJson 16 | func TestGenerateSampleJson(t *testing.T) { 17 | } 18 | 19 | // main 20 | func TestMain(t *testing.T) { 21 | } 22 | 23 | // 24 | 25 | // dealWithOption {{{ 26 | func TestDealWithOptionIfArgument(t *testing.T) { 27 | actual := dealWithOption([]string{"-a"}) 28 | expected := "-a" 29 | if actual != expected { 30 | t.Errorf("got %v\nwant %v", actual, expected) 31 | } 32 | } 33 | 34 | func TestDealWithOptionIfArguments(t *testing.T) { 35 | actual := dealWithOption([]string{"-a", "--all"}) 36 | expected := "'{-a,--all}'" 37 | if actual != expected { 38 | t.Errorf("got %v\nwant %v", actual, expected) 39 | } 40 | } 41 | 42 | //}}} 43 | 44 | // dealWithSwitchOption {{{ 45 | func TestDealWithSwitchOptionIfArgument(t *testing.T) { 46 | var s Switch = Switch{ 47 | Option: []string{"-a"}, 48 | Description: "", 49 | Exclusion: []string{}, 50 | } 51 | 52 | actual := dealWithSwitchOption(s) 53 | expected := "-a" 54 | if actual != expected { 55 | t.Errorf("got %v\nwant %v", actual, expected) 56 | } 57 | } 58 | 59 | func TestDealWithSwitchOptionIfArguments(t *testing.T) { 60 | var s Switch = Switch{ 61 | Option: []string{"-a", "--all"}, 62 | Description: "", 63 | Exclusion: []string{}, 64 | } 65 | 66 | actual := dealWithSwitchOption(s) 67 | expected := "'{-a,--all}'" 68 | if actual != expected { 69 | t.Errorf("got %v\nwant %v", actual, expected) 70 | } 71 | } 72 | 73 | //}}} 74 | 75 | // dealWithFlagOption {{{ 76 | func TestDealWithFlagOption(t *testing.T) { 77 | var f Flag = Flag{ 78 | Option: []string{"-a", "--all"}, 79 | Description: "", 80 | Exclusion: []string{}, 81 | Argument: Argument{ 82 | Group: "", 83 | Type: nil, 84 | Style: map[string][]string{ 85 | "standard": []string{}, 86 | "touch": []string{}, 87 | "touchable": []string{}, 88 | "equal": []string{}, 89 | "equalable": []string{}, 90 | }, 91 | }, 92 | } 93 | 94 | actual := dealWithFlagOption(f) 95 | expected := "'{-a,--all}'" 96 | 97 | if actual != expected { 98 | t.Errorf("got %v\nwant %v", actual, expected) 99 | } 100 | } 101 | 102 | func TestDealWithFlagOptionEqual(t *testing.T) { 103 | var f Flag = Flag{ 104 | Option: []string{"-a", "--all"}, 105 | Description: "", 106 | Exclusion: []string{}, 107 | Argument: Argument{ 108 | Group: "", 109 | Type: nil, 110 | Style: map[string][]string{ 111 | "standard": []string{}, 112 | "touch": []string{}, 113 | "touchable": []string{}, 114 | "equal": []string{"--all"}, 115 | "equalable": []string{}, 116 | }, 117 | }, 118 | } 119 | 120 | actual := dealWithFlagOption(f) 121 | expected := "'{-a,--all=-}'" 122 | 123 | if actual != expected { 124 | t.Errorf("got %v\nwant %v", actual, expected) 125 | } 126 | } 127 | 128 | //}}} 129 | 130 | // dealWithExclusion {{{ 131 | func TestDealWithExclusion(t *testing.T) { 132 | actual := dealWithExclusion([]string{"-a", "--all"}) 133 | expected := "(-a --all)" 134 | 135 | if actual != expected { 136 | t.Errorf("got %v\nwant %v", actual, expected) 137 | } 138 | } 139 | 140 | //}}} 141 | 142 | // dealWithSwitchExclusion {{{ 143 | func TestDealWithSwitchExclusionNoExclusion(t *testing.T) { 144 | var s Switch = Switch{ 145 | Option: []string{"-opt"}, 146 | Description: "", 147 | Exclusion: []string{}, 148 | } 149 | 150 | actual := dealWithSwitchExclusion(s) 151 | expected := "(-opt)" 152 | 153 | if actual != expected { 154 | t.Errorf("got %v\nwant %v", actual, expected) 155 | } 156 | } 157 | 158 | func TestDealWithSwitchExclusionExclusion(t *testing.T) { 159 | var s Switch = Switch{ 160 | Option: []string{"-opt"}, 161 | Description: "", 162 | Exclusion: []string{"--other"}, 163 | } 164 | 165 | actual := dealWithSwitchExclusion(s) 166 | expected := "(-opt --other)" 167 | 168 | if actual != expected { 169 | t.Errorf("got %v\nwant %v", actual, expected) 170 | } 171 | } 172 | 173 | func TestDealWithSwitchExclusionToggle(t *testing.T) { 174 | var s Switch = Switch{ 175 | Option: []string{"-opt"}, 176 | Description: "", 177 | Exclusion: []string{"--other", "-opt"}, 178 | } 179 | 180 | actual := dealWithSwitchExclusion(s) 181 | expected := "(--other)" 182 | 183 | if actual != expected { 184 | t.Errorf("got %v\nwant %v", actual, expected) 185 | } 186 | } 187 | 188 | //}}} 189 | 190 | // dealWithFlagExclusion {{{ 191 | func TestDealWithFlagExclusionNoExclusion(t *testing.T) { 192 | var f Flag = Flag{ 193 | Option: []string{"-opt"}, 194 | Description: "", 195 | Exclusion: []string{}, 196 | } 197 | 198 | actual := dealWithFlagExclusion(f) 199 | expected := "(-opt)" 200 | 201 | if actual != expected { 202 | t.Errorf("got %v\nwant %v", actual, expected) 203 | } 204 | } 205 | 206 | func TestDealWithFlagExclusionExclusion(t *testing.T) { 207 | var f Flag = Flag{ 208 | Option: []string{"-opt"}, 209 | Description: "", 210 | Exclusion: []string{"--other"}, 211 | } 212 | 213 | actual := dealWithFlagExclusion(f) 214 | expected := "(-opt --other)" 215 | 216 | if actual != expected { 217 | t.Errorf("got %v\nwant %v", actual, expected) 218 | } 219 | } 220 | 221 | func TestDealWithFlagExclusionToggle(t *testing.T) { 222 | var f Flag = Flag{ 223 | Option: []string{"-opt"}, 224 | Description: "", 225 | Exclusion: []string{"--other", "-opt"}, 226 | } 227 | 228 | actual := dealWithFlagExclusion(f) 229 | expected := "(--other)" 230 | 231 | if actual != expected { 232 | t.Errorf("got %v\nwant %v", actual, expected) 233 | } 234 | } 235 | 236 | //}}} 237 | 238 | // dealWithDescription {{{ 239 | func TestDealWithDescription(t *testing.T) { 240 | actual := dealWithDescription("description") 241 | expected := "description" 242 | 243 | if actual != expected { 244 | t.Errorf("got %v\nwant %v", actual, expected) 245 | } 246 | } 247 | 248 | func TestDealWithDescriptionIncludeSingleQuotation(t *testing.T) { 249 | actual := dealWithDescription("I'm lovin' it") 250 | expected := "I'\\''m lovin'\\'' it" 251 | 252 | if actual != expected { 253 | t.Errorf("got %v\nwant %v", actual, expected) 254 | } 255 | } 256 | 257 | //}}} 258 | 259 | // dealWithFlagArgumentStyle {{{ 260 | func helperEqual(a, b []string) bool { 261 | if len(a) != len(b) { 262 | return false 263 | } 264 | 265 | for i := range a { 266 | if a[i] != b[i] { 267 | return false 268 | } 269 | } 270 | 271 | return true 272 | } 273 | 274 | func TestDealWithFlagArgumentStyle(t *testing.T) { 275 | var f Flag = Flag{ 276 | Option: []string{"-a", "--all"}, 277 | Description: "", 278 | Exclusion: []string{}, 279 | Argument: Argument{ 280 | Group: "", 281 | Type: nil, 282 | Style: map[string][]string{ 283 | "standard": []string{}, 284 | "touch": []string{}, 285 | "touchable": []string{}, 286 | "equal": []string{"--all"}, 287 | "equalable": []string{}, 288 | }, 289 | }, 290 | } 291 | 292 | actual := dealWithFlagArgumentStyle(f) 293 | expected := []string{"-a", "--all=-"} 294 | 295 | //if !reflect.DeepEqual(actual, expected) { 296 | if !helperEqual(actual, expected) { 297 | t.Errorf("got %v\nwant %v", actual, expected) 298 | } 299 | } 300 | 301 | func TestDealWithFlagArgumentStyle2(t *testing.T) { 302 | var f Flag = Flag{ 303 | Option: []string{"-a", "--all"}, 304 | Description: "", 305 | Exclusion: []string{}, 306 | Argument: Argument{ 307 | Group: "", 308 | Type: nil, 309 | Style: map[string][]string{ 310 | "standard": []string{}, 311 | "touch": []string{}, 312 | "touchable": []string{"-a"}, 313 | "equal": []string{"--all"}, 314 | "equalable": []string{}, 315 | }, 316 | }, 317 | } 318 | 319 | actual := dealWithFlagArgumentStyle(f) 320 | expected := []string{"-a+", "--all=-"} 321 | 322 | //if !reflect.DeepEqual(actual, expected) { 323 | if !helperEqual(actual, expected) { 324 | t.Errorf("got %v\nwant %v", actual, expected) 325 | } 326 | } 327 | 328 | //}}} 329 | 330 | // whetherOptionIsEnabled {{{ 331 | func TestWhetherOptionIsEnabledFalse(t *testing.T) { 332 | actual := whetherOptionIsEnabled([]string{}) 333 | expected := false 334 | 335 | if actual != expected { 336 | t.Errorf("got %v\nwant %v", actual, expected) 337 | } 338 | } 339 | 340 | func TestWhetherOptionIsEnabledTrue(t *testing.T) { 341 | actual := whetherOptionIsEnabled([]string{"-a"}) 342 | expected := true 343 | 344 | if actual != expected { 345 | t.Errorf("got %v\nwant %v", actual, expected) 346 | } 347 | } 348 | 349 | //}}} 350 | 351 | // whetherTypeIsFunc {{{ 352 | func TestWhetherTypeIsFunc(t *testing.T) { 353 | actual := whetherTypeIsFunc("func") 354 | expected := true 355 | 356 | if actual != expected { 357 | t.Errorf("got %v\nwant %v", actual, expected) 358 | } 359 | } 360 | 361 | func TestWhetherTypeIsFuncFlag(t *testing.T) { 362 | var f Flag = Flag{ 363 | Option: []string{}, 364 | Description: "", 365 | Exclusion: []string{}, 366 | Argument: Argument{ 367 | Group: "", 368 | Type: "func", 369 | Style: map[string][]string{ 370 | "standard": []string{}, 371 | "touch": []string{}, 372 | "touchable": []string{}, 373 | "equal": []string{}, 374 | "equalable": []string{}, 375 | }, 376 | }, 377 | } 378 | 379 | actual := whetherTypeIsFunc(f) 380 | expected := true 381 | 382 | if actual != expected { 383 | t.Errorf("got %v\nwant %v", actual, expected) 384 | } 385 | } 386 | 387 | func TestWhetherTypeIsNotFunc(t *testing.T) { 388 | actual := whetherTypeIsFunc("file") 389 | expected := false 390 | 391 | if actual != expected { 392 | t.Errorf("got %v\nwant %v", actual, expected) 393 | } 394 | } 395 | 396 | func TestWhetherTypeIsNotString(t *testing.T) { 397 | actual := whetherTypeIsFunc([]string{}) 398 | expected := false 399 | 400 | if actual != expected { 401 | t.Errorf("got %v\nwant %v", actual, expected) 402 | } 403 | } 404 | 405 | //}}} 406 | 407 | // setFlagMessage {{{ 408 | func TestSetFlagMessage(t *testing.T) { 409 | var f Flag = Flag{ 410 | Option: []string{}, 411 | Description: "", 412 | Exclusion: []string{}, 413 | Argument: Argument{ 414 | Group: "akb", 415 | Type: "", 416 | Style: map[string][]string{ 417 | "standard": []string{}, 418 | "touch": []string{}, 419 | "touchable": []string{}, 420 | "equal": []string{}, 421 | "equalable": []string{}, 422 | }, 423 | }, 424 | } 425 | 426 | actual := setFlagMessage(f) 427 | expected := "akb" 428 | 429 | if actual != expected { 430 | t.Errorf("got %v\nwant %v", actual, expected) 431 | } 432 | } 433 | 434 | func TestSetFlagMessageBlank(t *testing.T) { 435 | var f Flag = Flag{ 436 | Option: []string{}, 437 | Description: "", 438 | Exclusion: []string{}, 439 | Argument: Argument{ 440 | Group: "", 441 | Type: "", 442 | Style: map[string][]string{ 443 | "standard": []string{}, 444 | "touch": []string{}, 445 | "touchable": []string{}, 446 | "equal": []string{}, 447 | "equalable": []string{}, 448 | }, 449 | }, 450 | } 451 | 452 | actual := setFlagMessage(f) 453 | expected := " " 454 | 455 | if actual != expected { 456 | t.Errorf("got %v\nwant %v", actual, expected) 457 | } 458 | } 459 | 460 | //}}} 461 | 462 | // setArgument {{{ 463 | func TestSetArgumentIfStringFile(t *testing.T) { 464 | var f Flag = Flag{ 465 | Option: []string{}, 466 | Description: "", 467 | Exclusion: []string{}, 468 | Argument: Argument{ 469 | Group: "", 470 | Type: "file", 471 | Style: map[string][]string{ 472 | "standard": []string{}, 473 | "touch": []string{}, 474 | "touchable": []string{}, 475 | "equal": []string{}, 476 | "equalable": []string{}, 477 | }, 478 | }, 479 | } 480 | 481 | actual := setAction(f) 482 | expected := "_files" 483 | 484 | if actual != expected { 485 | t.Errorf("got %v\nwant %v", actual, expected) 486 | } 487 | } 488 | 489 | func TestSetArgumentIfStringDir(t *testing.T) { 490 | var f Flag = Flag{ 491 | Option: []string{}, 492 | Description: "", 493 | Exclusion: []string{}, 494 | Argument: Argument{ 495 | Group: "", 496 | Type: "dir", 497 | Style: map[string][]string{ 498 | "standard": []string{}, 499 | "touch": []string{}, 500 | "touchable": []string{}, 501 | "equal": []string{}, 502 | "equalable": []string{}, 503 | }, 504 | }, 505 | } 506 | 507 | actual := setAction(f) 508 | expected := "_files -/" 509 | 510 | if actual != expected { 511 | t.Errorf("got %v\nwant %v", actual, expected) 512 | } 513 | } 514 | 515 | func TestSetArgumentIfStringFuncIfOptionIsBlank(t *testing.T) { 516 | var f Flag = Flag{ 517 | Option: []string{}, 518 | Description: "", 519 | Exclusion: []string{}, 520 | Argument: Argument{ 521 | Group: "", 522 | Type: "func", 523 | Style: map[string][]string{ 524 | "standard": []string{}, 525 | "touch": []string{}, 526 | "touchable": []string{}, 527 | "equal": []string{}, 528 | "equalable": []string{}, 529 | }, 530 | }, 531 | } 532 | 533 | actual := setAction(f) 534 | expected := "func" 535 | 536 | if actual != expected { 537 | t.Errorf("got %v\nwant %v", actual, expected) 538 | } 539 | } 540 | 541 | func TestSetArgumentIfStringFuncIfFlag(t *testing.T) { 542 | var f Flag = Flag{ 543 | Option: []string{"--all"}, 544 | Description: "", 545 | Exclusion: []string{}, 546 | Argument: Argument{ 547 | Group: "", 548 | Type: "func", 549 | Style: map[string][]string{ 550 | "standard": []string{}, 551 | "touch": []string{}, 552 | "touchable": []string{}, 553 | "equal": []string{}, 554 | "equalable": []string{}, 555 | }, 556 | }, 557 | } 558 | 559 | actual := setAction(f) 560 | expected := "->all_func" 561 | 562 | if actual != expected { 563 | t.Errorf("got %v\nwant %v", actual, expected) 564 | } 565 | } 566 | 567 | func TestSetArgumentIfStringFuncIfNonFlag(t *testing.T) { 568 | var f Arguments = Arguments{ 569 | Always: false, 570 | Type: "func", 571 | } 572 | 573 | actual := setAction(f.Type) 574 | expected := "->args" 575 | 576 | if actual != expected { 577 | t.Errorf("got %v\nwant %v", actual, expected) 578 | } 579 | } 580 | 581 | func TestSetArgumentIfStringIsBlank(t *testing.T) { 582 | var f Flag = Flag{ 583 | Option: []string{}, 584 | Description: "", 585 | Exclusion: []string{}, 586 | Argument: Argument{ 587 | Group: "", 588 | Type: "", 589 | Style: map[string][]string{ 590 | "standard": []string{}, 591 | "touch": []string{}, 592 | "touchable": []string{}, 593 | "equal": []string{}, 594 | "equalable": []string{}, 595 | }, 596 | }, 597 | } 598 | 599 | actual := setAction(f) 600 | expected := " " 601 | 602 | if actual != expected { 603 | t.Errorf("got %v\nwant %v", actual, expected) 604 | } 605 | } 606 | 607 | func TestSetArgumentIfSlice(t *testing.T) { 608 | var f Flag = Flag{ 609 | Option: []string{}, 610 | Description: "", 611 | Exclusion: []string{}, 612 | Argument: Argument{ 613 | Group: "", 614 | Type: []string{"word1", "word2"}, 615 | Style: map[string][]string{ 616 | "standard": []string{}, 617 | "touch": []string{}, 618 | "touchable": []string{}, 619 | "equal": []string{}, 620 | "equalable": []string{}, 621 | }, 622 | }, 623 | } 624 | 625 | actual := setAction(f) 626 | expected := "(word1 word2)" 627 | 628 | if actual != expected { 629 | t.Errorf("got %v\nwant %v", actual, expected) 630 | } 631 | } 632 | 633 | func TestSetArgumentIfSliceInterface(t *testing.T) { 634 | var f Flag = Flag{ 635 | Option: []string{}, 636 | Description: "", 637 | Exclusion: []string{}, 638 | Argument: Argument{ 639 | Group: "", 640 | Type: []interface{}{"word1", "word2"}, 641 | Style: map[string][]string{ 642 | "standard": []string{}, 643 | "touch": []string{}, 644 | "touchable": []string{}, 645 | "equal": []string{}, 646 | "equalable": []string{}, 647 | }, 648 | }, 649 | } 650 | 651 | actual := setAction(f) 652 | expected := "(word1 word2)" 653 | 654 | if actual != expected { 655 | t.Errorf("got %v\nwant %v", actual, expected) 656 | } 657 | } 658 | 659 | func TestSetArgumentIfMap(t *testing.T) { 660 | var f Flag = Flag{ 661 | Option: []string{}, 662 | Description: "", 663 | Exclusion: []string{}, 664 | Argument: Argument{ 665 | Group: "", 666 | Type: map[string]string{"word1": "desc1", "word2": "desc2"}, 667 | Style: map[string][]string{ 668 | "standard": []string{}, 669 | "touch": []string{}, 670 | "touchable": []string{}, 671 | "equal": []string{}, 672 | "equalable": []string{}, 673 | }, 674 | }, 675 | } 676 | 677 | actual := setAction(f) 678 | expected1 := "((word1\\:\"desc1\" word2\\:\"desc2\"))" 679 | expected2 := "((word2\\:\"desc2\" word1\\:\"desc1\"))" 680 | 681 | if actual != expected1 && actual != expected2 { 682 | t.Errorf("got %v\nwant %v or %v", actual, expected1, expected2) 683 | } 684 | } 685 | 686 | func TestSetArgumentIfMapInterface(t *testing.T) { 687 | var f Flag = Flag{ 688 | Option: []string{}, 689 | Description: "", 690 | Exclusion: []string{}, 691 | Argument: Argument{ 692 | Group: "", 693 | Type: map[string]interface{}{"word1": "desc1", "word2": "desc2"}, 694 | Style: map[string][]string{ 695 | "standard": []string{}, 696 | "touch": []string{}, 697 | "touchable": []string{}, 698 | "equal": []string{}, 699 | "equalable": []string{}, 700 | }, 701 | }, 702 | } 703 | 704 | actual := setAction(f) 705 | expected1 := "((word1\\:\"desc1\" word2\\:\"desc2\"))" 706 | expected2 := "((word2\\:\"desc2\" word1\\:\"desc1\"))" 707 | 708 | if actual != expected1 && actual != expected2 { 709 | t.Errorf("got %v\nwant %v or %v", actual, expected1, expected2) 710 | } 711 | } 712 | 713 | func TestSetArgumentIfInvalid(t *testing.T) { 714 | var f Flag = Flag{ 715 | Option: []string{}, 716 | Description: "", 717 | Exclusion: []string{}, 718 | Argument: Argument{ 719 | Group: "", 720 | Type: 1, 721 | Style: map[string][]string{ 722 | "standard": []string{}, 723 | "touch": []string{}, 724 | "touchable": []string{}, 725 | "equal": []string{}, 726 | "equalable": []string{}, 727 | }, 728 | }, 729 | } 730 | 731 | actual := setAction(f) 732 | expected := "[[Parse Error]]" 733 | 734 | if actual != expected { 735 | t.Errorf("got %v\nwant %v", actual, expected) 736 | } 737 | } 738 | 739 | //}}} 740 | 741 | // helperTrimArrowInType {{{ 742 | func TestHelperTrimArrowInType(t *testing.T) { 743 | actual := helperTrimArrowInType("->hello") 744 | expected := "hello" 745 | 746 | if actual != expected { 747 | t.Errorf("got %v\nwant %v", actual, expected) 748 | } 749 | } 750 | 751 | func TestHelperTrimArrowInTypeReturn(t *testing.T) { 752 | actual := helperTrimArrowInType("he->llo") 753 | expected := "he->llo" 754 | 755 | if actual != expected { 756 | t.Errorf("got %v\nwant %v", actual, expected) 757 | } 758 | } 759 | 760 | //}}} 761 | 762 | // helperAddFlagArgumentStyle {{{ 763 | func TestHelperAddFlagArgumentStyleIfStandard(t *testing.T) { 764 | m := map[string][]string{ 765 | "standard": []string{"-opt"}, 766 | "touch": []string{}, 767 | "touchable": []string{}, 768 | "equal": []string{}, 769 | "equalable": []string{}, 770 | } 771 | 772 | actual := helperAddFlagArgumentStyle(m, "-opt") 773 | expected := "-opt" 774 | 775 | if actual != expected { 776 | t.Errorf("got %v\nwant %v", actual, expected) 777 | } 778 | } 779 | 780 | func TestHelperAddFlagArgumentStyleIfTouch(t *testing.T) { 781 | m := map[string][]string{ 782 | "standard": []string{}, 783 | "touch": []string{"-opt"}, 784 | "touchable": []string{}, 785 | "equal": []string{}, 786 | "equalable": []string{}, 787 | } 788 | 789 | actual := helperAddFlagArgumentStyle(m, "-opt") 790 | expected := "-opt-" 791 | 792 | if actual != expected { 793 | t.Errorf("got %v\nwant %v", actual, expected) 794 | } 795 | } 796 | 797 | func TestHelperAddFlagArgumentStyleIfTouchable(t *testing.T) { 798 | m := map[string][]string{ 799 | "standard": []string{}, 800 | "touch": []string{}, 801 | "touchable": []string{"-opt"}, 802 | "equal": []string{}, 803 | "equalable": []string{}, 804 | } 805 | 806 | actual := helperAddFlagArgumentStyle(m, "-opt") 807 | expected := "-opt+" 808 | 809 | if actual != expected { 810 | t.Errorf("got %v\nwant %v", actual, expected) 811 | } 812 | } 813 | 814 | func TestHelperAddFlagArgumentStyleIfEqual(t *testing.T) { 815 | m := map[string][]string{ 816 | "standard": []string{}, 817 | "touch": []string{}, 818 | "touchable": []string{}, 819 | "equal": []string{"-opt"}, 820 | "equalable": []string{}, 821 | } 822 | 823 | actual := helperAddFlagArgumentStyle(m, "-opt") 824 | expected := "-opt=-" 825 | 826 | if actual != expected { 827 | t.Errorf("got %v\nwant %v", actual, expected) 828 | } 829 | } 830 | 831 | func TestHelperAddFlagArgumentStyleIfEqualable(t *testing.T) { 832 | m := map[string][]string{ 833 | "standard": []string{}, 834 | "touch": []string{}, 835 | "touchable": []string{}, 836 | "equal": []string{}, 837 | "equalable": []string{"-opt"}, 838 | } 839 | 840 | actual := helperAddFlagArgumentStyle(m, "-opt") 841 | expected := "-opt=" 842 | 843 | if actual != expected { 844 | t.Errorf("got %v\nwant %v", actual, expected) 845 | } 846 | } 847 | 848 | func TestHelperAddFlagArgumentStyleReturnAsItIs(t *testing.T) { 849 | m := map[string][]string{ 850 | "false": []string{""}, 851 | } 852 | 853 | actual := helperAddFlagArgumentStyle(m, "--true") 854 | expected := "--true" 855 | 856 | if actual != expected { 857 | t.Errorf("got %v\nwant %v", actual, expected) 858 | } 859 | } 860 | 861 | //func TestHelperAddFlagArgumentStyleParseErrorReturnBlank(t *testing.T) { 862 | // m := map[string][]string{ 863 | // "standard": []string{""}, 864 | // } 865 | // 866 | // actual := helperAddFlagArgumentStyle(m, "-false") 867 | // expected := "" 868 | // 869 | // if actual != expected { 870 | // t.Errorf("got %v\nwant %v", actual, expected) 871 | // } 872 | //} 873 | 874 | func TestHelperAddFlagArgumentStyleGetRidOf(t *testing.T) { 875 | m := map[string][]string{ 876 | "standard": []string{}, 877 | "touch": []string{"--true"}, 878 | "touchable": []string{}, 879 | "equal": []string{"--false"}, 880 | "equalable": []string{}, 881 | } 882 | 883 | actual := helperAddFlagArgumentStyle(m, "--true") 884 | expected := "--true-" 885 | 886 | if actual != expected { 887 | t.Errorf("got %v\nwant %v", actual, expected) 888 | } 889 | } 890 | 891 | //}}} 892 | 893 | // vim: fdm=marker fdc=3 894 | --------------------------------------------------------------------------------