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 167 init) 168 _arguments -s -S $common $registry \ 169 '--lib[use library template]' \ 170 '--edition=[specify edition to set for the crate generated]:edition:(2015 2018 2021)' \ 171 '--vcs=[initialize a new repo with a given VCS]:vcs:(git hg pijul fossil none)' \ 172 '--name=[set the resulting package name]:name' \ 173 '1:path:_directories' 174 ;; 175 176 install) 177 _arguments -s -S $common $parallel $features $triple $registry \ 178 '(-f --force)'{-f,--force}'[force overwriting of existing crates or binaries]' \ 179 '--bin=[only install the specified binary]:binary' \ 180 '--branch=[branch to use when installing from git]:branch' \ 181 '--debug[Build in debug mode (with the "dev" profile) instead of release mode]' \ 182 '--example=[install the specified example instead of binaries]:example:_cargo_example_names' \ 183 '--git=[specify URL from which to install the crate]:url:_urls' \ 184 '--path=[local filesystem path to crate to install]: :_directories' \ 185 '--rev=[specific commit to use when installing from git]:commit' \ 186 '--root=[directory to install packages into]: :_directories' \ 187 '--tag=[tag to use when installing from git]:tag' \ 188 '--version=[version to install from crates.io]:version' \ 189 '--list[list all installed packages and their versions]' \ 190 '--ignore-rust-version[Ignore rust-version specification in packages]' \ 191 '*: :_guard "^-*" "crate"' 192 ;; 193 194 locate-project) 195 _arguments -s -S $common $manifest \ 196 '--message-format=[specify output representation]:output representation [json]:(json plain)' \ 197 '--workspace[locate Cargo.toml of the workspace root]' 198 ;; 199 200 login) 201 _arguments -s -S $common $registry \ 202 '*: :_guard "^-*" "token"' 203 ;; 204 205 metadata) 206 _arguments -s -S $common $features $manifest \ 207 "--no-deps[output information only about the root package and don't fetch dependencies]" \ 208 '--format-version=[specify format version]:version [1]:(1)' 209 ;; 210 211 new) 212 _arguments -s -S $common $registry \ 213 '--lib[use library template]' \ 214 '--vcs:initialize a new repo with a given VCS:(git hg none)' \ 215 '--name=[set the resulting package name]' 216 ;; 217 218 owner) 219 _arguments -s -S $common $registry \ 220 '(-a --add)'{-a,--add}'[specify name of a user or team to invite as an owner]:name' \ 221 '--index=[specify registry index]:index' \ 222 '(-l --list)'{-l,--list}'[list owners of a crate]' \ 223 '(-r --remove)'{-r,--remove}'[specify name of a user or team to remove as an owner]:name' \ 224 '--token=[specify API token to use when authenticating]:token' \ 225 '*: :_guard "^-*" "crate"' 226 ;; 227 228 package) 229 _arguments -s -S $common $parallel $features $triple $target $manifest \ 230 '(-l --list)'{-l,--list}'[print files included in a package without making one]' \ 231 '--no-metadata[ignore warnings about a lack of human-usable metadata]' \ 232 '--allow-dirty[allow dirty working directories to be packaged]' \ 233 "--no-verify[don't build to verify contents]" 234 ;; 235 236 pkgid) 237 _arguments -s -S $common $manifest \ 238 '(-p --package)'{-p+,--package=}'[specify package to get ID specifier for]:package:_cargo_package_names' \ 239 '*: :_guard "^-*" "spec"' 240 ;; 241 242 publish) 243 _arguments -s -S $common $parallel $features $triple $target $manifest $registry \ 244 '--index=[specify registry index]:index' \ 245 '--allow-dirty[allow dirty working directories to be packaged]' \ 246 "--no-verify[don't verify the contents by building them]" \ 247 '--token=[specify token to use when uploading]:token' \ 248 '--dry-run[perform all checks without uploading]' 249 ;; 250 251 read-manifest) 252 _arguments -s -S $common $manifest 253 ;; 254 255 remove | rm) 256 _arguments -s -A "^--" $common $manifest \ 257 "--dev[remove as a dev dependency]" \ 258 "--build[remove as a build dependency]" \ 259 "--target=[remove as a dependency from the given target platform]" \ 260 "--dry-run[don't actually write the manifest]" \ 261 '(-p --package)'{-p+,--package=}'[package to remove from]:package:_cargo_package_names' \ 262 '1: :_guard "^-*" "crate name"' \ 263 '*:args:_default' 264 ;; 265 266 run | r) 267 _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ 268 '--example=[name of the bin target]:name:_cargo_example_names' \ 269 '--bin=[name of the bin target]:name' \ 270 '(-p --package)'{-p+,--package=}'[specify package with the target to run]:package:_cargo_package_names' \ 271 '--release[build in release mode]' \ 272 '--ignore-rust-version[Ignore rust-version specification in packages]' \ 273 '*: :_default' 274 ;; 275 276 rustc) 277 _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ 278 '(-p --package)'{-p+,--package=}'[specify package to build]:package:_cargo_package_names' \ 279 '--profile=[specify profile to build the selected target for]:profile' \ 280 '--release[build artifacts in release mode, with optimizations]' \ 281 "${command_scope_spec[@]}" \ 282 '--ignore-rust-version[Ignore rust-version specification in packages]' \ 283 '*: : _dispatch rustc rustc -default-' 284 ;; 285 286 rustdoc) 287 _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ 288 '--document-private-items[include non-public items in the documentation]' \ 289 '--open[open the docs in a browser after the operation]' \ 290 '(-p --package)'{-p+,--package=}'[specify package to document]:package:_cargo_package_names' \ 291 '--release[build artifacts in release mode, with optimizations]' \ 292 "${command_scope_spec[@]}" \ 293 '--ignore-rust-version[Ignore rust-version specification in packages]' \ 294 '*: : _dispatch rustdoc rustdoc -default-' 295 ;; 296 297 search) 298 _arguments -s -S $common $registry \ 299 '--index=[specify registry index]:index' \ 300 '--limit=[limit the number of results]:results [10]' \ 301 '*: :_guard "^-*" "query"' 302 ;; 303 304 test | t) 305 _arguments -s -S $common $jobs $features $msgfmt $triple $target $manifest \ 306 '--test=[test name]: :_cargo_test_names' \ 307 '--no-fail-fast[run all tests regardless of failure]' \ 308 '--no-run[compile but do not run]' \ 309 '(-p --package)'{-p+,--package=}'[package to run tests for]:package:_cargo_package_names' \ 310 '--all[test all packages in the workspace]' \ 311 '--release[build artifacts in release mode, with optimizations]' \ 312 '1: :_cargo_test_names' \ 313 '(--doc --bin --example --test --bench)--lib[only test library]' \ 314 '(--lib --bin --example --test --bench)--doc[only test documentation]' \ 315 '(--lib --doc --example --test --bench)--bin=[binary name]' \ 316 '(--lib --doc --bin --test --bench)--example=[example name]:_cargo_example_names' \ 317 '(--lib --doc --bin --example --bench)--test=[test name]' \ 318 '(--lib --doc --bin --example --test)--bench=[benchmark name]' \ 319 '--ignore-rust-version[Ignore rust-version specification in packages]' \ 320 '*: :_default' 321 ;; 322 323 tree) 324 _arguments -s -S $common $features $triple $manifest \ 325 '(-p --package)'{-p+,--package=}'[package to use as the root]:package:_cargo_package_names' \ 326 '(-i --invert)'{-i+,--invert=}'[invert the tree for the given package]:package:_cargo_package_names' \ 327 '--prefix=[line prefix]:prefix:(depth indent none)' \ 328 '--no-dedupe[repeat shared dependencies]' \ 329 '(-d --duplicates)'{-d,--duplicates}'[packages with multiple versions]' \ 330 '--charset=[utf8 or ascii]:charset:(utf8 ascii)' \ 331 '(-f --format)'{-f,--format=}'[format string]:format' \ 332 '(-e --edges)'{-e,--edges=}'[edge kinds]:kind:(features normal build dev all no-dev no-build no-normal)' \ 333 ;; 334 335 uninstall) 336 _arguments -s -S $common \ 337 '(-p --package)'{-p+,--package=}'[specify package to uninstall]:package:_cargo_package_names' \ 338 '--bin=[only uninstall the specified binary]:name' \ 339 '--root=[directory to uninstall packages from]: :_files -/' \ 340 '*:crate:_cargo_installed_crates -F line' 341 ;; 342 343 update) 344 _arguments -s -S $common $manifest \ 345 '--aggressive=[force dependency update]' \ 346 '--recursive=[force dependency update]' \ 347 "--dry-run[don't actually write the lockfile]" \ 348 '(-p --package)'{-p+,--package=}'[specify package to update]:package:_cargo_package_names' \ 349 '--precise=[update single dependency to precise release]:release' \ 350 '*:package:_cargo_package_names' 351 ;; 352 353 verify-project) 354 _arguments -s -S $common $manifest 355 ;; 356 357 version) 358 _arguments -s -S $common 359 ;; 360 361 yank) 362 _arguments -s -S $common $registry \ 363 '--version=[specify yank version]:version' \ 364 '--undo[undo a yank, putting a version back into the index]' \ 365 '--index=[specify registry index to yank from]:registry index' \ 366 '--token=[specify API token to use when authenticating]:token' \ 367 '*: :_guard "^-*" "crate"' 368 ;; 369 *) 370 # allow plugins to define their own functions 371 if ! _call_function ret _cargo-${words[1]}; then 372 # fallback on default completion for unknown commands 373 _default && ret=0 374 fi 375 (( ! ret )) 376 ;; 377 esac 378 ;; 379 esac 380} 381 382_cargo_unstable_flags() { 383 local flags 384 flags=( help ${${${(M)${(f)"$(_call_program flags cargo -Z help)"}:#*--*}/ #-- #/:}##*-Z } ) 385 _describe -t flags 'unstable flag' flags 386} 387 388_cargo_installed_crates() { 389 local expl 390 _description crates expl 'crate' 391 compadd "$@" "$expl[@]" - ${${${(f)"$(cargo install --list)"}:# *}%% *} 392} 393 394_cargo_cmds() { 395 local -a commands 396 # This uses Parameter Expansion Flags, which are a built-in Zsh feature. 397 # See more: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags 398 # and http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion 399 # 400 # # How this work? 401 # 402 # First it splits the result of `cargo --list` at newline, then it removes the first line. 403 # Then it removes indentation (4 whitespaces) before each items. (Note the x## pattern [1]). 404 # Then it replaces those spaces between item and description with a `:` 405 # 406 # [1]: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org#patterns 407 commands=( ${${${(M)"${(f)$(_call_program commands cargo --list)}":# *}/ ##/}/ ##/:} ) 408 _describe -t commands 'command' commands 409} 410 411_cargo_target_triple() { 412 local -a result 413 414 if (( $+commands[rustup] )); then 415 result=( ${(f)"$(rustup target list --installed)"} ) 416 else 417 result=( ${(f)"$(rustc --print target-list)"} ) 418 fi 419 420 _describe 'target triple' result 421} 422 423#FIXME: Disabled until fixed 424#gets package names from the manifest file 425_cargo_package_names() { 426 _message -e packages package 427} 428 429# Extracts the values of "name" from the array given in $1 and shows them as 430# command line options for completion 431_cargo_names_from_array() { 432 local manifest=$(cargo locate-project --message-format plain) 433 if [[ -z $manifest ]]; then 434 return 0 435 fi 436 437 local last_line 438 local -a names; 439 local in_block=false 440 local block_name=$1 441 names=() 442 while read -r line; do 443 if [[ $last_line == "[[$block_name]]" ]]; then 444 in_block=true 445 else 446 if [[ $last_line =~ '\s*\[\[.*' ]]; then 447 in_block=false 448 fi 449 fi 450 451 if [[ $in_block == true ]]; then 452 if [[ $line =~ '\s*name\s*=' ]]; then 453 regexp-replace line '^\s*name\s*=\s*|"' '' 454 names+=( "$line" ) 455 fi 456 fi 457 458 last_line=$line 459 done < "$manifest" 460 _describe "$block_name" names 461 462} 463 464#Gets the test names from the manifest file 465_cargo_test_names() { 466 _cargo_names_from_array "test" 467} 468 469#Gets the bench names from the manifest file 470_cargo_benchmark_names() { 471 _cargo_names_from_array "bench" 472} 473 474_cargo_example_names() { 475 if [[ -d examples ]]; then 476 local -a files=(${(@f)$(echo examples/*.rs(:t:r))}) 477 _values 'example' "${files[@]}" 478 fi 479} 480 481_cargo 482