Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add custom completer for completing cargo build --packge <TAB> #14553

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions src/cargo/util/command_prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub mod heading {
pub trait CommandExt: Sized {
fn _arg(self, arg: Arg) -> Self;

fn _name(&self) -> String;

/// Do not use this method, it is only for backwards compatibility.
/// Use `arg_package_spec_no_all` instead.
fn arg_package_spec(
Expand Down Expand Up @@ -87,10 +89,18 @@ pub trait CommandExt: Sized {
}

fn arg_package_spec_simple(self, package: &'static str) -> Self {
let name = self._name();
self._arg(
optional_multi_opt("package", "SPEC", package)
.short('p')
.help_heading(heading::PACKAGE_SELECTION),
.help_heading(heading::PACKAGE_SELECTION)
.add(clap_complete::ArgValueCandidates::new(move || {
if ["build", "tree"].contains(&name.as_str()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is subtle and going to be very confusing. I wonder if we should better organize the arguments by what kind of package is being requested

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While implementing the completion, I discovered that parameters with the same name require different values under different subcommands. To minimize disruption to the original code, I added the _name interface, which allows the subcommand being used to be determined at compile time. Is there a better way to determine what kind of package is being requested?

get_ws_member_candidates()
} else {
vec![]
}
})),
)
}

Expand All @@ -99,7 +109,10 @@ pub trait CommandExt: Sized {
optional_opt("package", package)
.short('p')
.value_name("SPEC")
.help_heading(heading::PACKAGE_SELECTION),
.help_heading(heading::PACKAGE_SELECTION)
.add(clap_complete::ArgValueCandidates::new(
get_ws_member_candidates,
)),
epage marked this conversation as resolved.
Show resolved Hide resolved
)
}

Expand Down Expand Up @@ -480,6 +493,10 @@ impl CommandExt for Command {
fn _arg(self, arg: Arg) -> Self {
self.arg(arg)
}

fn _name(&self) -> String {
self.get_name().to_string()
}
}

pub fn flag(name: &'static str, help: &'static str) -> Arg {
Expand Down Expand Up @@ -1298,6 +1315,31 @@ fn get_packages() -> CargoResult<Vec<Package>> {
Ok(packages)
}

fn get_ws_member_candidates() -> Vec<clap_complete::CompletionCandidate> {
get_ws_member_packages()
.unwrap_or_default()
.into_iter()
.map(|pkg| {
clap_complete::CompletionCandidate::new(pkg.name().as_str()).help(
pkg.manifest()
.metadata()
.description
.to_owned()
.map(From::from),
)
})
.collect::<Vec<_>>()
}

fn get_ws_member_packages() -> CargoResult<Vec<Package>> {
let gctx = new_gctx_for_completions()?;
let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;

let packages = ws.members().map(|pkg| pkg.to_owned()).collect::<Vec<_>>();

Ok(packages)
}

fn new_gctx_for_completions() -> CargoResult<GlobalContext> {
let cwd = std::env::current_dir()?;
let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?);
Expand Down