_cargo 19.2 KB
Newer Older
1
2
3
4
5
#compdef cargo

autoload -U regexp-replace

_cargo() {
6
7
8
9
10
11
12
13
14
    local curcontext="$curcontext" ret=1
    local -a command_scope_spec common parallel features msgfmt triple target registry
    local -a state line state_descr # These are set by _arguments
    typeset -A opt_args

    common=(
        '(-q --quiet)*'{-v,--verbose}'[use verbose output]'
        '(-q --quiet -v --verbose)'{-q,--quiet}'[no output printed to stdout]'
        '-Z+[pass unstable (nightly-only) flags to cargo]: :_cargo_unstable_flags'
15
16
        '--frozen[require that Cargo.lock and cache are up to date]'
        '--locked[require that Cargo.lock is up to date]'
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
        '--color=[specify colorization option]:coloring:(auto always never)'
        '(- 1 *)'{-h,--help}'[show help message]'
    )

    # leading items in parentheses are an exclusion list for the arguments following that arg
    # See: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-Functions
    #   - => exclude all other options
    #   1 => exclude positional arg 1
    #   * => exclude all other args
    #   +blah => exclude +blah
    _arguments -s -S -C $common \
        '(- 1 *)--list[list installed commands]' \
        '(- 1 *)--explain=[provide a detailed explanation of an error message]:error code' \
        '(- 1 *)'{-V,--version}'[show version information]' \
        '(+beta +nightly)+stable[use the stable toolchain]' \
        '(+stable +nightly)+beta[use the beta toolchain]' \
        '(+stable +beta)+nightly[use the nightly toolchain]' \
        '1: :_cargo_cmds' \
        '*:: :->args'

    # These flags are mutually exclusive specifiers for the scope of a command; as
    # they are used in multiple places without change, they are expanded into the
    # appropriate command's `_arguments` where appropriate.
    command_scope_spec=(
        '(--bin --example --test --lib)--bench=[specify benchmark name]: :_cargo_benchmark_names'
        '(--bench --bin --test --lib)--example=[specify example name]:example name'
        '(--bench --example --test --lib)--bin=[specify binary name]:binary name'
        '(--bench --bin --example --test)--lib=[specify library name]:library name'
        '(--bench --bin --example --lib)--test=[specify test name]:test name'
    )

    parallel=(
        '(-j --jobs)'{-j+,--jobs=}'[specify number of parallel jobs]:jobs [# of CPUs]'
    )

    features=(
        '(--all-features)--features=[specify features to activate]:feature'
        '(--features)--all-features[activate all available features]'
        "--no-default-features[don't build the default features]"
    )

    msgfmt='--message-format=[specify error format]:error format [human]:(human json short)'
    triple='--target=[specify target triple]:target triple'
    target='--target-dir=[specify directory for all generated artifacts]:directory:_directories'
    manifest='--manifest-path=[specify path to manifest]:path:_directories'
    registry='--registry=[specify registry to use]:registry'

    case $state in
        args)
            curcontext="${curcontext%:*}-${words[1]}:"
            case ${words[1]} in
                bench)
                    _arguments -s -A "^--" $common $parallel $features $msgfmt $triple $target $manifest \
                        "${command_scope_spec[@]}" \
                        '--all-targets[benchmark all targets]' \
                        "--no-run[compile but don't run]" \
                        '(-p --package)'{-p+,--package=}'[specify package to run benchmarks for]:package:_cargo_package_names' \
                        '--exclude=[exclude packages from the benchmark]:spec' \
                        '--no-fail-fast[run all benchmarks regardless of failure]' \
                        '1: :_guard "^-*" "bench name"' \
                        '*:args:_default'
                        ;;

                build)
                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
                        '--all-targets[equivalent to specifying --lib --bins --tests --benches --examples]' \
                        "${command_scope_spec[@]}" \
                        '(-p --package)'{-p+,--package=}'[specify package to build]:package:_cargo_package_names' \
                        '--release[build in release mode]' \
                        '--build-plan[output the build plan in JSON]' \
                        ;;

                check)
                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
                        '--all-targets[equivalent to specifying --lib --bins --tests --benches --examples]' \
                        "${command_scope_spec[@]}" \
                        '(-p --package)'{-p+,--package=}'[specify package to check]:package:_cargo_package_names' \
                        '--release[check in release mode]' \
                        ;;

                clean)
                    _arguments -s -S $common $triple $target $manifest \
                        '(-p --package)'{-p+,--package=}'[specify package to clean]:package:_cargo_package_names' \
                        '--release[clean release artifacts]' \
                        '--doc[clean just the documentation directory]'
                        ;;

                doc)
                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
                        '--no-deps[do not build docs for dependencies]' \
                        '--document-private-items[include non-public items in the documentation]' \
                        '--open[open docs in browser after the build]' \
                        '(-p --package)'{-p+,--package=}'[specify package to document]:package:_cargo_package_names' \
                        '--release[build artifacts in release mode, with optimizations]' \
                        ;;

                fetch)
                    _arguments -s -S $common $triple $manifest
                        ;;

                fix)
                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
                        "${command_scope_spec[@]}" \
                        '--broken-code[fix code even if it already has compiler errors]' \
                        '--edition[fix in preparation for the next edition]' \
                        '--edition-idioms[fix warnings to migrate to the idioms of an edition]' \
                        '--allow-no-vcs[fix code even if a VCS was not detected]' \
                        '--allow-dirty[fix code even if the working directory is dirty]' \
                        '--allow-staged[fix code even if the working directory has staged changes]'
                ;;

                generate-lockfile)
                    _arguments -s -S $common $manifest
                        ;;

                git-checkout)
                    _arguments -s -S $common \
                        '--reference=:reference' \
                        '--url=:url:_urls'
                        ;;

                help)
                    _cargo_cmds
                        ;;

                init)
                    _arguments -s -S $common $registry \
                        '--lib[use library template]' \
                        '--edition=[specify edition to set for the crate generated]:edition:(2015 2018)' \
                        '--vcs=[initialize a new repo with a given VCS]:vcs:(git hg pijul fossil none)' \
                        '--name=[set the resulting package name]:name' \
                        '1:path:_directories'
                        ;;

                install)
                    _arguments -s -S $common $parallel $features $triple $registry \
                        '(-f --force)'{-f,--force}'[force overwriting of existing crates or binaries]' \
                        '--bin=[only install the specified binary]:binary' \
                        '--branch=[branch to use when installing from git]:branch' \
                        '--debug[build in debug mode instead of release mode]' \
                        '--example=[install the specified example instead of binaries]:example' \
                        '--git=[specify URL from which to install the crate]:url:_urls' \
                        '--path=[local filesystem path to crate to install]: :_directories' \
                        '--rev=[specific commit to use when installing from git]:commit' \
                        '--root=[directory to install packages into]: :_directories' \
                        '--tag=[tag to use when installing from git]:tag' \
                        '--vers=[version to install from crates.io]:version' \
                        '--list[list all installed packages and their versions]' \
                        '*: :_guard "^-*" "crate"'
                        ;;

                locate-project)
                    _arguments -s -S $common $manifest
                        ;;

                login)
                    _arguments -s -S $common $registry \
                        '*: :_guard "^-*" "token"'
                        ;;

                metadata)
                    _arguments -s -S $common $features $manifest \
                        "--no-deps[output information only about the root package and don't fetch dependencies]" \
                        '--format-version=[specify format version]:version [1]:(1)'
                        ;;

                new)
                    _arguments -s -S $common $registry \
                        '--lib[use library template]' \
                        '--vcs:initialize a new repo with a given VCS:(git hg none)' \
                        '--name=[set the resulting package name]'
                        ;;

                owner)
                    _arguments -s -S $common $registry \
                        '(-a --add)'{-a,--add}'[specify name of a user or team to invite as an owner]:name' \
                        '--index=[specify registry index]:index' \
                        '(-l --list)'{-l,--list}'[list owners of a crate]' \
                        '(-r --remove)'{-r,--remove}'[specify name of a user or team to remove as an owner]:name' \
                        '--token=[specify API token to use when authenticating]:token' \
                        '*: :_guard "^-*" "crate"'
                        ;;

                package)
                    _arguments -s -S $common $parallel $features $triple $target $manifest \
                        '(-l --list)'{-l,--list}'[print files included in a package without making one]' \
                        '--no-metadata[ignore warnings about a lack of human-usable metadata]' \
                        '--allow-dirty[allow dirty working directories to be packaged]' \
                        "--no-verify[don't build to verify contents]"
                        ;;

                pkgid)
                    _arguments -s -S $common $manifest \
                        '(-p --package)'{-p+,--package=}'[specify package to get ID specifier for]:package:_cargo_package_names' \
                        '*: :_guard "^-*" "spec"'
                        ;;

                publish)
                    _arguments -s -S $common $parallel $features $triple $target $manifest $registry \
                        '--index=[specify registry index]:index' \
                        '--allow-dirty[allow dirty working directories to be packaged]' \
                        "--no-verify[don't verify the contents by building them]" \
                        '--token=[specify token to use when uploading]:token' \
                        '--dry-run[perform all checks without uploading]'
                        ;;

                read-manifest)
                    _arguments -s -S $common $manifest
                        ;;

                run)
                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
                        '--example=[name of the bin target]:name' \
                        '--bin=[name of the bin target]:name' \
                        '(-p --package)'{-p+,--package=}'[specify package with the target to run]:package:_cargo_package_names' \
                        '--release[build in release mode]' \
                        '*: :_default'
                        ;;

                rustc)
                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
                        '(-p --package)'{-p+,--package=}'[specify package to build]:package:_cargo_package_names' \
                        '--profile=[specify profile to build the selected target for]:profile' \
                        '--release[build artifacts in release mode, with optimizations]' \
                        "${command_scope_spec[@]}" \
                        '*: : _dispatch rustc rustc -default-'
                        ;;

                rustdoc)
                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
                        '--document-private-items[include non-public items in the documentation]' \
                        '--open[open the docs in a browser after the operation]' \
                        '(-p --package)'{-p+,--package=}'[specify package to document]:package:_cargo_package_names' \
                        '--release[build artifacts in release mode, with optimizations]' \
                        "${command_scope_spec[@]}" \
                        '*: : _dispatch rustdoc rustdoc -default-'
                        ;;

                search)
                    _arguments -s -S $common $registry \
                        '--index=[specify registry index]:index' \
                        '--limit=[limit the number of results]:results [10]' \
                        '*: :_guard "^-*" "query"'
                        ;;

                test)
                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
                        '--test=[test name]: :_cargo_test_names' \
                        '--no-fail-fast[run all tests regardless of failure]' \
                        '--no-run[compile but do not run]' \
                        '(-p --package)'{-p+,--package=}'[package to run tests for]:package:_cargo_package_names' \
                        '--all[test all packages in the workspace]' \
                        '--release[build artifacts in release mode, with optimizations]' \
                        '1: :_cargo_test_names' \
                        '(--doc --bin --example --test --bench)--lib[only test library]' \
                        '(--lib --bin --example --test --bench)--doc[only test documentation]' \
                        '(--lib --doc --example --test --bench)--bin=[binary name]' \
                        '(--lib --doc --bin --test --bench)--example=[example name]' \
                        '(--lib --doc --bin --example --bench)--test=[test name]' \
                        '(--lib --doc --bin --example --test)--bench=[benchmark name]' \
                        '*: :_default'
                        ;;

                uninstall)
                    _arguments -s -S $common \
                        '(-p --package)'{-p+,--package=}'[specify package to uninstall]:package:_cargo_package_names' \
                        '--bin=[only uninstall the specified binary]:name' \
                        '--root=[directory to uninstall packages from]: :_files -/' \
                        '*:crate:_cargo_installed_crates -F line'
                        ;;

                update)
                    _arguments -s -S $common $manifest \
                        '--aggressive=[force dependency update]' \
                        "--dry-run[don't actually write the lockfile]" \
                        '(-p --package)'{-p+,--package=}'[specify package to update]:package:_cargo_package_names' \
                        '--precise=[update single dependency to precise release]:release'
                        ;;

                verify-project)
                    _arguments -s -S $common $manifest
                        ;;

                version)
                    _arguments -s -S $common
                        ;;

                yank)
                    _arguments -s -S $common $registry \
                        '--vers=[specify yank version]:version' \
                        '--undo[undo a yank, putting a version back into the index]' \
                        '--index=[specify registry index to yank from]:registry index' \
                        '--token=[specify API token to use when authenticating]:token' \
                        '*: :_guard "^-*" "crate"'
                        ;;
                *)
                    # allow plugins to define their own functions
                    if ! _call_function ret _cargo-${words[1]}; then
                        # fallback on default completion for unknown commands
                        _default && ret=0
                    fi
                    (( ! ret ))
                ;;
            esac
            ;;
    esac
}

