diff --git a/src/bin/cargo/main.rs b/src/bin/cargo/main.rs index 5dbf9363460d..12c3e0735e19 100644 --- a/src/bin/cargo/main.rs +++ b/src/bin/cargo/main.rs @@ -1,5 +1,6 @@ #![allow(clippy::self_named_module_files)] // false positive in `commands/build.rs` +use cargo::core::features; use cargo::core::shell::Shell; use cargo::util::network::http::http_handle; use cargo::util::network::http::needs_custom_http_transport; @@ -28,6 +29,13 @@ fn main() { } }; + let nightly_features_allowed = matches!(&*features::channel(), "nightly" | "dev"); + if nightly_features_allowed { + clap_complete::CompleteEnv::with_factory(|| cli::cli(&mut gctx)) + .var("CARGO_COMPLETE") + .complete(); + } + let result = if let Some(lock_addr) = cargo::ops::fix_get_proxy_lock_addr() { cargo::ops::fix_exec_rustc(&gctx, &lock_addr).map_err(|e| CliError::from(e)) } else { diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index ce7f2cb6dd21..604f1fdd6d90 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -116,6 +116,7 @@ Each new feature described below should explain how to use it. * [gitoxide](#gitoxide) --- Use `gitoxide` instead of `git2` for a set of operations. * [script](#script) --- Enable support for single-file `.rs` packages. * [lockfile-path](#lockfile-path) --- Allows to specify a path to lockfile other than the default path `/Cargo.lock`. + * [native-completions](#native-completion) --- Move cargo shell completions to native completions ## allow-features @@ -1643,6 +1644,27 @@ Example: cargo +nightly metadata --lockfile-path=$LOCKFILES_ROOT/my-project/Cargo.lock -Z unstable-options ``` +## native-completions +* Original Issue: [#6645](https://github.com/rust-lang/cargo/issues/6645) +* Tracking Issue: [#14520](https://github.com/rust-lang/cargo/issues/14520) + +This feature moves the handwritten completion scripts to Rust native, making it +easier for us to add, extend and test new completions. This feature is enabled with the +nightly channel, without requiring additional `-Z` options. + +### How to use native-completions feature: +- bash: + Add `source <(CARGO_COMPLETE=bash cargo)` to your .bashrc. + +- zsh: + Add `source <(CARGO_COMPLETE=zsh cargo)` to your .zshrc. + +- fish: + Add `source (CARGO_COMPLETE=fish cargo | psub)` to `$XDG_CONFIG_HOME/fish/completions/cargo.fish` + +- elvish: + Add `eval (E:CARGO_COMPLETE=elvish cargo | slurp)` to `$XDG_CONFIG_HOME/elvish/rc.elv` + # Stabilized and removed features ## Compile progress diff --git a/tests/testsuite/shell_completions/mod.rs b/tests/testsuite/shell_completions/mod.rs index 4cb896ab87d2..22c0a35144d5 100644 --- a/tests/testsuite/shell_completions/mod.rs +++ b/tests/testsuite/shell_completions/mod.rs @@ -1,2 +1,3 @@ pub mod common; +mod register; diff --git a/tests/testsuite/shell_completions/register.rs b/tests/testsuite/shell_completions/register.rs new file mode 100644 index 000000000000..7fe69f18c3e2 --- /dev/null +++ b/tests/testsuite/shell_completions/register.rs @@ -0,0 +1,130 @@ +use cargo_test_support::cargo_test; +use snapbox::assert_data_eq; + +use crate::shell_completions; + +#[cargo_test] +fn bash() { + let input = "cargo \t\t"; + let expected = snapbox::str![ + "% +--version --help check install read-manifest update +--list -V clean locate-project remove vendor +--explain -v config login report verify-project +--verbose -q doc logout run version +--quiet -C fetch metadata rustc yank +--color -Z fix new rustdoc +--locked -h generate-lockfile owner search +--offline add help package test +--frozen bench info pkgid tree +--config build init publish uninstall " + ]; + let actual = shell_completions::common::complete(input, "bash"); + assert_data_eq!(actual, expected); +} + +#[cargo_test] +fn elvish() { + let input = "cargo \t\t"; + let expected = snapbox::str![ + "% cargo +COMPLETING argument +--color --version check install read-manifest update +--config -C clean locate-project remove vendor +--explain -V config login report verify-project +--frozen -Z doc logout run version +--help -h fetch metadata rustc yank +--list -q fix new rustdoc +--locked -v generate-lockfile owner search +--offline add help package test +--quiet bench info pkgid tree +--verbose build init publish uninstall " + ]; + let actual = shell_completions::common::complete(input, "elvish"); + assert_data_eq!(actual, expected); +} + +#[cargo_test] +fn fish() { + let input = "cargo \t\t"; + let expected = snapbox::str![ + "% cargo +--version (Print version info and exit) +--list (List installed commands) +--explain (Provide a detailed explanation of a rustc error message) +--verbose (Use verbose output (-vv very verbose/build.rs output)) +--quiet (Do not print cargo log messages) +--color (Coloring: auto, always, never) +--locked (Assert that `Cargo.lock` will remain unchanged) +--offline (Run without accessing the network) +--frozen (Equivalent to specifying both --locked and --offline) +--config (Override a configuration value) +--help (Print help) +-V (Print version info and exit) +-v (Use verbose output (-vv very verbose/build.rs output)) +-q (Do not print cargo log messages) +-C (Change to DIRECTORY before doing anything (nightly-only)) +-Z (Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details) +-h (Print help) +add (Add dependencies to a Cargo.toml manifest file) +bench (Execute all benchmarks of a local package) +build (Compile a local package and all of its dependencies) +check (Check a local package and all of its dependencies for errors) +clean (Remove artifacts that cargo has generated in the past) +config (Inspect configuration values) +doc (Build a package's documentation) +fetch (Fetch dependencies of a package from the network) +fix (Automatically fix lint warnings reported by rustc) +generate-lockfile (Generate the lockfile for a package) +help (Displays help for a cargo subcommand) +info (Display information about a package in the registry) +init (Create a new cargo package in an existing directory) +install (Install a Rust binary) +locate-project (Print a JSON representation of a Cargo.toml file's location) +login (Log in to a registry.) +logout (Remove an API token from the registry locally) +metadata (Output the resolved dependencies of a package, the concrete used versions including overrides, in machine-r…) +new (Create a new cargo package at ) +owner (Manage the owners of a crate on the registry) +package (Assemble the local package into a distributable tarball) +pkgid (Print a fully qualified package specification) +publish (Upload a package to the registry) +read-manifest (Print a JSON representation of a Cargo.toml manifest.) +remove (Remove dependencies from a Cargo.toml manifest file) +report (Generate and display various kinds of reports) +run (Run a binary or example of the local package) +rustc (Compile a package, and pass extra options to the compiler) +rustdoc (Build a package's documentation, using specified custom flags.) +search (Search packages in the registry. Default registry is crates.io) +test (Execute all unit and integration tests and build examples of a local package) +tree (Display a tree visualization of a dependency graph) +uninstall (Remove a Rust binary) +update (Update dependencies as recorded in the local lock file) +vendor (Vendor all dependencies for a project locally) +verify-project (Check correctness of crate manifest) +version (Show version information) +yank (Remove a pushed crate from the index)"]; + + let actual = shell_completions::common::complete(input, "fish"); + assert_data_eq!(actual, expected); +} + +#[cargo_test] +fn test_register_native_completions() { + let input = "cargo \t\t"; + let expected = snapbox::str![ + "% cargo +--color --version check install read-manifest update +--config -C clean locate-project remove vendor +--explain -V config login report verify-project +--frozen -Z doc logout run version +--help -h fetch metadata rustc yank +--list -q fix new rustdoc +--locked -v generate-lockfile owner search +--offline add help package test +--quiet bench info pkgid tree +--verbose build init publish uninstall " + ]; + let actual = shell_completions::common::complete(input, "zsh"); + assert_data_eq!(actual, expected); +}