diff --git a/src/bin/cargo/commands/add.rs b/src/bin/cargo/commands/add.rs index 263b29b1fac..1d2fb5febb4 100644 --- a/src/bin/cargo/commands/add.rs +++ b/src/bin/cargo/commands/add.rs @@ -146,7 +146,8 @@ This is the catch all, handling hashes to named references in remote repositorie .value_name("NAME") .help("Package registry for this dependency") .add(clap_complete::ArgValueCandidates::new(|| { - let candidates = get_registry_candidates(); + let cwd = std::env::current_dir(); + let candidates = get_registry_candidates(cwd.ok()); candidates.unwrap_or_default() })), ]) diff --git a/src/bin/cargo/commands/uninstall.rs b/src/bin/cargo/commands/uninstall.rs index d50996d9af6..3ea8bc23cd2 100644 --- a/src/bin/cargo/commands/uninstall.rs +++ b/src/bin/cargo/commands/uninstall.rs @@ -45,7 +45,7 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { Ok(()) } -fn get_installed_crates() -> Vec { +pub fn get_installed_crates() -> Vec { get_installed_crates_().unwrap_or_default() } diff --git a/src/bin/cargo/commands/update.rs b/src/bin/cargo/commands/update.rs index a1733a50487..740e51d1886 100644 --- a/src/bin/cargo/commands/update.rs +++ b/src/bin/cargo/commands/update.rs @@ -14,9 +14,10 @@ pub fn cli() -> Command { .help_heading(heading::PACKAGE_SELECTION) .group("package-group") .help("Package to update") - .add(clap_complete::ArgValueCandidates::new( - get_pkg_id_spec_candidates, - ))]) + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_pkg_id_spec_candidates(cwd.ok()) + }))]) .arg( optional_multi_opt("package", "SPEC", "Package to update") .short('p') diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 16e588cd480..4206e7064b6 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -22,6 +22,7 @@ use cargo_util_schemas::manifest::RegistryName; use cargo_util_schemas::manifest::StringOrVec; use clap::builder::UnknownArgumentValueParser; use home::cargo_home_with_cwd; +use itertools::Itertools; use semver::Version; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; @@ -163,13 +164,19 @@ pub trait CommandExt: Sized { ._arg( optional_multi_opt("test", "NAME", test) .help_heading(heading::TARGET_SELECTION) - .add(clap_complete::ArgValueCandidates::new(get_test_candidates)), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_test_candidates(cwd.ok()) + })), ) ._arg(flag("benches", benches).help_heading(heading::TARGET_SELECTION)) ._arg( optional_multi_opt("bench", "NAME", bench) .help_heading(heading::TARGET_SELECTION) - .add(clap_complete::ArgValueCandidates::new(get_bench_candidates)), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_bench_candidates(cwd.ok()) + })), ) ._arg(flag("all-targets", all).help_heading(heading::TARGET_SELECTION)) } @@ -187,15 +194,19 @@ pub trait CommandExt: Sized { ._arg( optional_multi_opt("bin", "NAME", bin) .help_heading(heading::TARGET_SELECTION) - .add(clap_complete::ArgValueCandidates::new(get_bin_candidates)), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_bin_candidates(cwd.ok()) + })), ) ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION)) ._arg( optional_multi_opt("example", "NAME", example) .help_heading(heading::TARGET_SELECTION) - .add(clap_complete::ArgValueCandidates::new( - get_example_candidates, - )), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_example_candidates(cwd.ok()) + })), ) } @@ -209,15 +220,19 @@ pub trait CommandExt: Sized { self._arg( optional_multi_opt("bin", "NAME", bin) .help_heading(heading::TARGET_SELECTION) - .add(clap_complete::ArgValueCandidates::new(get_bin_candidates)), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_bin_candidates(cwd.ok()) + })), ) ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION)) ._arg( optional_multi_opt("example", "NAME", example) .help_heading(heading::TARGET_SELECTION) - .add(clap_complete::ArgValueCandidates::new( - get_example_candidates, - )), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_example_candidates(cwd.ok()) + })), ) ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION)) } @@ -226,14 +241,18 @@ pub trait CommandExt: Sized { self._arg( optional_multi_opt("bin", "NAME", bin) .help_heading(heading::TARGET_SELECTION) - .add(clap_complete::ArgValueCandidates::new(get_bin_candidates)), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_bin_candidates(cwd.ok()) + })), ) ._arg( optional_multi_opt("example", "NAME", example) .help_heading(heading::TARGET_SELECTION) - .add(clap_complete::ArgValueCandidates::new( - get_example_candidates, - )), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_example_candidates(cwd.ok()) + })), ) } @@ -294,7 +313,10 @@ pub trait CommandExt: Sized { self._arg( optional_multi_opt("target", "TRIPLE", target) .help_heading(heading::COMPILATION_OPTIONS) - .add(clap_complete::ArgValueCandidates::new(get_target_triples)), + .add(clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_target_triples(cwd.ok()) + })), ) ._arg(unsupported_short_arg) } @@ -367,8 +389,12 @@ pub trait CommandExt: Sized { .value_parser(["git", "hg", "pijul", "fossil", "none"]), ) ._arg( - flag("bin", "Use a binary (application) template [default]") - .add(clap_complete::ArgValueCandidates::new(get_bin_candidates)), + flag("bin", "Use a binary (application) template [default]").add( + clap_complete::ArgValueCandidates::new(|| { + let cwd = std::env::current_dir(); + get_bin_candidates(cwd.ok()) + }), + ), ) ._arg(flag("lib", "Use a library template")) ._arg( @@ -388,7 +414,8 @@ pub trait CommandExt: Sized { fn arg_registry(self, help: &'static str) -> Self { self._arg(opt("registry", help).value_name("REGISTRY").add( clap_complete::ArgValueCandidates::new(|| { - let candidates = get_registry_candidates(); + let cwd = std::env::current_dir(); + let candidates = get_registry_candidates(cwd.ok()); candidates.unwrap_or_default() }), )) @@ -1068,14 +1095,17 @@ pub fn lockfile_path( return Ok(Some(path)); } -pub fn get_registry_candidates() -> CargoResult> { - let gctx = new_gctx_for_completions()?; +pub fn get_registry_candidates( + cwd: Option, +) -> CargoResult> { + let gctx = new_gctx_for_completions(cwd)?; if let Ok(Some(registries)) = gctx.get::>>>("registries") { Ok(registries .keys() + .sorted() .map(|name| clap_complete::CompletionCandidate::new(name.to_owned())) .collect()) } else { @@ -1083,8 +1113,8 @@ pub fn get_registry_candidates() -> CargoResult Vec { - get_targets_from_metadata() +pub fn get_example_candidates(cwd: Option) -> Vec { + get_targets_from_metadata(cwd) .unwrap_or_default() .into_iter() .filter_map(|target| match target.kind() { @@ -1094,8 +1124,8 @@ fn get_example_candidates() -> Vec { .collect::>() } -fn get_bench_candidates() -> Vec { - get_targets_from_metadata() +pub fn get_bench_candidates(cwd: Option) -> Vec { + get_targets_from_metadata(cwd) .unwrap_or_default() .into_iter() .filter_map(|target| match target.kind() { @@ -1105,8 +1135,8 @@ fn get_bench_candidates() -> Vec { .collect::>() } -fn get_test_candidates() -> Vec { - get_targets_from_metadata() +pub fn get_test_candidates(cwd: Option) -> Vec { + get_targets_from_metadata(cwd) .unwrap_or_default() .into_iter() .filter_map(|target| match target.kind() { @@ -1116,8 +1146,8 @@ fn get_test_candidates() -> Vec { .collect::>() } -fn get_bin_candidates() -> Vec { - get_targets_from_metadata() +pub fn get_bin_candidates(cwd: Option) -> Vec { + get_targets_from_metadata(cwd) .unwrap_or_default() .into_iter() .filter_map(|target| match target.kind() { @@ -1127,10 +1157,9 @@ fn get_bin_candidates() -> Vec { .collect::>() } -fn get_targets_from_metadata() -> CargoResult> { - let cwd = std::env::current_dir()?; - let gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?); - let ws = Workspace::new(&find_root_manifest_for_wd(&cwd)?, &gctx)?; +fn get_targets_from_metadata(cwd: Option) -> CargoResult> { + let gctx = new_gctx_for_completions(cwd)?; + let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?; let packages = ws.members().collect::>(); @@ -1142,7 +1171,7 @@ fn get_targets_from_metadata() -> CargoResult> { Ok(targets) } -fn get_target_triples() -> Vec { +pub fn get_target_triples(cwd: Option) -> Vec { let mut candidates = Vec::new(); if let Ok(targets) = get_target_triples_from_rustup() { @@ -1150,7 +1179,7 @@ fn get_target_triples() -> Vec { } if candidates.is_empty() { - if let Ok(targets) = get_target_triples_from_rustc() { + if let Ok(targets) = get_target_triples_from_rustc(cwd) { candidates = targets; } } @@ -1182,10 +1211,11 @@ fn get_target_triples_from_rustup() -> CargoResult CargoResult> { - let cwd = std::env::current_dir()?; - let gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?); - let ws = Workspace::new(&find_root_manifest_for_wd(&PathBuf::from(&cwd))?, &gctx); +fn get_target_triples_from_rustc( + cwd: Option, +) -> CargoResult> { + let gctx = new_gctx_for_completions(cwd)?; + let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx); let rustc = gctx.load_global_rustc(ws.as_ref().ok())?; @@ -1198,12 +1228,12 @@ fn get_target_triples_from_rustc() -> CargoResult Vec { +pub fn get_pkg_id_spec_candidates(cwd: Option) -> Vec { let mut candidates = vec![]; let package_map = HashMap::<&str, Vec>::new(); let package_map = - get_packages() + get_packages(cwd) .unwrap_or_default() .into_iter() .fold(package_map, |mut map, package| { @@ -1285,8 +1315,8 @@ pub fn get_pkg_id_spec_candidates() -> Vec { candidates } -fn get_packages() -> CargoResult> { - let gctx = new_gctx_for_completions()?; +fn get_packages(cwd: Option) -> CargoResult> { + let gctx = new_gctx_for_completions(cwd)?; let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?; @@ -1318,8 +1348,8 @@ fn get_packages() -> CargoResult> { Ok(packages) } -fn new_gctx_for_completions() -> CargoResult { - let cwd = std::env::current_dir()?; +pub fn new_gctx_for_completions(cwd: Option) -> CargoResult { + let cwd = cwd.unwrap_or(std::env::current_dir()?); let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?); let verbose = 0; diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 9ca03d36789..3a8b3198236 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -167,6 +167,7 @@ mod rustflags; mod rustup; mod script; mod search; +mod shell_completions; mod shell_quoting; mod source_replacement; mod ssh; diff --git a/tests/testsuite/shell_completions/mod.rs b/tests/testsuite/shell_completions/mod.rs new file mode 100644 index 00000000000..e13a988fe50 --- /dev/null +++ b/tests/testsuite/shell_completions/mod.rs @@ -0,0 +1,90 @@ +use std::path::PathBuf; + +use cargo::util::command_prelude::*; +use cargo_test_support::cargo_test; + +#[cargo_test] +fn test_get_bin_candidates() { + let current_dir = std::env::current_dir().expect("Failed to get current directory"); + let cwd = PathBuf::from(file!()).parent().unwrap().join("template"); + let cwd = current_dir.join(cwd); + + let expected = snapbox::str![ + "bench_crate_1 +bench_crate_2 +template" + ]; + let actual = print_candidates(get_bin_candidates(Some(cwd))); + snapbox::assert_data_eq!(actual, expected); +} + +#[cargo_test] +fn test_get_bench_candidates() { + let current_dir = std::env::current_dir().expect("Failed to get current directory"); + let cwd = PathBuf::from(file!()).parent().unwrap().join("template"); + let cwd = current_dir.join(cwd); + + let expected = snapbox::str![ + "bench1 +bench2" + ]; + let actual = print_candidates(get_bench_candidates(Some(cwd))); + snapbox::assert_data_eq!(actual, expected); +} + +#[cargo_test] +fn test_get_test_candidates() { + let current_dir = std::env::current_dir().expect("Failed to get current directory"); + let cwd = PathBuf::from(file!()).parent().unwrap().join("template"); + let cwd = current_dir.join(cwd); + + let expected = snapbox::str![ + "test1 +test2" + ]; + let actual = print_candidates(get_test_candidates(Some(cwd))); + snapbox::assert_data_eq!(actual, expected); +} + +#[cargo_test] +fn test_get_example_candidates() { + let current_dir = std::env::current_dir().expect("Failed to get current directory"); + let cwd = PathBuf::from(file!()).parent().unwrap().join("template"); + let cwd = current_dir.join(cwd); + + let expected = snapbox::str![ + "example1 +example2" + ]; + let actual = print_candidates(get_example_candidates(Some(cwd))); + snapbox::assert_data_eq!(actual, expected); +} + +#[cargo_test] +fn test_get_registry_candidates() { + let current_dir = std::env::current_dir().expect("Failed to get current directory"); + let cwd = PathBuf::from(file!()).parent().unwrap().join("template"); + let cwd = current_dir.join(cwd); + + let expected = snapbox::str![ + "my-registry1 +my-registry2" + ]; + let actual = print_candidates(get_registry_candidates(Some(cwd)).unwrap()); + snapbox::assert_data_eq!(actual, expected); +} + +fn print_candidates(candidates: Vec) -> String { + candidates + .into_iter() + .map(|candidate| { + let compl = candidate.get_value().to_str().unwrap(); + if let Some(help) = candidate.get_help() { + format!("{compl}\t{help}") + } else { + compl.to_owned() + } + }) + .collect::>() + .join("\n") +} diff --git a/tests/testsuite/shell_completions/template/.cargo/config.toml b/tests/testsuite/shell_completions/template/.cargo/config.toml new file mode 100644 index 00000000000..9e44265d0b9 --- /dev/null +++ b/tests/testsuite/shell_completions/template/.cargo/config.toml @@ -0,0 +1,3 @@ +[registries] +my-registry1 = { index = "my-registry1"} +my-registry2 = { index = "my-registry2"} diff --git a/tests/testsuite/shell_completions/template/Cargo.toml b/tests/testsuite/shell_completions/template/Cargo.toml new file mode 100644 index 00000000000..7124c62b68c --- /dev/null +++ b/tests/testsuite/shell_completions/template/Cargo.toml @@ -0,0 +1,19 @@ +[workspace] +resolver = "2" +members = [ + "crates/*" +] + +[workspace.package] +rust-version = "1.81" +edition = "2021" + +[package] +name = "template" +version = "0.1.0" +edition.workspace = true +rust-version = "1.81" + +[lib] +name = "template" +path = "src/lib.rs" \ No newline at end of file diff --git a/tests/testsuite/shell_completions/template/benches/bench1.rs b/tests/testsuite/shell_completions/template/benches/bench1.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/shell_completions/template/benches/bench2.rs b/tests/testsuite/shell_completions/template/benches/bench2.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/shell_completions/template/crates/crate1/Cargo.toml b/tests/testsuite/shell_completions/template/crates/crate1/Cargo.toml new file mode 100644 index 00000000000..529a9219895 --- /dev/null +++ b/tests/testsuite/shell_completions/template/crates/crate1/Cargo.toml @@ -0,0 +1,2 @@ +[package] +name = "bench_crate_1" \ No newline at end of file diff --git a/tests/testsuite/shell_completions/template/crates/crate1/src/main.rs b/tests/testsuite/shell_completions/template/crates/crate1/src/main.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/shell_completions/template/crates/crate2/Cargo.toml b/tests/testsuite/shell_completions/template/crates/crate2/Cargo.toml new file mode 100644 index 00000000000..6552e5948af --- /dev/null +++ b/tests/testsuite/shell_completions/template/crates/crate2/Cargo.toml @@ -0,0 +1,2 @@ +[package] +name = "bench_crate_2" \ No newline at end of file diff --git a/tests/testsuite/shell_completions/template/crates/crate2/src/main.rs b/tests/testsuite/shell_completions/template/crates/crate2/src/main.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/shell_completions/template/examples/example1.rs b/tests/testsuite/shell_completions/template/examples/example1.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/testsuite/shell_completions/template/examples/example1.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/shell_completions/template/examples/example2.rs b/tests/testsuite/shell_completions/template/examples/example2.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/testsuite/shell_completions/template/examples/example2.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/shell_completions/template/src/lib.rs b/tests/testsuite/shell_completions/template/src/lib.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/testsuite/shell_completions/template/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/shell_completions/template/src/main.rs b/tests/testsuite/shell_completions/template/src/main.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/shell_completions/template/tests/test1.rs b/tests/testsuite/shell_completions/template/tests/test1.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/shell_completions/template/tests/test2.rs b/tests/testsuite/shell_completions/template/tests/test2.rs new file mode 100644 index 00000000000..e69de29bb2d