Commit 6d333dfa0d24fbb9481cb7b159834ec4271f2f7a

Authored by Shrikant Sharat
1 parent 620c89d5b4

Fix #20. Using `sed -r` breaks antigen on Mac.

The BSD version of `sed` apparently does not have the `-r` option. So,
eliminated the use of `sed` in this place altogather.

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