From 8371c20a7ffde31b2525842e54577a4dcf9d98c9 Mon Sep 17 00:00:00 2001 From: Ricardo Silva Veloso Date: Fri, 1 Jun 2018 17:43:10 -0300 Subject: [PATCH 01/13] Suggest home directory for bash completion --- src/cli/help.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cli/help.rs b/src/cli/help.rs index 0a781d2514..1bb6e03f9e 100644 --- a/src/cli/help.rs +++ b/src/cli/help.rs @@ -164,10 +164,12 @@ r"DISCUSSION: BASH: - Completion files are commonly stored in `/etc/bash_completion.d/`. + Completion files are commonly stored in `/etc/bash_completion.d/` for + system-wide commands, but can be stored in in + `~/.local/share/bash_completion/completions` for user-specific commands. Run the command: - $ rustup completions bash > /etc/bash_completion.d/rustup.bash-completion + $ rustup completions bash >> ~/.local/share/bash_completion/completions/rustup This installs the completion script. You may have to log out and log back in to your shell session for the changes to take affect. From 147f46d3331a31fa7406cce8a76146b91ac61eff Mon Sep 17 00:00:00 2001 From: Ricardo Silva Veloso Date: Fri, 1 Jun 2018 20:31:43 -0300 Subject: [PATCH 02/13] Include shell completion for Cargo --- src/cli/rustup_mode.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index aa9d4fa48b..9f44b97afd 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -85,11 +85,34 @@ pub fn main() -> Result<()> { }, ("completions", Some(c)) => { if let Some(shell) = c.value_of("shell") { + let shell = shell.parse::().unwrap(); + let prefix = "~/.rustup/toolchains/$(rustup toolchain list --default)"; + cli().gen_completions_to( "rustup", - shell.parse::().unwrap(), + shell, &mut io::stdout(), ); + + match shell { + Shell::Bash => { + writeln!( + &mut io::stdout(), + "\n. {}{}", + prefix, + "/etc/bash_completion.d/cargo" + ); + } + Shell::Zsh => { + writeln!( + &mut io::stdout(), + "\n. {}{}", + prefix, + "/share/zsh/site-functions/_cargo" + ); + } + _ => (), + }; } } (_, _) => unreachable!(), From 50c0f3ef3eb46d61dce707393b5e113478998861 Mon Sep 17 00:00:00 2001 From: Ricardo Silva Veloso Date: Fri, 1 Jun 2018 20:49:54 -0300 Subject: [PATCH 03/13] Update the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ed933ed1d..e085e94137 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ but the gist is as simple as using one of the following: ``` # Bash -$ rustup completions bash > /etc/bash_completion.d/rustup.bash-completion +$ rustup completions bash > ~/.local/share/bash_completion/completions/rustup # Bash (macOS/Homebrew) $ rustup completions bash > $(brew --prefix)/etc/bash_completion.d/rustup.bash-completion From 4ab5ff2b0d4f07fd12253b9664483b603c6d1bad Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Thu, 7 Feb 2019 02:58:41 -0500 Subject: [PATCH 04/13] Move completion command handler to separate function Signed-off-by: Gabriel Smith --- src/cli/rustup_mode.rs | 60 ++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 9f44b97afd..b385fb9d49 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -85,34 +85,7 @@ pub fn main() -> Result<()> { }, ("completions", Some(c)) => { if let Some(shell) = c.value_of("shell") { - let shell = shell.parse::().unwrap(); - let prefix = "~/.rustup/toolchains/$(rustup toolchain list --default)"; - - cli().gen_completions_to( - "rustup", - shell, - &mut io::stdout(), - ); - - match shell { - Shell::Bash => { - writeln!( - &mut io::stdout(), - "\n. {}{}", - prefix, - "/etc/bash_completion.d/cargo" - ); - } - Shell::Zsh => { - writeln!( - &mut io::stdout(), - "\n. {}{}", - prefix, - "/share/zsh/site-functions/_cargo" - ); - } - _ => (), - }; + output_completion_script(shell.parse::().unwrap())?; } } (_, _) => unreachable!(), @@ -1073,3 +1046,34 @@ fn set_default_host_triple(cfg: &Cfg, m: &ArgMatches<'_>) -> Result<()> { cfg.set_default_host_triple(m.value_of("host_triple").expect(""))?; Ok(()) } + +fn output_completion_script(shell: Shell) -> Result<()> { + cli().gen_completions_to( + "rustup", + shell, + &mut io::stdout(), + ); + + let prefix = "$(rustc --print sysroot)"; + match shell { + Shell::Bash => { + writeln!( + &mut io::stdout(), + "{}{}", + prefix, + "/etc/bash_completion.d/cargo" + )?; + } + Shell::Zsh => { + writeln!( + &mut io::stdout(), + "{}{}", + prefix, + "/share/zsh/site-functions/_cargo" + )?; + } + _ => {} + }; + + Ok(()) +} From e8ade2cae8d60b6f1185029549564469df9e31db Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Thu, 7 Feb 2019 03:01:25 -0500 Subject: [PATCH 05/13] Add optional "command" argument to rustup completion This "command" argument allows rustup to output the completion information of more than just rustup. In this case it adds a cargo completion script. Signed-off-by: Gabriel Smith --- src/cli/rustup_mode.rs | 197 ++++++++++++++++++++++++++++++----------- 1 file changed, 143 insertions(+), 54 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index b385fb9d49..f5b1734938 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -13,6 +13,7 @@ use std::io::{self, Write}; use std::iter; use std::path::Path; use std::process::{self, Command}; +use std::str::FromStr; fn handle_epipe(res: Result<()>) -> Result<()> { match res { @@ -85,7 +86,12 @@ pub fn main() -> Result<()> { }, ("completions", Some(c)) => { if let Some(shell) = c.value_of("shell") { - output_completion_script(shell.parse::().unwrap())?; + output_completion_script( + shell.parse::().unwrap(), + c.value_of("command") + .and_then(|cmd| cmd.parse::().ok()) + .unwrap_or(CompletionCommand::Rustup), + )?; } } (_, _) => unreachable!(), @@ -438,40 +444,98 @@ pub fn cli() -> App<'static, 'static> { ); } + app = app + .subcommand( + SubCommand::with_name("self") + .about("Modify the rustup installation") + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("update").about("Download and install updates to rustup"), + ) + .subcommand( + SubCommand::with_name("uninstall") + .about("Uninstall rustup.") + .arg(Arg::with_name("no-prompt").short("y")), + ) + .subcommand( + SubCommand::with_name("upgrade-data") + .about("Upgrade the internal data format."), + ), + ) + .subcommand( + SubCommand::with_name("set") + .about("Alter rustup settings") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("default-host") + .about("The triple used to identify toolchains when not specified") + .arg(Arg::with_name("host_triple").required(true)), + ), + ) + .subcommand( + SubCommand::with_name("self") + .about("Modify the rustup installation") + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("update").about("Download and install updates to rustup"), + ) + .subcommand( + SubCommand::with_name("uninstall") + .about("Uninstall rustup.") + .arg(Arg::with_name("no-prompt").short("y")), + ) + .subcommand( + SubCommand::with_name("upgrade-data") + .about("Upgrade the internal data format."), + ), + ) + .subcommand( + SubCommand::with_name("telemetry") + .about("rustup telemetry commands") + .setting(AppSettings::Hidden) + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(SubCommand::with_name("enable").about("Enable rustup telemetry")) + .subcommand(SubCommand::with_name("disable").about("Disable rustup telemetry")) + .subcommand(SubCommand::with_name("analyze").about("Analyze stored telemetry")), + ) + .subcommand( + SubCommand::with_name("set") + .about("Alter rustup settings") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("default-host") + .about("The triple used to identify toolchains when not specified") + .arg(Arg::with_name("host_triple").required(true)), + ), + ); + + // Clap provides no good way to say that help should be printed in all + // cases where an argument without a default is not provided. The following + // creates lists out all the conditions where the "shell" argument are + // provided and give the default of "rustup". This way if "shell" is not + // provided then the help will still be printed. + let completion_defaults = Shell::variants() + .iter() + .map(|&shell| ("shell", Some(shell), "rustup")) + .collect::>(); + app.subcommand( - SubCommand::with_name("self") - .about("Modify the rustup installation") - .setting(AppSettings::VersionlessSubcommands) - .setting(AppSettings::DeriveDisplayOrder) - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand( - SubCommand::with_name("update").about("Download and install updates to rustup"), - ) - .subcommand( - SubCommand::with_name("uninstall") - .about("Uninstall rustup.") - .arg(Arg::with_name("no-prompt").short("y")), - ) - .subcommand( - SubCommand::with_name("upgrade-data").about("Upgrade the internal data format."), - ), - ) - .subcommand( - SubCommand::with_name("set") - .about("Alter rustup settings") - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand( - SubCommand::with_name("default-host") - .about("The triple used to identify toolchains when not specified") - .arg(Arg::with_name("host_triple").required(true)), - ), - ) - .subcommand( SubCommand::with_name("completions") .about("Generate completion scripts for your shell") .after_help(COMPLETIONS_HELP) .setting(AppSettings::ArgRequiredElseHelp) - .arg(Arg::with_name("shell").possible_values(&Shell::variants())), + .arg(Arg::with_name("shell").possible_values(&Shell::variants())) + .arg( + Arg::with_name("command") + .possible_values(&CompletionCommand::variants()) + .default_value_ifs(&completion_defaults[..]), + ), ) } @@ -1047,33 +1111,58 @@ fn set_default_host_triple(cfg: &Cfg, m: &ArgMatches<'_>) -> Result<()> { Ok(()) } -fn output_completion_script(shell: Shell) -> Result<()> { - cli().gen_completions_to( - "rustup", - shell, - &mut io::stdout(), - ); +#[derive(Copy, Clone, Debug)] +enum CompletionCommand { + Rustup, + Cargo, +} - let prefix = "$(rustc --print sysroot)"; - match shell { - Shell::Bash => { - writeln!( - &mut io::stdout(), - "{}{}", - prefix, - "/etc/bash_completion.d/cargo" - )?; +impl CompletionCommand { + fn variants() -> [&'static str; 2] { + ["rustup", "cargo"] + } +} + +impl FromStr for CompletionCommand { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s { + _ if s.eq_ignore_ascii_case("rustup") => Ok(CompletionCommand::Rustup), + _ if s.eq_ignore_ascii_case("cargo") => Ok(CompletionCommand::Cargo), + _ => Err(String::from("[valid values: rustup, cargo]")), } - Shell::Zsh => { - writeln!( - &mut io::stdout(), - "{}{}", - prefix, - "/share/zsh/site-functions/_cargo" - )?; + } +} + +fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result<()> { + match command { + CompletionCommand::Rustup => { + cli().gen_completions_to("rustup", shell, &mut io::stdout()); } - _ => {} - }; + CompletionCommand::Cargo => { + let prefix = "$(rustc --print sysroot)"; + match shell { + Shell::Bash => { + writeln!( + &mut io::stdout(), + "{}{}", + prefix, + "/etc/bash_completion.d/cargo" + )?; + } + Shell::Zsh => { + writeln!( + &mut io::stdout(), + "{}{}", + prefix, + "/share/zsh/site-functions/_cargo" + )?; + } + _ => {} + }; + } + } Ok(()) } From 634eba572130e767cbe0f6faad315eac8fd1ea8b Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Fri, 8 Feb 2019 11:21:49 -0500 Subject: [PATCH 06/13] For cargo completion script, output line to source real script This allows the cargo script for the current default toolchain to be called, and if the script is ever updated the user won't have to maually update their completion script after updating Rust. Signed-off-by: Gabriel Smith --- src/cli/rustup_mode.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index f5b1734938..6218ef37c3 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -1141,26 +1141,19 @@ fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result< cli().gen_completions_to("rustup", shell, &mut io::stdout()); } CompletionCommand::Cargo => { - let prefix = "$(rustc --print sysroot)"; - match shell { - Shell::Bash => { - writeln!( - &mut io::stdout(), - "{}{}", - prefix, - "/etc/bash_completion.d/cargo" - )?; - } - Shell::Zsh => { - writeln!( - &mut io::stdout(), - "{}{}", - prefix, - "/share/zsh/site-functions/_cargo" - )?; - } - _ => {} + let script = match shell { + Shell::Bash => Some("/etc/bash_completion.d/cargo"), + Shell::Zsh => Some("/share/zsh/site-functions/_cargo"), + _ => None, }; + + if let Some(script) = script { + writeln!( + &mut io::stdout(), + "source $(rustc --print sysroot){}", + script, + )?; + } } } From dc6baebf733844425d740ca4aa476865674d0b96 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Fri, 8 Feb 2019 11:33:30 -0500 Subject: [PATCH 07/13] Add information to the help output about bash completions Signed-off-by: Gabriel Smith --- src/cli/help.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/cli/help.rs b/src/cli/help.rs index 1bb6e03f9e..f2a71b5189 100644 --- a/src/cli/help.rs +++ b/src/cli/help.rs @@ -251,7 +251,22 @@ r"DISCUSSION: into a separate file and source it inside our profile. To save the completions into our profile simply use - PS C:\> rustup completions powershell >> ${env:USERPROFILE}\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"; + PS C:\> rustup completions powershell >> ${env:USERPROFILE}\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 + + CARGO: + + Rustup can also generate a completion script for `cargo`. The script output + by `rustup` will source the completion script distributed with your default + toolchain. Not all shells are currently supported. Here are examples for + the currently supported shells. + + BASH: + + $ rustup completions bash cargo >> ~/.local/share/bash_completion/completions/cargo + + ZSH: + + $ rustup completions zsh cargo > ~/.zfunc/_cargo"; pub static TOOLCHAIN_ARG_HELP: &'static str = "Toolchain name, such as 'stable', 'nightly', \ or '1.8.0'. For more information see `rustup \ From af17f186e7d4b1c99d8b3bc20ef8f5bd6c963223 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 16 Mar 2019 15:24:14 -0400 Subject: [PATCH 08/13] Print an error message for unsupported cargo completions --- src/cli/rustup_mode.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 6218ef37c3..2fb47545e7 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -1153,6 +1153,12 @@ fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result< "source $(rustc --print sysroot){}", script, )?; + } else { + writeln!( + &mut io::stderr(), + "Cargo does not currently support completions for {}.", + shell, + )?; } } } From 4a4b26067e9c8f67c9677fc99c223fc1e60a0752 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 16 Mar 2019 15:37:03 -0400 Subject: [PATCH 09/13] Use term2 for completion script outputs --- src/cli/rustup_mode.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 2fb47545e7..5d5f158dc9 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -9,7 +9,7 @@ use rustup::dist::manifest::Component; use rustup::utils::utils::{self, ExitCode}; use rustup::{command, Cfg, Toolchain}; use std::error::Error; -use std::io::{self, Write}; +use std::io::Write; use std::iter; use std::path::Path; use std::process::{self, Command}; @@ -1138,7 +1138,7 @@ impl FromStr for CompletionCommand { fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result<()> { match command { CompletionCommand::Rustup => { - cli().gen_completions_to("rustup", shell, &mut io::stdout()); + cli().gen_completions_to("rustup", shell, &mut term2::stdout()); } CompletionCommand::Cargo => { let script = match shell { @@ -1149,13 +1149,13 @@ fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result< if let Some(script) = script { writeln!( - &mut io::stdout(), + &mut term2::stdout(), "source $(rustc --print sysroot){}", script, )?; } else { writeln!( - &mut io::stderr(), + &mut term2::stderr(), "Cargo does not currently support completions for {}.", shell, )?; From 02d63fcf27c18b58c89e43ae81176833857a6a43 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 16 Mar 2019 16:03:40 -0400 Subject: [PATCH 10/13] Hold all programs with completions in an array This removes the duplication between the `variants` and `from_str` implementations. --- src/cli/rustup_mode.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 5d5f158dc9..552f5035e3 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -1117,9 +1117,14 @@ enum CompletionCommand { Cargo, } +static COMPLETIONS: &[(&'static str, CompletionCommand)] = &[ + ("rustup", CompletionCommand::Rustup), + ("cargo", CompletionCommand::Cargo), +]; + impl CompletionCommand { - fn variants() -> [&'static str; 2] { - ["rustup", "cargo"] + fn variants() -> Vec<&'static str> { + COMPLETIONS.iter().map(|&(s, _)| s).collect::>() } } @@ -1127,10 +1132,13 @@ impl FromStr for CompletionCommand { type Err = String; fn from_str(s: &str) -> std::result::Result { - match s { - _ if s.eq_ignore_ascii_case("rustup") => Ok(CompletionCommand::Rustup), - _ if s.eq_ignore_ascii_case("cargo") => Ok(CompletionCommand::Cargo), - _ => Err(String::from("[valid values: rustup, cargo]")), + match COMPLETIONS + .iter() + .filter(|&(val, _)| val.eq_ignore_ascii_case(s)) + .next() + { + Some(&(_, cmd)) => Ok(cmd), + None => Err(String::from("[valid values: rustup, cargo]")), } } } From 8a189faab0400c99f3454a95c00ec4ca2fa732cc Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 30 Mar 2019 02:55:37 -0400 Subject: [PATCH 11/13] cli: Rework completion error handling Moves the error reporting from the completion function to the standard error handling mechanism. CompletionCommand had to be made fully public, not that this matters for a binary target. That said, this could have been prevented by transforming the CompletionCommand into its String form at the error site rather than at the reporting site. It was chosen to go with this method as stringly-typed interfaces are generally bad. --- src/cli/errors.rs | 7 ++++++ src/cli/rustup_mode.rs | 49 ++++++++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/cli/errors.rs b/src/cli/errors.rs index ee1d29b327..a031dfe608 100644 --- a/src/cli/errors.rs +++ b/src/cli/errors.rs @@ -1,8 +1,11 @@ #![allow(dead_code)] +use crate::rustup_mode::CompletionCommand; + use std::io; use std::path::PathBuf; +use clap::Shell; use error_chain::error_chain; use error_chain::error_chain_processing; use error_chain::{impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace}; @@ -46,5 +49,9 @@ error_chain! { WindowsUninstallMadness { description("failure during windows uninstall") } + UnsupportedCompletionShell(shell: Shell, cmd: CompletionCommand) { + description("completion script for shell not yet supported for tool") + display("{} does not currently support completions for {}", cmd, shell) + } } } diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 552f5035e3..5fbe954593 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -9,6 +9,7 @@ use rustup::dist::manifest::Component; use rustup::utils::utils::{self, ExitCode}; use rustup::{command, Cfg, Toolchain}; use std::error::Error; +use std::fmt; use std::io::Write; use std::iter; use std::path::Path; @@ -1111,8 +1112,8 @@ fn set_default_host_triple(cfg: &Cfg, m: &ArgMatches<'_>) -> Result<()> { Ok(()) } -#[derive(Copy, Clone, Debug)] -enum CompletionCommand { +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum CompletionCommand { Rustup, Cargo, } @@ -1138,7 +1139,25 @@ impl FromStr for CompletionCommand { .next() { Some(&(_, cmd)) => Ok(cmd), - None => Err(String::from("[valid values: rustup, cargo]")), + None => { + let completion_options = COMPLETIONS + .iter() + .map(|&(v, _)| v) + .fold("".to_owned(), |s, v| format!("{}{}, ", s, v)); + Err(format!( + "[valid values: {}]", + completion_options.trim_end_matches(", ") + )) + } + } + } +} + +impl fmt::Display for CompletionCommand { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match COMPLETIONS.iter().filter(|&(_, cmd)| cmd == self).next() { + Some(&(val, _)) => write!(f, "{}", val), + None => unreachable!(), } } } @@ -1150,24 +1169,16 @@ fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result< } CompletionCommand::Cargo => { let script = match shell { - Shell::Bash => Some("/etc/bash_completion.d/cargo"), - Shell::Zsh => Some("/share/zsh/site-functions/_cargo"), - _ => None, + Shell::Bash => "/etc/bash_completion.d/cargo", + Shell::Zsh => "/share/zsh/site-functions/_cargo", + _ => return Err(ErrorKind::UnsupportedCompletionShell(shell, command).into()), }; - if let Some(script) = script { - writeln!( - &mut term2::stdout(), - "source $(rustc --print sysroot){}", - script, - )?; - } else { - writeln!( - &mut term2::stderr(), - "Cargo does not currently support completions for {}.", - shell, - )?; - } + writeln!( + &mut term2::stdout(), + "source $(rustc --print sysroot){}", + script, + )?; } } From 9bb56d08df1939941368073096de8c421200f137 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 30 Mar 2019 03:01:04 -0400 Subject: [PATCH 12/13] test: mock: Add expect_ok_eq function Compares two different command invocations and panics if their return code and stdout/stderr don't match. --- tests/mock/clitools.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/mock/clitools.rs b/tests/mock/clitools.rs index 67ab674482..29a28b9084 100644 --- a/tests/mock/clitools.rs +++ b/tests/mock/clitools.rs @@ -253,6 +253,18 @@ pub fn expect_ok_contains(config: &Config, args: &[&str], stdout: &str, stderr: } } +pub fn expect_ok_eq(config: &Config, args1: &[&str], args2: &[&str]) { + let out1 = run(config, args1[0], &args1[1..], &[]); + let out2 = run(config, args2[0], &args2[1..], &[]); + if !out1.ok || !out2.ok || out1.stdout != out2.stdout || out1.stderr != out2.stderr { + print_command(args1, &out1); + println!("expected.ok: {}", true); + print_command(args2, &out2); + println!("expected.ok: {}", true); + panic!(); + } +} + fn print_command(args: &[&str], out: &SanitizedOutput) { print!("\n>"); for arg in args { From 0312b11cfe440b5d205799cfb1469b5088a12b80 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 30 Mar 2019 03:02:49 -0400 Subject: [PATCH 13/13] test: Added tests for shell completion script support --- tests/cli-misc.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/tests/cli-misc.rs b/tests/cli-misc.rs index d28df66c9d..f6e01992db 100644 --- a/tests/cli-misc.rs +++ b/tests/cli-misc.rs @@ -4,8 +4,8 @@ pub mod mock; use crate::mock::clitools::{ - self, expect_err, expect_ok, expect_ok_ex, expect_stderr_ok, expect_stdout_ok, run, - set_current_dist_date, this_host_triple, Config, Scenario, + self, expect_err, expect_ok, expect_ok_eq, expect_ok_ex, expect_stderr_ok, expect_stdout_ok, + run, set_current_dist_date, this_host_triple, Config, Scenario, }; use rustup::dist::errors::TOOLSTATE_MSG; use rustup::utils::{raw, utils}; @@ -789,3 +789,66 @@ fn update_unavailable_rustc() { expect_stdout_ok(config, &["rustc", "--version"], "hash-n-1"); }); } + +#[test] +fn completion_rustup() { + setup(&|config| { + expect_ok(config, &["rustup", "completions", "bash", "rustup"]); + }); +} + +#[test] +fn completion_cargo() { + setup(&|config| { + expect_ok(config, &["rustup", "completions", "bash", "cargo"]); + }); +} + +#[test] +fn completion_default() { + setup(&|config| { + expect_ok_eq( + config, + &["rustup", "completions", "bash"], + &["rustup", "completions", "bash", "rustup"], + ); + }); +} + +#[test] +fn completion_bad_shell() { + setup(&|config| { + expect_err( + config, + &["rustup", "completions", "fake"], + "error: 'fake' isn't a valid value for ''", + ); + expect_err( + config, + &["rustup", "completions", "fake", "cargo"], + "error: 'fake' isn't a valid value for ''", + ); + }); +} + +#[test] +fn completion_bad_tool() { + setup(&|config| { + expect_err( + config, + &["rustup", "completions", "bash", "fake"], + "error: 'fake' isn't a valid value for ''", + ); + }); +} + +#[test] +fn completion_cargo_unsupported_shell() { + setup(&|config| { + expect_err( + config, + &["rustup", "completions", "fish", "cargo"], + "error: cargo does not currently support completions for ", + ); + }); +}