_cargo_unstable_flags() {
    local flags
    flags=( help ${${${(M)${(f)"$(_call_program flags cargo -Z help)"}:#*--*}/ #-- #/:}##*-Z } )
    _describe -t flags 'unstable flag' flags
329
330
}

331
332
333
334
_cargo_installed_crates() {
    local expl
    _description crates expl 'crate'
    compadd "$@" "$expl[@]" - ${${${(f)"$(cargo install --list)"}:# *}%% *}
335
}
336

337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
_cargo_cmds() {
    local -a commands
    # This uses Parameter Expansion Flags, which are a built-in Zsh feature.
    # See more: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
    # and       http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion
    #
    # # How this work?
    #
    # First it splits the result of `cargo --list` at newline, then it removes the first line.
    # Then it removes indentation (4 whitespaces) before each items. (Note the x## pattern [1]).
    # Then it replaces those spaces between item and description with a `:`
    #
    # [1]: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org#patterns
    commands=( ${${${(M)"${(f)$(_call_program commands cargo --list)}":#    *}/ ##/}/ ##/:} )
    _describe -t commands 'command' commands
352
353
354
355
356
}


#FIXME: Disabled until fixed
#gets package names from the manifest file
357
358
_cargo_package_names() {
    _message -e packages package
359
360
361
362
}

# Extracts the values of "name" from the array given in $1 and shows them as
# command line options for completion
363
364
365
_cargo_names_from_array() {
    # strip json from the path
    local manifest=${${${"$(cargo locate-project)"}%\"\}}##*\"}
366
367
368
369
370
371
372
373
374
    if [[ -z $manifest ]]; then
        return 0
    fi

    local last_line
    local -a names;
    local in_block=false
    local block_name=$1
    names=()
375
    while read -r line; do
376
377
378
        if [[ $last_line == "[[$block_name]]" ]]; then
            in_block=true
        else
379
            if [[ $last_line =~ '\s*\[\[.*' ]]; then
380
381
382
383
384
                in_block=false
            fi
        fi

        if [[ $in_block == true ]]; then
385
386
387
            if [[ $line =~ '\s*name\s*=' ]]; then
                regexp-replace line '^\s*name\s*=\s*|"' ''
                names+=( "$line" )
388
389
390
391
            fi
        fi

        last_line=$line
392
393
    done < "$manifest"
    _describe "$block_name" names
394
395
396
397

}

#Gets the test names from the manifest file
398
399
_cargo_test_names() {
    _cargo_names_from_array "test"
400
401
402
}

#Gets the bench names from the manifest file
403
404
_cargo_benchmark_names() {
    _cargo_names_from_array "bench"
405
406
407
}

_cargo