Commit 9d1b71736ad13e498537d1c7b3b45d94f078c696

Authored by Shrikant Sharat
1 parent 723b284b47

Detect repeated arguments and report them.

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