Skip to content

Commit

Permalink
feat: Add native completions with CompleteEnv and under the nightly…
Browse files Browse the repository at this point in the history
… features
  • Loading branch information
shannmu committed Sep 9, 2024
1 parent f25806c commit 7a1a235
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/bin/cargo/main.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
22 changes: 22 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Each new feature described below should explain how to use it.
* [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 `<workspace_root>/Cargo.lock`.
* [package-workspace](#package-workspace) --- Allows for packaging and publishing multiple crates in a workspace.
* [native-completions](#native-completions) --- Move cargo shell completions to native completions.

## allow-features

Expand Down Expand Up @@ -1686,6 +1687,27 @@ cargo +nightly -Zpackage-workspace --registry=my-registry package -p foo -p dep
cargo +nightly -Zpackage-workspace --index=https://example.com package -p foo -p dep
```

## 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
Expand Down
142 changes: 141 additions & 1 deletion tests/testsuite/shell_completions.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,150 @@
use cargo_test_support::cargo_test;
#[cfg(unix)]
use completest_pty::Runtime;
use snapbox::assert_data_eq;

#[cfg(unix)]
#[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 = complete(input, "bash");
assert_data_eq!(actual, expected);
}

#[cfg(unix)]
#[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 = complete(input, "elvish");
assert_data_eq!(actual, expected);
}

#[cfg(unix)]
#[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 <path>)
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 = complete(input, "fish");
assert_data_eq!(actual, expected);
}

#[cfg(unix)]
#[cargo_test]
fn zsh() {
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 = complete(input, "zsh");
assert_data_eq!(actual, expected);
}

fn complete(input: &str, shell: &str) -> String {
let shell = shell.into();

// Load the runtime
let mut runtime = match shell {
#[cfg(unix)]
"bash" => load_runtime::<completest_pty::BashRuntimeBuilder>("bash"),
#[cfg(unix)]
"elvish" => load_runtime::<completest_pty::ElvishRuntimeBuilder>("elvish"),
#[cfg(unix)]
"fish" => load_runtime::<completest_pty::FishRuntimeBuilder>("fish"),
#[cfg(unix)]
"zsh" => load_runtime::<completest_pty::ZshRuntimeBuilder>("zsh"),
_ => panic!("Unsupported shell: {}", shell),
};
Expand All @@ -32,15 +169,19 @@ where
let mut runtime = Box::new(R::new(bin_root, home).unwrap());

match shell {
#[cfg(unix)]
"bash" => runtime
.register("", "source <(CARGO_COMPLETE=bash cargo)")
.unwrap(),
#[cfg(unix)]
"elvish" => runtime
.register("", "eval (E:CARGO_COMPLETE=elvish cargo | slurp)")
.unwrap(),
#[cfg(unix)]
"fish" => runtime
.register("cargo", "source (CARGO_COMPLETE=fish cargo | psub)")
.unwrap(),
#[cfg(unix)]
"zsh" => runtime
.register(
"cargo",
Expand All @@ -53,4 +194,3 @@ source <(CARGO_COMPLETE=zsh cargo)",

runtime
}

0 comments on commit 7a1a235

Please sign in to comment.