• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#compdef cargo
2
3autoload -U regexp-replace
4
5_cargo() {
6    local curcontext="$curcontext" ret=1
7    local -a command_scope_spec common parallel features msgfmt triple target registry
8    local -a state line state_descr # These are set by _arguments
9    typeset -A opt_args
10
11    common=(
12        '(-q --quiet)*'{-v,--verbose}'[use verbose output]'
13        '(-q --quiet -v --verbose)'{-q,--quiet}'[no output printed to stdout]'
14        '-Z+[pass unstable (nightly-only) flags to cargo]: :_cargo_unstable_flags'
15        '--frozen[require that Cargo.lock and cache are up-to-date]'
16        '--locked[require that Cargo.lock is up-to-date]'
17        '--color=[specify colorization option]:coloring:(auto always never)'
18        '(- 1 *)'{-h,--help}'[show help message]'
19    )
20
21    # leading items in parentheses are an exclusion list for the arguments following that arg
22    # See: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-Functions
23    #   - => exclude all other options
24    #   1 => exclude positional arg 1
25    #   * => exclude all other args
26    #   +blah => exclude +blah
27    _arguments -s -S -C $common \
28        '(- 1 *)--list[list installed commands]' \
29        '(- 1 *)--explain=[provide a detailed explanation of an error message]:error code' \
30        '(- 1 *)'{-V,--version}'[show version information]' \
31        '(+beta +nightly)+stable[use the stable toolchain]' \
32        '(+stable +nightly)+beta[use the beta toolchain]' \
33        '(+stable +beta)+nightly[use the nightly toolchain]' \
34        '1: :_cargo_cmds' \
35        '*:: :->args'
36
37    # These flags are mutually exclusive specifiers for the scope of a command; as
38    # they are used in multiple places without change, they are expanded into the
39    # appropriate command's `_arguments` where appropriate.
40    command_scope_spec=(
41        '(--bin --example --test --lib)--bench=[specify benchmark name]: :_cargo_benchmark_names'
42        '(--bench --bin --test --lib)--example=[specify example name]:example name:_cargo_example_names'
43        '(--bench --example --test --lib)--bin=[specify binary name]:binary name'
44        '(--bench --bin --example --test)--lib=[specify library name]:library name'
45        '(--bench --bin --example --lib)--test=[specify test name]:test name'
46    )
47
48    jobs=(
49        '(-j --jobs)'{-j+,--jobs=}'[specify number of parallel jobs]:jobs [# of CPUs]'
50    )
51
52    parallel=(
53        "${jobs[@]}"
54        '--keep-going[do not abort build on first build error]'
55    )
56
57    features=(
58        '(--all-features)'{-F+,--features=}'[specify features to activate]:feature'
59        '(--features -F)--all-features[activate all available features]'
60        "--no-default-features[don't build the default features]"
61    )
62
63    msgfmt='--message-format=[specify error format]:error format [human]:(human json short)'
64    triple='--target=[specify target triple]:target triple:_cargo_target_triple'
65    target='--target-dir=[specify directory for all generated artifacts]:directory:_directories'
66    manifest='--manifest-path=[specify path to manifest]:path:_directories'
67    registry='--registry=[specify registry to use]:registry'
68
69    case $state in
70        args)
71            curcontext="${curcontext%:*}-${words[1]}:"
72            case ${words[1]} in
73                add)
74                    _arguments -s -A "^--" $common $manifest $registry \
75                        {-F+,--features=}'[specify features to activate]:feature' \
76                        "--default-features[enable the default features]" \
77                        "--no-default-features[don't enable the default features]" \
78                        "--optional[mark the dependency as optional]" \
79                        "--no-optional[mark the dependency as required]" \
80                        "--dev[add as a dev dependency]" \
81                        "--build[add as a build dependency]" \
82                        "--target=[add as a dependency to the given target platform]" \
83                        "--rename=[rename the dependency]" \
84                        "--dry-run[don't actually write the manifest]" \
85                        '--branch=[branch to use when adding from git]:branch' \
86                        '--git=[specify URL from which to add the crate]:url:_urls' \
87                        '--path=[local filesystem path to crate to add]: :_directories' \
88                        '--rev=[specific commit to use when adding from git]:commit' \
89                        '--tag=[tag to use when adding from git]:tag' \
90                        '--ignore-rust-version[Ignore rust-version specification in packages]' \
91                        '1: :_guard "^-*" "crate name"' \
92                        '*:args:_default'
93                        ;;
94                bench)
95                    _arguments -s -A "^--" $common $jobs $features $msgfmt $triple $target $manifest \
96                        "${command_scope_spec[@]}" \
97                        '--all-targets[benchmark all targets]' \
98                        "--no-run[compile but don't run]" \
99                        '(-p --package)'{-p+,--package=}'[specify package to run benchmarks for]:package:_cargo_package_names' \
100                        '--exclude=[exclude packages from the benchmark]:spec' \
101                        '--no-fail-fast[run all benchmarks regardless of failure]' \
102                        '--ignore-rust-version[Ignore rust-version specification in packages]' \
103                        '1: :_guard "^-*" "bench name"' \
104                        '*:args:_default'
105                        ;;
106
107                build | b)
108                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
109                        '--all-targets[equivalent to specifying --lib --bins --tests --benches --examples]' \
110                        "${command_scope_spec[@]}" \
111                        '(-p --package)'{-p+,--package=}'[specify package to build]:package:_cargo_package_names' \
112                        '--release[build in release mode]' \
113                        '--build-plan[output the build plan in JSON]' \
114                        '--ignore-rust-version[Ignore rust-version specification in packages]'
115                        ;;
116
117                check | c)
118                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
119                        '--all-targets[equivalent to specifying --lib --bins --tests --benches --examples]' \
120                        "${command_scope_spec[@]}" \
121                        '(-p --package)'{-p+,--package=}'[specify package to check]:package:_cargo_package_names' \
122                        '--release[check in release mode]' \
123                        '--ignore-rust-version[Ignore rust-version specification in packages]'
124                        ;;
125
126                clean)
127                    _arguments -s -S $common $triple $target $manifest \
128                        '(-p --package)'{-p+,--package=}'[specify package to clean]:package:_cargo_package_names' \
129                        '--release[clean release artifacts]' \
130                        '--doc[clean just the documentation directory]'
131                        ;;
132
133                doc | d)
134                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
135                        '--no-deps[do not build docs for dependencies]' \
136                        '--document-private-items[include non-public items in the documentation]' \
137                        '--open[open docs in browser after the build]' \
138                        '(-p --package)'{-p+,--package=}'[specify package to document]:package:_cargo_package_names' \
139                        '--release[build artifacts in release mode, with optimizations]' \
140                        '--ignore-rust-version[Ignore rust-version specification in packages]'
141                        ;;
142
143                fetch)
144                    _arguments -s -S $common $triple $manifest
145                        ;;
146
147                fix)
148                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
149                        "${command_scope_spec[@]}" \
150                        '--broken-code[fix code even if it already has compiler errors]' \
151                        '--edition[fix in preparation for the next edition]' \
152                        '--edition-idioms[fix warnings to migrate to the idioms of an edition]' \
153                        '--allow-no-vcs[fix code even if a VCS was not detected]' \
154                        '--allow-dirty[fix code even if the working directory is dirty]' \
155                        '--allow-staged[fix code even if the working directory has staged changes]' \
156                        '--ignore-rust-version[Ignore rust-version specification in packages]'
157                ;;
158
159                generate-lockfile)
160                    _arguments -s -S $common $manifest
161                        ;;
162
163                help)
164                    _cargo_cmds
165                        ;;
166                info)
167                    _arguments -s -A "^--" $common $registry \
168                        '--index=[specify registry index]:index' \
169                        '*: :_guard "^-*" "crate"'
170                        ;;
171
172                init)
173                    _arguments -s -S $common $registry \
174                        '--lib[use library template]' \
175                        '--edition=[specify edition to set for the crate generated]:edition:(2015 2018 2021)' \
176                        '--vcs=[initialize a new repo with a given VCS]:vcs:(git hg pijul fossil none)' \
177                        '--name=[set the resulting package name]:name' \
178                        '1:path:_directories'
179                        ;;
180
181                install)
182                    _arguments -s -S $common $parallel $features $triple $registry \
183                        '(-f --force)'{-f,--force}'[force overwriting of existing crates or binaries]' \
184                        '--bin=[only install the specified binary]:binary' \
185                        '--branch=[branch to use when installing from git]:branch' \
186                        '--debug[Build in debug mode (with the "dev" profile) instead of release mode]' \
187                        '--example=[install the specified example instead of binaries]:example:_cargo_example_names' \
188                        '--git=[specify URL from which to install the crate]:url:_urls' \
189                        '--path=[local filesystem path to crate to install]: :_directories' \
190                        '--rev=[specific commit to use when installing from git]:commit' \
191                        '--root=[directory to install packages into]: :_directories' \
192                        '--tag=[tag to use when installing from git]:tag' \
193                        '--version=[version to install from crates.io]:version' \
194                        '--list[list all installed packages and their versions]' \
195                        '--ignore-rust-version[Ignore rust-version specification in packages]' \
196                        '*: :_guard "^-*" "crate"'
197                        ;;
198
199                locate-project)
200                    _arguments -s -S $common $manifest \
201                        '--message-format=[specify output representation]:output representation [json]:(json plain)' \
202                        '--workspace[locate Cargo.toml of the workspace root]'
203                        ;;
204
205                login)
206                    _arguments -s -S $common $registry \
207                        '*: :_guard "^-*" "token"'
208                        ;;
209
210                metadata)
211                    _arguments -s -S $common $features $manifest \
212                        "--no-deps[output information only about the root package and don't fetch dependencies]" \
213                        '--format-version=[specify format version]:version [1]:(1)'
214                        ;;
215
216                new)
217                    _arguments -s -S $common $registry \
218                        '--lib[use library template]' \
219                        '--vcs:initialize a new repo with a given VCS:(git hg none)' \
220                        '--name=[set the resulting package name]'
221                        ;;
222
223                owner)
224                    _arguments -s -S $common $registry \
225                        '(-a --add)'{-a,--add}'[specify name of a user or team to invite as an owner]:name' \
226                        '--index=[specify registry index]:index' \
227                        '(-l --list)'{-l,--list}'[list owners of a crate]' \
228                        '(-r --remove)'{-r,--remove}'[specify name of a user or team to remove as an owner]:name' \
229                        '--token=[specify API token to use when authenticating]:token' \
230                        '*: :_guard "^-*" "crate"'
231                        ;;
232
233                package)
234                    _arguments -s -S $common $parallel $features $triple $target $manifest $registry \
235                        '--index=[specify registry index]:index' \
236                        '(-l --list)'{-l,--list}'[print files included in a package without making one]' \
237                        '--no-metadata[ignore warnings about a lack of human-usable metadata]' \
238                        '--allow-dirty[allow dirty working directories to be packaged]' \
239                        "--no-verify[don't build to verify contents]"
240                        ;;
241
242                pkgid)
243                    _arguments -s -S $common $manifest \
244                        '(-p --package)'{-p+,--package=}'[specify package to get ID specifier for]:package:_cargo_package_names' \
245                        '*: :_guard "^-*" "spec"'
246                        ;;
247
248                publish)
249                    _arguments -s -S $common $parallel $features $triple $target $manifest $registry \
250                        '--index=[specify registry index]:index' \
251                        '--allow-dirty[allow dirty working directories to be packaged]' \
252                        "--no-verify[don't verify the contents by building them]" \
253                        '--token=[specify token to use when uploading]:token' \
254                        '--dry-run[perform all checks without uploading]'
255                        ;;
256
257                read-manifest)
258                    _arguments -s -S $common $manifest
259                        ;;
260
261                remove | rm)
262                    _arguments -s -A "^--" $common $manifest \
263                        "--dev[remove as a dev dependency]" \
264                        "--build[remove as a build dependency]" \
265                        "--target=[remove as a dependency from the given target platform]" \
266                        "--dry-run[don't actually write the manifest]" \
267                        '(-p --package)'{-p+,--package=}'[package to remove from]:package:_cargo_package_names' \
268                        '1: :_guard "^-*" "crate name"' \
269                        '*:args:_default'
270                        ;;
271
272                run | r)
273                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
274                        '--example=[name of the bin target]:name:_cargo_example_names' \
275                        '--bin=[name of the bin target]:name' \
276                        '(-p --package)'{-p+,--package=}'[specify package with the target to run]:package:_cargo_package_names' \
277                        '--release[build in release mode]' \
278                        '--ignore-rust-version[Ignore rust-version specification in packages]' \
279                        '*: :_default'
280                        ;;
281
282                rustc)
283                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
284                        '(-p --package)'{-p+,--package=}'[specify package to build]:package:_cargo_package_names' \
285                        '--profile=[specify profile to build the selected target for]:profile' \
286                        '--release[build artifacts in release mode, with optimizations]' \
287                        "${command_scope_spec[@]}" \
288                        '--ignore-rust-version[Ignore rust-version specification in packages]' \
289                        '*: : _dispatch rustc rustc -default-'
290                        ;;
291
292                rustdoc)
293                    _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
294                        '--document-private-items[include non-public items in the documentation]' \
295                        '--open[open the docs in a browser after the operation]' \
296                        '(-p --package)'{-p+,--package=}'[specify package to document]:package:_cargo_package_names' \
297                        '--release[build artifacts in release mode, with optimizations]' \
298                        "${command_scope_spec[@]}" \
299                        '--ignore-rust-version[Ignore rust-version specification in packages]' \
300                        '*: : _dispatch rustdoc rustdoc -default-'
301                        ;;
302
303                search)
304                    _arguments -s -S $common $registry \
305                        '--index=[specify registry index]:index' \
306                        '--limit=[limit the number of results]:results [10]' \
307                        '*: :_guard "^-*" "query"'
308                        ;;
309
310                test | t)
311                    _arguments -s -S $common $jobs $features $msgfmt $triple $target $manifest \
312                        '--test=[test name]: :_cargo_test_names' \
313                        '--no-fail-fast[run all tests regardless of failure]' \
314                        '--no-run[compile but do not run]' \
315                        '(-p --package)'{-p+,--package=}'[package to run tests for]:package:_cargo_package_names' \
316                        '--all[test all packages in the workspace]' \
317                        '--release[build artifacts in release mode, with optimizations]' \
318                        '1: :_cargo_test_names' \
319                        '(--doc --bin --example --test --bench)--lib[only test library]' \
320                        '(--lib --bin --example --test --bench)--doc[only test documentation]' \
321                        '(--lib --doc --example --test --bench)--bin=[binary name]' \
322                        '(--lib --doc --bin --test --bench)--example=[example name]:_cargo_example_names' \
323                        '(--lib --doc --bin --example --bench)--test=[test name]' \
324                        '(--lib --doc --bin --example --test)--bench=[benchmark name]' \
325                        '--ignore-rust-version[Ignore rust-version specification in packages]' \
326                        '*: :_default'
327                        ;;
328
329                tree)
330                    _arguments -s -S $common $features $triple $manifest \
331                        '(-p --package)'{-p+,--package=}'[package to use as the root]:package:_cargo_package_names' \
332                        '(-i --invert)'{-i+,--invert=}'[invert the tree for the given package]:package:_cargo_package_names' \
333                        '--prefix=[line prefix]:prefix:(depth indent none)' \
334                        '--no-dedupe[repeat shared dependencies]' \
335                        '(-d --duplicates)'{-d,--duplicates}'[packages with multiple versions]' \
336                        '--charset=[utf8 or ascii]:charset:(utf8 ascii)' \
337                        '(-f --format)'{-f,--format=}'[format string]:format' \
338                        '(-e --edges)'{-e,--edges=}'[edge kinds]:kind:(features normal build dev all no-dev no-build no-normal)' \
339                        ;;
340
341                uninstall)
342                    _arguments -s -S $common \
343                        '(-p --package)'{-p+,--package=}'[specify package to uninstall]:package:_cargo_package_names' \
344                        '--bin=[only uninstall the specified binary]:name' \
345                        '--root=[directory to uninstall packages from]: :_files -/' \
346                        '*:crate:_cargo_installed_crates -F line'
347                        ;;
348
349                update)
350                    _arguments -s -S $common $manifest \
351                        '--aggressive=[force dependency update]' \
352                        '--recursive=[force dependency update]' \
353                        "--dry-run[don't actually write the lockfile]" \
354                        '(-p --package)'{-p+,--package=}'[specify package to update]:package:_cargo_package_names' \
355                        '--precise=[update single dependency to precise release]:release' \
356                        '*:package:_cargo_package_names'
357                        ;;
358
359                verify-project)
360                    _arguments -s -S $common $manifest
361                        ;;
362
363                version)
364                    _arguments -s -S $common
365                        ;;
366
367                yank)
368                    _arguments -s -S $common $registry \
369                        '--version=[specify yank version]:version' \
370                        '--undo[undo a yank, putting a version back into the index]' \
371                        '--index=[specify registry index to yank from]:registry index' \
372                        '--token=[specify API token to use when authenticating]:token' \
373                        '*: :_guard "^-*" "crate"'
374                        ;;
375                *)
376                    # allow plugins to define their own functions
377                    if ! _call_function ret _cargo-${words[1]}; then
378                        # fallback on default completion for unknown commands
379                        _default && ret=0
380                    fi
381                    (( ! ret ))
382                ;;
383            esac
384            ;;
385    esac
386}
387
388_cargo_unstable_flags() {
389    local flags
390    flags=( help ${${${(M)${(f)"$(_call_program flags cargo -Z help)"}:#*--*}/ #-- #/:}##*-Z } )
391    _describe -t flags 'unstable flag' flags
392}
393
394_cargo_installed_crates() {
395    local expl
396    _description crates expl 'crate'
397    compadd "$@" "$expl[@]" - ${${${(f)"$(cargo install --list)"}:# *}%% *}
398}
399
400_cargo_cmds() {
401    local -a commands
402    # This uses Parameter Expansion Flags, which are a built-in Zsh feature.
403    # See more: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
404    # and       http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion
405    #
406    # # How this work?
407    #
408    # First it splits the result of `cargo --list` at newline, then it removes the first line.
409    # Then it removes indentation (4 whitespaces) before each items. (Note the x## pattern [1]).
410    # Then it replaces those spaces between item and description with a `:`
411    #
412    # [1]: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org#patterns
413    commands=( ${${${(M)"${(f)$(_call_program commands cargo --list)}":#    *}/ ##/}/ ##/:} )
414    _describe -t commands 'command' commands
415}
416
417_cargo_target_triple() {
418    local -a result
419
420    if (( $+commands[rustup] )); then
421        result=( ${(f)"$(rustup target list --installed)"} )
422    else
423        result=( ${(f)"$(rustc --print target-list)"} )
424    fi
425
426    _describe 'target triple' result
427}
428
429#FIXME: Disabled until fixed
430#gets package names from the manifest file
431_cargo_package_names() {
432    _message -e packages package
433}
434
435# Extracts the values of "name" from the array given in $1 and shows them as
436# command line options for completion
437_cargo_names_from_array() {
438    local manifest=$(cargo locate-project --message-format plain)
439    if [[ -z $manifest ]]; then
440        return 0
441    fi
442
443    local last_line
444    local -a names;
445    local in_block=false
446    local block_name=$1
447    names=()
448    while read -r line; do
449        if [[ $last_line == "[[$block_name]]" ]]; then
450            in_block=true
451        else
452            if [[ $last_line =~ '\s*\[\[.*' ]]; then
453                in_block=false
454            fi
455        fi
456
457        if [[ $in_block == true ]]; then
458            if [[ $line =~ '\s*name\s*=' ]]; then
459                regexp-replace line '^\s*name\s*=\s*|"' ''
460                names+=( "$line" )
461            fi
462        fi
463
464        last_line=$line
465    done < "$manifest"
466    _describe "$block_name" names
467
468}
469
470#Gets the test names from the manifest file
471_cargo_test_names() {
472    _cargo_names_from_array "test"
473}
474
475#Gets the bench names from the manifest file
476_cargo_benchmark_names() {
477    _cargo_names_from_array "bench"
478}
479
480_cargo_example_names() {
481    if [[ -d examples ]]; then
482        local -a files=(${(@f)$(echo examples/*.rs(:t:r))})
483        _values 'example' "${files[@]}"
484    fi
485}
486
487_cargo
488