Commit 973558eee274f9868cf4921aa9745c2f4934aaf2

Authored by Shrikant Sharat
1 parent cffa56ed75

Support positional arguments as keyword args.

The positional arguments can also be specified as keyword arguments.

Showing 2 changed files with 9 additions and 1 deletions Inline Diff

1 #!/bin/zsh 1 #!/bin/zsh
2 2
3 # Each line in this string has the following entries separated by a space 3 # Each line in this string has the following entries separated by a space
4 # character. 4 # character.
5 # <repo-url>, <plugin-location>, <bundle-type>, <has-local-clone> 5 # <repo-url>, <plugin-location>, <bundle-type>, <has-local-clone>
6 # FIXME: Is not kept local by zsh! 6 # FIXME: Is not kept local by zsh!
7 local _ANTIGEN_BUNDLE_RECORD="" 7 local _ANTIGEN_BUNDLE_RECORD=""
8 8
9 # Syntaxes 9 # Syntaxes
10 # antigen-bundle <url> [<loc>=/] 10 # antigen-bundle <url> [<loc>=/]
11 # Keyword only arguments: 11 # Keyword only arguments:
12 # branch - The branch of the repo to use for this bundle. 12 # branch - The branch of the repo to use for this bundle.
13 antigen-bundle () { 13 antigen-bundle () {
14 14
15 # Bundle spec arguments' default values. 15 # Bundle spec arguments' default values.
16 local url="$ANTIGEN_DEFAULT_REPO_URL" 16 local url="$ANTIGEN_DEFAULT_REPO_URL"
17 local loc=/ 17 local loc=/
18 local branch= 18 local branch=
19 local no_local_clone=false 19 local no_local_clone=false
20 local btype=plugin 20 local btype=plugin
21 21
22 # Set spec values based on the positional arguments. 22 # Set spec values based on the positional arguments.
23 local position_args 23 local position_args
24 position_args=(url loc) 24 position_args=(url loc)
25 local i=1 25 local i=1
26 while ! [[ -z $1 || $1 == --* ]]; do 26 while ! [[ -z $1 || $1 == --* ]]; do
27 local arg_name="${position_args[$i]}" 27 local arg_name="${position_args[$i]}"
28 local arg_value="$1" 28 local arg_value="$1"
29 eval "local $arg_name='$arg_value'" 29 eval "local $arg_name='$arg_value'"
30 shift 30 shift
31 i=$(($i + 1)) 31 i=$(($i + 1))
32 done 32 done
33 33
34 # Set spec values from keyword arguments, if any. The remaining arguments 34 # Set spec values from keyword arguments, if any. The remaining arguments
35 # are all assumed to be keyword arguments. 35 # are all assumed to be keyword arguments.
36 while [[ $1 == --* ]]; do 36 while [[ $1 == --* ]]; do
37 # Remove the `--` at the start. 37 # Remove the `--` at the start.
38 local arg="${1#--}" 38 local arg="${1#--}"
39 39
40 if [[ $arg != *=* ]]; then 40 if [[ $arg != *=* ]]; then
41 arg="$arg=true" 41 arg="$arg=true"
42 fi 42 fi
43 43
44 # Get the name of the arg and replace the `-`'s to `_`'s. 44 # Get the name of the arg and replace the `-`'s to `_`'s.
45 local arg_name="${${arg%\=*}//-/_}" 45 local arg_name="${${arg%\=*}//-/_}"
46 46
47 # Get the value of the arg. 47 # Get the value of the arg.
48 local arg_value="${arg#*\=}" 48 local arg_value="${arg#*\=}"
49 49
50 eval "local $arg_name='$arg_value'" 50 eval "local $arg_name='$arg_value'"
51 shift 51 shift
52 done 52 done
53 53
54 # Check if url is just the plugin name. Super short syntax. 54 # Check if url is just the plugin name. Super short syntax.
55 if [[ "$url" != */* ]]; then 55 if [[ "$url" != */* ]]; then
56 loc="plugins/$url" 56 loc="plugins/$url"
57 url="$ANTIGEN_DEFAULT_REPO_URL" 57 url="$ANTIGEN_DEFAULT_REPO_URL"
58 fi 58 fi
59 59
60 # Resolve the url. 60 # Resolve the url.
61 url="$(-antigen-resolve-bundle-url "$url")" 61 url="$(-antigen-resolve-bundle-url "$url")"
62 62
63 # Add the branch information to the url. 63 # Add the branch information to the url.
64 if [[ ! -z $branch ]]; then 64 if [[ ! -z $branch ]]; then
65 url="$url|$branch" 65 url="$url|$branch"
66 fi 66 fi
67 67
68 # The `make_local_clone` variable better represents whether there should be 68 # The `make_local_clone` variable better represents whether there should be
69 # a local clone made. For cloning to be avoided, firstly, the `$url` should 69 # a local clone made. For cloning to be avoided, firstly, the `$url` should
70 # be an absolute local path and `$branch` should be empty. In addition to 70 # be an absolute local path and `$branch` should be empty. In addition to
71 # these two conditions, either the `--no-local-clone` option should be 71 # these two conditions, either the `--no-local-clone` option should be
72 # given, or `$url` should not a git repo. 72 # given, or `$url` should not a git repo.
73 local make_local_clone=true 73 local make_local_clone=true
74 if [[ $url == /* && -z $branch && 74 if [[ $url == /* && -z $branch &&
75 ( $no_local_clone == true || ! -d $url/.git ) ]]; then 75 ( $no_local_clone == true || ! -d $url/.git ) ]]; then
76 make_local_clone=false 76 make_local_clone=false
77 fi 77 fi
78 78
79 # Add it to the record. 79 # Add it to the record.
80 _ANTIGEN_BUNDLE_RECORD="$_ANTIGEN_BUNDLE_RECORD\n$url $loc $btype" 80 _ANTIGEN_BUNDLE_RECORD="$_ANTIGEN_BUNDLE_RECORD\n$url $loc $btype"
81 _ANTIGEN_BUNDLE_RECORD="$_ANTIGEN_BUNDLE_RECORD $make_local_clone" 81 _ANTIGEN_BUNDLE_RECORD="$_ANTIGEN_BUNDLE_RECORD $make_local_clone"
82 82
83 # Ensure a clone exists for this repo, if needed. 83 # Ensure a clone exists for this repo, if needed.
84 if $make_local_clone; then 84 if $make_local_clone; then
85 -antigen-ensure-repo "$url" 85 -antigen-ensure-repo "$url"
86 fi 86 fi
87 87
88 # Load the plugin. 88 # Load the plugin.
89 -antigen-load "$url" "$loc" "$btype" "$make_local_clone" 89 -antigen-load "$url" "$loc" "$btype" "$make_local_clone"
90 90
91 } 91 }
92 92
93 -antigen-resolve-bundle-url () { 93 -antigen-resolve-bundle-url () {
94 # Given an acceptable short/full form of a bundle's repo url, this function 94 # Given an acceptable short/full form of a bundle's repo url, this function
95 # echoes the full form of the repo's clone url. 95 # echoes the full form of the repo's clone url.
96 96
97 local url="$1" 97 local url="$1"
98 98
99 # Expand short github url syntax: `username/reponame`. 99 # Expand short github url syntax: `username/reponame`.
100 if [[ $url != git://* && 100 if [[ $url != git://* &&
101 $url != https://* && 101 $url != https://* &&
102 $url != /* && 102 $url != /* &&
103 $url != git@github.com:*/* 103 $url != git@github.com:*/*
104 ]]; then 104 ]]; then
105 url="https://github.com/${url%.git}.git" 105 url="https://github.com/${url%.git}.git"
106 fi 106 fi
107 107
108 echo "$url" 108 echo "$url"
109 } 109 }
110 110
111 antigen-bundles () { 111 antigen-bundles () {
112 # Bulk add many bundles at one go. Empty lines and lines starting with a `#` 112 # Bulk add many bundles at one go. Empty lines and lines starting with a `#`
113 # are ignored. Everything else is given to `antigen-bundle` as is, no 113 # are ignored. Everything else is given to `antigen-bundle` as is, no
114 # quoting rules applied. 114 # quoting rules applied.
115 115
116 local line 116 local line
117 117
118 grep -v '^\s*$\|^#' | while read line; do 118 grep -v '^\s*$\|^#' | while read line; do
119 # Using `eval` so that we can use the shell-style quoting in each line 119 # Using `eval` so that we can use the shell-style quoting in each line
120 # piped to `antigen-bundles`. 120 # piped to `antigen-bundles`.
121 eval "antigen-bundle $line" 121 eval "antigen-bundle $line"
122 done 122 done
123 } 123 }
124 124
125 antigen-update () { 125 antigen-update () {
126 # Update your bundles, i.e., `git pull` in all the plugin repos. 126 # Update your bundles, i.e., `git pull` in all the plugin repos.
127 -antigen-echo-record | 127 -antigen-echo-record |
128 awk '{print $1}' | 128 awk '{print $1}' |
129 sort -u | 129 sort -u |
130 while read url; do 130 while read url; do
131 echo "**** Pulling $url" 131 echo "**** Pulling $url"
132 -antigen-ensure-repo --update --verbose "$url" 132 -antigen-ensure-repo --update --verbose "$url"
133 echo 133 echo
134 done 134 done
135 } 135 }
136 136
137 -antigen-get-clone-dir () { 137 -antigen-get-clone-dir () {
138 # Takes a repo url and gives out the path that this url needs to be cloned 138 # Takes a repo url and gives out the path that this url needs to be cloned
139 # to. Doesn't actually clone anything. 139 # to. Doesn't actually clone anything.
140 # TODO: Memoize? 140 # TODO: Memoize?
141 141
142 # The url given. 142 # The url given.
143 local url="$1" 143 local url="$1"
144 144
145 # Echo the full path to the clone directory. 145 # Echo the full path to the clone directory.
146 echo -n $ADOTDIR/repos/ 146 echo -n $ADOTDIR/repos/
147 echo "$url" | sed \ 147 echo "$url" | sed \
148 -e 's./.-SLASH-.g' \ 148 -e 's./.-SLASH-.g' \
149 -e 's.:.-COLON-.g' \ 149 -e 's.:.-COLON-.g' \
150 -e 's.|.-PIPE-.g' 150 -e 's.|.-PIPE-.g'
151 } 151 }
152 152
153 -antigen-get-clone-url () { 153 -antigen-get-clone-url () {
154 # Takes a repo's clone dir and gives out the repo's original url that was 154 # Takes a repo's clone dir and gives out the repo's original url that was
155 # used to create the given directory path. 155 # used to create the given directory path.
156 # TODO: Memoize? 156 # TODO: Memoize?
157 echo "$1" | sed \ 157 echo "$1" | sed \
158 -e "s:^$ADOTDIR/repos/::" \ 158 -e "s:^$ADOTDIR/repos/::" \
159 -e 's.-SLASH-./.g' \ 159 -e 's.-SLASH-./.g' \
160 -e 's.-COLON-.:.g' \ 160 -e 's.-COLON-.:.g' \
161 -e 's.-PIPE-.|.g' 161 -e 's.-PIPE-.|.g'
162 } 162 }
163 163
164 -antigen-ensure-repo () { 164 -antigen-ensure-repo () {
165 165
166 # Ensure that a clone exists for the given repo url and branch. If the first 166 # Ensure that a clone exists for the given repo url and branch. If the first
167 # argument is `--update` and if a clone already exists for the given repo 167 # argument is `--update` and if a clone already exists for the given repo
168 # and branch, it is pull-ed, i.e., updated. 168 # and branch, it is pull-ed, i.e., updated.
169 169
170 # Argument defaults. 170 # Argument defaults.
171 # Check if we have to update. 171 # Check if we have to update.
172 local update=false 172 local update=false
173 # Verbose output. 173 # Verbose output.
174 local verbose=false 174 local verbose=false
175 175
176 # Load any boolean arguments specified. 176 # Load any boolean arguments specified.
177 while [[ $1 == --* ]]; do 177 while [[ $1 == --* ]]; do
178 eval "local '${1#--}=true'" 178 eval "local '${1#--}=true'"
179 shift 179 shift
180 done 180 done
181 181
182 # Get the clone's directory as per the given repo url and branch. 182 # Get the clone's directory as per the given repo url and branch.
183 local url="$1" 183 local url="$1"
184 local clone_dir="$(-antigen-get-clone-dir $url)" 184 local clone_dir="$(-antigen-get-clone-dir $url)"
185 185
186 # A temporary function wrapping the `git` command with repeated arguments. 186 # A temporary function wrapping the `git` command with repeated arguments.
187 --plugin-git () { 187 --plugin-git () {
188 eval git --git-dir=$clone_dir/.git --work-tree=$clone_dir "$@" 188 eval git --git-dir=$clone_dir/.git --work-tree=$clone_dir "$@"
189 } 189 }
190 190
191 # Clone if it doesn't already exist. 191 # Clone if it doesn't already exist.
192 if [[ ! -d $clone_dir ]]; then 192 if [[ ! -d $clone_dir ]]; then
193 git clone "${url%|*}" "$clone_dir" 193 git clone "${url%|*}" "$clone_dir"
194 elif $update; then 194 elif $update; then
195 # Save current revision. 195 # Save current revision.
196 local old_rev="$(--plugin-git rev-parse HEAD)" 196 local old_rev="$(--plugin-git rev-parse HEAD)"
197 # Pull changes if update requested. 197 # Pull changes if update requested.
198 --plugin-git pull 198 --plugin-git pull
199 # Get the new revision. 199 # Get the new revision.
200 local new_rev="$(--plugin-git rev-parse HEAD)" 200 local new_rev="$(--plugin-git rev-parse HEAD)"
201 fi 201 fi
202 202
203 # If its a specific branch that we want, checkout that branch. 203 # If its a specific branch that we want, checkout that branch.
204 if [[ $url == *\|* ]]; then 204 if [[ $url == *\|* ]]; then
205 local current_branch=${$(--plugin-git symbolic-ref HEAD)##refs/heads/} 205 local current_branch=${$(--plugin-git symbolic-ref HEAD)##refs/heads/}
206 local requested_branch="${url#*|}" 206 local requested_branch="${url#*|}"
207 # Only do the checkout when we are not already on the branch. 207 # Only do the checkout when we are not already on the branch.
208 [[ $requested_branch != $current_branch ]] && 208 [[ $requested_branch != $current_branch ]] &&
209 --plugin-git checkout $requested_branch 209 --plugin-git checkout $requested_branch
210 fi 210 fi
211 211
212 if ! [[ -z $old_rev || $old_rev == $new_rev ]]; then 212 if ! [[ -z $old_rev || $old_rev == $new_rev ]]; then
213 echo Updated from ${old_rev:0:7} to ${new_rev:0:7}. 213 echo Updated from ${old_rev:0:7} to ${new_rev:0:7}.
214 if $verbose; then 214 if $verbose; then
215 --plugin-git log --oneline --reverse --no-merges --stat '@{1}..' 215 --plugin-git log --oneline --reverse --no-merges --stat '@{1}..'
216 fi 216 fi
217 fi 217 fi
218 218
219 # Remove the temporary git wrapper function. 219 # Remove the temporary git wrapper function.
220 unfunction -- --plugin-git 220 unfunction -- --plugin-git
221 221
222 } 222 }
223 223
224 -antigen-load () { 224 -antigen-load () {
225 225
226 local url="$1" 226 local url="$1"
227 local loc="$2" 227 local loc="$2"
228 local btype="$3" 228 local btype="$3"
229 local make_local_clone="$4" 229 local make_local_clone="$4"
230 230
231 # The full location where the plugin is located. 231 # The full location where the plugin is located.
232 local location 232 local location
233 if $make_local_clone; then 233 if $make_local_clone; then
234 location="$(-antigen-get-clone-dir "$url")/$loc" 234 location="$(-antigen-get-clone-dir "$url")/$loc"
235 else 235 else
236 location="$url" 236 location="$url"
237 fi 237 fi
238 238
239 if [[ $btype == theme ]]; then 239 if [[ $btype == theme ]]; then
240 240
241 # Of course, if its a theme, the location would point to the script 241 # Of course, if its a theme, the location would point to the script
242 # file. 242 # file.
243 source "$location" 243 source "$location"
244 244
245 else 245 else
246 246
247 # Source the plugin script. 247 # Source the plugin script.
248 # FIXME: I don't know. Looks very very ugly. Needs a better 248 # FIXME: I don't know. Looks very very ugly. Needs a better
249 # implementation once tests are ready. 249 # implementation once tests are ready.
250 local script_loc="$(ls "$location" | grep -m1 '\.plugin\.zsh$')" 250 local script_loc="$(ls "$location" | grep -m1 '\.plugin\.zsh$')"
251 251
252 if [[ -f $script_loc ]]; then 252 if [[ -f $script_loc ]]; then
253 # If we have a `*.plugin.zsh`, source it. 253 # If we have a `*.plugin.zsh`, source it.
254 source "$script_loc" 254 source "$script_loc"
255 255
256 elif [[ ! -z "$(ls "$location" | grep -m1 '\.zsh$')" ]]; then 256 elif [[ ! -z "$(ls "$location" | grep -m1 '\.zsh$')" ]]; then
257 # If there is no `*.plugin.zsh` file, source *all* the `*.zsh` 257 # If there is no `*.plugin.zsh` file, source *all* the `*.zsh`
258 # files. 258 # files.
259 for script ($location/*.zsh(N)) source "$script" 259 for script ($location/*.zsh(N)) source "$script"
260 260
261 elif [[ ! -z "$(ls "$location" | grep -m1 '\.sh$')" ]]; then 261 elif [[ ! -z "$(ls "$location" | grep -m1 '\.sh$')" ]]; then
262 # If there are no `*.zsh` files either, we look for and source any 262 # If there are no `*.zsh` files either, we look for and source any
263 # `*.sh` files instead. 263 # `*.sh` files instead.
264 for script ($location/*.sh(N)) source "$script" 264 for script ($location/*.sh(N)) source "$script"
265 265
266 fi 266 fi
267 267
268 # Add to $fpath, for completion(s). 268 # Add to $fpath, for completion(s).
269 fpath=($location $fpath) 269 fpath=($location $fpath)
270 270
271 fi 271 fi
272 272
273 } 273 }
274 274
275 antigen-cleanup () { 275 antigen-cleanup () {
276 276
277 # Cleanup unused repositories. 277 # Cleanup unused repositories.
278 278
279 local force=false 279 local force=false
280 if [[ $1 == --force ]]; then 280 if [[ $1 == --force ]]; then
281 force=true 281 force=true
282 fi 282 fi
283 283
284 if [[ ! -d "$ADOTDIR/repos" || -z "$(ls "$ADOTDIR/repos/")" ]]; then 284 if [[ ! -d "$ADOTDIR/repos" || -z "$(ls "$ADOTDIR/repos/")" ]]; then
285 echo "You don't have any bundles." 285 echo "You don't have any bundles."
286 return 0 286 return 0
287 fi 287 fi
288 288
289 # Find directores in ADOTDIR/repos, that are not in the bundles record. 289 # Find directores in ADOTDIR/repos, that are not in the bundles record.
290 local unused_clones="$(comm -13 \ 290 local unused_clones="$(comm -13 \
291 <(-antigen-echo-record | 291 <(-antigen-echo-record |
292 awk '$4 == "true" {print $1}' | 292 awk '$4 == "true" {print $1}' |
293 while read line; do 293 while read line; do
294 -antigen-get-clone-dir "$line" 294 -antigen-get-clone-dir "$line"
295 done | 295 done |
296 sort -u) \ 296 sort -u) \
297 <(ls -d "$ADOTDIR/repos/"* | sort -u))" 297 <(ls -d "$ADOTDIR/repos/"* | sort -u))"
298 298
299 if [[ -z $unused_clones ]]; then 299 if [[ -z $unused_clones ]]; then
300 echo "You don't have any unidentified bundles." 300 echo "You don't have any unidentified bundles."
301 return 0 301 return 0
302 fi 302 fi
303 303
304 echo 'You have clones for the following repos, but are not used.' 304 echo 'You have clones for the following repos, but are not used.'
305 echo "$unused_clones" | 305 echo "$unused_clones" |
306 while read line; do 306 while read line; do
307 -antigen-get-clone-url "$line" 307 -antigen-get-clone-url "$line"
308 done | 308 done |
309 sed -e 's/^/ /' -e 's/|/, branch /' 309 sed -e 's/^/ /' -e 's/|/, branch /'
310 310
311 if $force || (echo -n '\nDelete them all? [y/N] '; read -q); then 311 if $force || (echo -n '\nDelete them all? [y/N] '; read -q); then
312 echo 312 echo
313 echo 313 echo
314 echo "$unused_clones" | while read line; do 314 echo "$unused_clones" | while read line; do
315 echo -n "Deleting clone for $(-antigen-get-clone-url "$line")..." 315 echo -n "Deleting clone for $(-antigen-get-clone-url "$line")..."
316 rm -rf "$line" 316 rm -rf "$line"
317 echo ' done.' 317 echo ' done.'
318 done 318 done
319 else 319 else
320 echo 320 echo
321 echo Nothing deleted. 321 echo Nothing deleted.
322 fi 322 fi
323 } 323 }
324 324
325 antigen-lib () { 325 antigen-lib () {
326 antigen-bundle --loc=lib 326 antigen-bundle --loc=lib
327 } 327 }
328 328
329 antigen-theme () { 329 antigen-theme () {
330 local name="${1:-robbyrussell}" 330 local name="${1:-robbyrussell}"
331 antigen-bundle --loc=themes/$name.zsh-theme --btype=theme 331 antigen-bundle --loc=themes/$name.zsh-theme --btype=theme
332 } 332 }
333 333
334 antigen-apply () { 334 antigen-apply () {
335 # Initialize completion. 335 # Initialize completion.
336 # TODO: Only load completions if there are any changes to the bundle 336 # TODO: Only load completions if there are any changes to the bundle
337 # repositories. 337 # repositories.
338 compinit -i 338 compinit -i
339 } 339 }
340 340
341 antigen-list () { 341 antigen-list () {
342 # List all currently installed bundles. 342 # List all currently installed bundles.
343 if [[ -z "$_ANTIGEN_BUNDLE_RECORD" ]]; then 343 if [[ -z "$_ANTIGEN_BUNDLE_RECORD" ]]; then
344 echo "You don't have any bundles." >&2 344 echo "You don't have any bundles." >&2
345 return 1 345 return 1
346 else 346 else
347 -antigen-echo-record | sort -u 347 -antigen-echo-record | sort -u
348 fi 348 fi
349 } 349 }
350 350
351 antigen-help () { 351 antigen-help () {
352 cat <<EOF 352 cat <<EOF
353 Antigen is a plugin management system for zsh. It makes it easy to grab awesome 353 Antigen is a plugin management system for zsh. It makes it easy to grab awesome
354 shell scripts and utilities, put up on github. For further details and complete 354 shell scripts and utilities, put up on github. For further details and complete
355 documentation, visit the project's page at 'http://antigen.sharats.me'. 355 documentation, visit the project's page at 'http://antigen.sharats.me'.
356 EOF 356 EOF
357 } 357 }
358 358
359 # A syntax sugar to avoid the `-` when calling antigen commands. With this 359 # A syntax sugar to avoid the `-` when calling antigen commands. With this
360 # function, you can write `antigen-bundle` as `antigen bundle` and so on. 360 # function, you can write `antigen-bundle` as `antigen bundle` and so on.
361 antigen () { 361 antigen () {
362 local cmd="$1" 362 local cmd="$1"
363 shift 363 shift
364 "antigen-$cmd" "$@" 364 "antigen-$cmd" "$@"
365 } 365 }
366 366
367 -antigen-parse-args () { 367 -antigen-parse-args () {
368 # An argument parsing functionality to parse arguments the *antigen* way :). 368 # An argument parsing functionality to parse arguments the *antigen* way :).
369 # Takes one first argument (called spec), which dictates how to parse and 369 # Takes one first argument (called spec), which dictates how to parse and
370 # the rest of the arguments are parsed. Outputs a piece of valid shell code 370 # the rest of the arguments are parsed. Outputs a piece of valid shell code
371 # that can be passed to `eval` inside a function which creates the arguments 371 # that can be passed to `eval` inside a function which creates the arguments
372 # and their values as local variables. Suggested use is to set the defaults 372 # and their values as local variables. Suggested use is to set the defaults
373 # to all arguments first and then eval the output of this function. 373 # to all arguments first and then eval the output of this function.
374 374
375 # Spec: Only long argument supported. No support for parsing short options. 375 # Spec: Only long argument supported. No support for parsing short options.
376 # The spec must have two sections, separated by a `;`. 376 # The spec must have two sections, separated by a `;`.
377 # '<positional-arguments>;<keyword-only-arguments>' 377 # '<positional-arguments>;<keyword-only-arguments>'
378 # Positional arguments are passed as just values, like `command a b`. 378 # Positional arguments are passed as just values, like `command a b`.
379 # Keyword arguments are passed as a `--name=value` pair, like `command 379 # Keyword arguments are passed as a `--name=value` pair, like `command
380 # --arg1=a --arg2=b`. 380 # --arg1=a --arg2=b`.
381 381
382 # Each argument in the spec is separated by a `,`. Each keyword argument can 382 # Each argument in the spec is separated by a `,`. Each keyword argument can
383 # end in a `:` to specifiy that this argument wants a value, otherwise it 383 # end in a `:` to specifiy that this argument wants a value, otherwise it
384 # doesn't take a value. (The value in the output when the keyword argument 384 # doesn't take a value. (The value in the output when the keyword argument
385 # doesn't have a `:` is `true`). 385 # doesn't have a `:` is `true`).
386 386
387 # Arguments in either section can end with a `?` (should come after `:`, if 387 # Arguments in either section can end with a `?` (should come after `:`, if
388 # both are present), means optional. FIXME: Not yet implemented. 388 # both are present), means optional. FIXME: Not yet implemented.
389 389
390 # See the test file, tests/arg-parser.t for (working) examples. 390 # See the test file, tests/arg-parser.t for (working) examples.
391 391
392 local spec="$1" 392 local spec="$1"
393 shift 393 shift
394 394
395 local code='' 395 local code=''
396 396
397 --add-var () { 397 --add-var () {
398 test -z "$code" || code="$code\n" 398 test -z "$code" || code="$code\n"
399 code="${code}local $1='$2'" 399 code="${code}local $1='$2'"
400 } 400 }
401 401
402 local positional_args="$(echo "$spec" | cut -d\; -f1)" 402 local positional_args="$(echo "$spec" | cut -d\; -f1)"
403 local positional_args_count="$(echo $positional_args | 403 local positional_args_count="$(echo $positional_args |
404 awk -F, '{print NF}')" 404 awk -F, '{print NF}')"
405 405
406 # Set spec values based on the positional arguments. 406 # Set spec values based on the positional arguments.
407 local i=1 407 local i=1
408 while ! [[ -z $1 || $1 == --* ]]; do 408 while ! [[ -z $1 || $1 == --* ]]; do
409 409
410 if (( $i > $positional_args_count )); then 410 if (( $i > $positional_args_count )); then
411 echo "Only $positional_args_count positional arguments allowed." >&2 411 echo "Only $positional_args_count positional arguments allowed." >&2
412 echo "Found at least one more: '$1'" >&2 412 echo "Found at least one more: '$1'" >&2
413 return 413 return
414 fi 414 fi
415 415
416 local name_spec="$(echo "$positional_args" | cut -d, -f$i)" 416 local name_spec="$(echo "$positional_args" | cut -d, -f$i)"
417 local name="${${name_spec%\?}%:}" 417 local name="${${name_spec%\?}%:}"
418 local value="$1" 418 local value="$1"
419 419
420 --add-var $name "$value" 420 --add-var $name "$value"
421 421
422 shift 422 shift
423 i=$(($i + 1)) 423 i=$(($i + 1))
424 done 424 done
425 425
426 local keyword_args="$(echo "$spec" | cut -d\; -f2 | tr , '\n')" 426 local keyword_args="$(
427 echo "$positional_args" | tr , '\n' | sed -r 's/(\??)$/:\1/'
428 echo "$spec" | cut -d\; -f2 | tr , '\n'
429 )"
427 local keyword_args_count="$(echo $keyword_args | awk -F, '{print NF}')" 430 local keyword_args_count="$(echo $keyword_args | awk -F, '{print NF}')"
428 431
429 # Set spec values from keyword arguments, if any. The remaining arguments 432 # Set spec values from keyword arguments, if any. The remaining arguments
430 # are all assumed to be keyword arguments. 433 # are all assumed to be keyword arguments.
431 while [[ $1 == --* ]]; do 434 while [[ $1 == --* ]]; do
432 # Remove the `--` at the start. 435 # Remove the `--` at the start.
433 local arg="${1#--}" 436 local arg="${1#--}"
434 437
435 # Get the argument name and value. 438 # Get the argument name and value.
436 if [[ $arg != *=* ]]; then 439 if [[ $arg != *=* ]]; then
437 local name="$arg" 440 local name="$arg"
438 local value='' 441 local value=''
439 else 442 else
440 local name="${arg%\=*}" 443 local name="${arg%\=*}"
441 local value="${arg#*=}" 444 local value="${arg#*=}"
442 fi 445 fi
443 446
444 # The specification for this argument, used for validations. 447 # The specification for this argument, used for validations.
445 local arg_line="$(echo "$keyword_args" | grep -m1 "^$name:\??\?")" 448 local arg_line="$(echo "$keyword_args" | grep -m1 "^$name:\??\?")"
446 449
447 # Validate argument and value. 450 # Validate argument and value.
448 if [[ -z $arg_line ]]; then 451 if [[ -z $arg_line ]]; then
449 # This argument is not known to us. 452 # This argument is not known to us.
450 echo "Unknown argument '$name'." >&2 453 echo "Unknown argument '$name'." >&2
451 return 454 return
452 455
453 elif (echo "$arg_line" | grep -qm1 ':') && [[ -z $value ]]; then 456 elif (echo "$arg_line" | grep -qm1 ':') && [[ -z $value ]]; then
454 # This argument needs a value, but is not provided. 457 # This argument needs a value, but is not provided.
455 echo "Required argument for '$name' not provided." >&2 458 echo "Required argument for '$name' not provided." >&2
456 return 459 return
457 460
458 elif (echo "$arg_line" | grep -vqm1 ':') && [[ ! -z $value ]]; then 461 elif (echo "$arg_line" | grep -vqm1 ':') && [[ ! -z $value ]]; then
459 # This argument doesn't need a value, but is provided. 462 # This argument doesn't need a value, but is provided.
460 echo "No argument required for '$name', but provided '$value'." >&2 463 echo "No argument required for '$name', but provided '$value'." >&2
461 return 464 return
462 465
463 fi 466 fi
464 467
465 if [[ -z $value ]]; then 468 if [[ -z $value ]]; then
466 value=true 469 value=true
467 fi 470 fi
468 471
469 --add-var "${name//-/_}" "$value" 472 --add-var "${name//-/_}" "$value"
470 shift 473 shift
471 done 474 done
472 475
473 echo "$code" 476 echo "$code"
474 477
475 unfunction -- --add-var 478 unfunction -- --add-var
476 479
477 } 480 }
478 481
479 # Echo the bundle specs as in the record. The first line is not echoed since it 482 # Echo the bundle specs as in the record. The first line is not echoed since it
480 # is a blank line. 483 # is a blank line.
481 -antigen-echo-record () { 484 -antigen-echo-record () {
482 echo "$_ANTIGEN_BUNDLE_RECORD" | sed -n '1!p' 485 echo "$_ANTIGEN_BUNDLE_RECORD" | sed -n '1!p'
483 } 486 }
484 487
485 -antigen-env-setup () { 488 -antigen-env-setup () {
486 # Pre-startup initializations. 489 # Pre-startup initializations.
487 -set-default ANTIGEN_DEFAULT_REPO_URL \ 490 -set-default ANTIGEN_DEFAULT_REPO_URL \
488 https://github.com/robbyrussell/oh-my-zsh.git 491 https://github.com/robbyrussell/oh-my-zsh.git
489 -set-default ADOTDIR $HOME/.antigen 492 -set-default ADOTDIR $HOME/.antigen
490 493
491 # Load the compinit module. 494 # Load the compinit module.
492 autoload -U compinit 495 autoload -U compinit
493 496
494 # Without the following, `compdef` function is not defined. 497 # Without the following, `compdef` function is not defined.
495 compinit -i 498 compinit -i
496 } 499 }
497 500
498 # Same as `export $1=$2`, but will only happen if the name specified by `$1` is 501 # Same as `export $1=$2`, but will only happen if the name specified by `$1` is
499 # not already set. 502 # not already set.
500 -set-default () { 503 -set-default () {
501 local arg_name="$1" 504 local arg_name="$1"
502 local arg_value="$2" 505 local arg_value="$2"
503 eval "test -z \"\$$arg_name\" && export $arg_name='$arg_value'" 506 eval "test -z \"\$$arg_name\" && export $arg_name='$arg_value'"
504 } 507 }
505 508
506 -antigen-env-setup 509 -antigen-env-setup
507 510
1 Helper alias. 1 Helper alias.
2 2
3 $ alias parse='-antigen-parse-args "url?,loc?;btype:?,no-local-clone?"' 3 $ alias parse='-antigen-parse-args "url?,loc?;btype:?,no-local-clone?"'
4 4
5 No arguments (since all are specified as optional). 5 No arguments (since all are specified as optional).
6 6
7 $ parse 7 $ parse
8 (glob) 8 (glob)
9 9
10 One positional argument. 10 One positional argument.
11 11
12 $ parse name 12 $ parse name
13 local url='name' 13 local url='name'
14 14
15 Two arguments. 15 Two arguments.
16 16
17 $ parse url location 17 $ parse url location
18 local url='url' 18 local url='url'
19 local loc='location' 19 local loc='location'
20 20
21 Three arguments. 21 Three arguments.
22 22
23 $ parse url location crap 23 $ parse url location crap
24 Only 2 positional arguments allowed. 24 Only 2 positional arguments allowed.
25 Found at least one more: 'crap' 25 Found at least one more: 'crap'
26 26
27 Keywordo magic. 27 Keywordo magic.
28 28
29 $ parse url location --btype=1 --no-local-clone 29 $ parse url location --btype=1 --no-local-clone
30 local url='url' 30 local url='url'
31 local loc='location' 31 local loc='location'
32 local btype='1' 32 local btype='1'
33 local no_local_clone='true' 33 local no_local_clone='true'
34 34
35 Unknown keyword argument. 35 Unknown keyword argument.
36 36
37 $ parse --me=genius 37 $ parse --me=genius
38 Unknown argument 'me'. 38 Unknown argument 'me'.
39 39
40 Missed value for keyword argument. 40 Missed value for keyword argument.
41 41
42 $ parse --btype 42 $ parse --btype
43 Required argument for 'btype' not provided. 43 Required argument for 'btype' not provided.
44 44
45 Provide value for keyword argument, that shouldn't be there. 45 Provide value for keyword argument, that shouldn't be there.
46 46
47 $ parse --no-local-clone=yes 47 $ parse --no-local-clone=yes
48 No argument required for 'no-local-clone', but provided 'yes'. 48 No argument required for 'no-local-clone', but provided 'yes'.
49
50 Positional argument as a keyword argument.
51
52 $ parse --url=some-url
53 local url='some-url'
49 54