mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 14:22:34 +00:00
Auto merge of #732 - Arnavion:fix-729, r=kbknapp
Completion support for PowerShell. Fixes #729 --- Requires PS 5.0+ for the `Microsoft.PowerShell.Core\Register-ArgumentCompleter` commandlet. Earlier versions using the equivalent commandlet from TabExpansionPlusPlus may work, but I haven't tested that. PS 5.0 is available for Windows 7 and non-Windows anyway. The resulting script can be used by pasting it into the PS profile or saving it to a file and sourcing it into the PS profile in the usual way.
This commit is contained in:
commit
fc22be0f04
4 changed files with 133 additions and 10 deletions
|
@ -119,10 +119,10 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
let out_dir = PathBuf::from(od);
|
||||
let name = &*self.meta.bin_name.as_ref().unwrap().clone();
|
||||
let file_name = match for_shell {
|
||||
|
||||
Shell::Bash => format!("{}.bash-completion", name),
|
||||
Shell::Fish => format!("{}.fish", name),
|
||||
Shell::Zsh => format!("_{}", name),
|
||||
Shell::PowerShell => format!("_{}.ps1", name),
|
||||
};
|
||||
|
||||
let mut file = match File::create(out_dir.join(file_name)) {
|
||||
|
|
|
@ -3,6 +3,7 @@ mod macros;
|
|||
mod bash;
|
||||
mod fish;
|
||||
mod zsh;
|
||||
mod powershell;
|
||||
mod shell;
|
||||
|
||||
// Std
|
||||
|
@ -13,6 +14,7 @@ use app::parser::Parser;
|
|||
use self::bash::BashGen;
|
||||
use self::fish::FishGen;
|
||||
use self::zsh::ZshGen;
|
||||
use self::powershell::PowerShellGen;
|
||||
pub use self::shell::Shell;
|
||||
|
||||
pub struct ComplGen<'a, 'b>
|
||||
|
@ -28,9 +30,10 @@ impl<'a, 'b> ComplGen<'a, 'b> {
|
|||
|
||||
pub fn generate<W: Write>(&self, for_shell: Shell, buf: &mut W) {
|
||||
match for_shell {
|
||||
Shell::Bash => BashGen::new(self.p).generate_to(buf),
|
||||
Shell::Fish => FishGen::new(self.p).generate_to(buf),
|
||||
Shell::Zsh => ZshGen::new(self.p).generate_to(buf),
|
||||
Shell::Bash => BashGen::new(self.p).generate_to(buf),
|
||||
Shell::Fish => FishGen::new(self.p).generate_to(buf),
|
||||
Shell::Zsh => ZshGen::new(self.p).generate_to(buf),
|
||||
Shell::PowerShell => PowerShellGen::new(self.p).generate_to(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
115
src/completions/powershell.rs
Normal file
115
src/completions/powershell.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
|
||||
// Std
|
||||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use app::parser::Parser;
|
||||
|
||||
pub struct PowerShellGen<'a, 'b>
|
||||
where 'a: 'b
|
||||
{
|
||||
p: &'b Parser<'a, 'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> PowerShellGen<'a, 'b> {
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
||||
PowerShellGen { p: p }
|
||||
}
|
||||
|
||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||
let bin_name = self.p.meta.bin_name.as_ref().unwrap();
|
||||
|
||||
let (subcommands_detection_cases, subcommands_cases) = generate_inner(&self.p, "");
|
||||
|
||||
let mut bin_names = vec![
|
||||
bin_name.to_string(),
|
||||
format!("./{0}", bin_name),
|
||||
];
|
||||
if cfg!(windows) {
|
||||
bin_names.push(format!("{0}.exe", bin_name));
|
||||
bin_names.push(format!(r".\{0}", bin_name));
|
||||
bin_names.push(format!(r".\{0}.exe", bin_name));
|
||||
bin_names.push(format!(r"./{0}.exe", bin_name));
|
||||
}
|
||||
|
||||
let bin_names = bin_names.iter().fold(String::new(), |previous, current| format!("{0}, '{1}'", previous, current));
|
||||
let bin_names = bin_names.trim_left_matches(", ");
|
||||
|
||||
let result = format!(r#"
|
||||
@({bin_names}) | %{{
|
||||
Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {{
|
||||
param($wordToComplete, $commandAst, $cursorPosition)
|
||||
|
||||
$command = '_{bin_name}'
|
||||
$commandAst.CommandElements |
|
||||
Select-Object -Skip 1 |
|
||||
%{{
|
||||
switch ($_.ToString()) {{
|
||||
{subcommands_detection_cases}
|
||||
}}
|
||||
}}
|
||||
|
||||
$completions = @()
|
||||
|
||||
switch ($command) {{
|
||||
{subcommands_cases}
|
||||
}}
|
||||
|
||||
$completions |
|
||||
?{{ $_ -like "$wordToComplete*" }} |
|
||||
Sort-Object |
|
||||
%{{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }}
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
bin_names = bin_names,
|
||||
bin_name = bin_name,
|
||||
subcommands_detection_cases = subcommands_detection_cases,
|
||||
subcommands_cases = subcommands_cases
|
||||
);
|
||||
|
||||
w!(buf, result.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_inner<'a, 'b>(p: &Parser<'a, 'b>, previous_command_name: &str) -> (String, String) {
|
||||
let command_name = format!("{}_{}", previous_command_name, &p.meta.name);
|
||||
|
||||
let mut subcommands_detection_cases =
|
||||
if previous_command_name == "" {
|
||||
String::new()
|
||||
}
|
||||
else {
|
||||
format!(r"
|
||||
'{0}' {{
|
||||
$command += '_{0}'
|
||||
break
|
||||
}}
|
||||
", &p.meta.name)
|
||||
};
|
||||
|
||||
let mut completions = String::new();
|
||||
for subcommand in &p.subcommands {
|
||||
completions.push_str(&format!("'{}', ", &subcommand.p.meta.name));
|
||||
}
|
||||
for short in &p.short_list {
|
||||
completions.push_str(&format!("'-{}', ", short));
|
||||
}
|
||||
for long in &p.long_list {
|
||||
completions.push_str(&format!("'--{}', ", long));
|
||||
}
|
||||
|
||||
let mut subcommands_cases = format!(r"
|
||||
'{}' {{
|
||||
$completions = @({})
|
||||
}}
|
||||
", &command_name, completions.trim_right_matches(", "));
|
||||
|
||||
for subcommand in &p.subcommands {
|
||||
let (subcommand_subcommands_detection_cases, subcommand_subcommands_cases) = generate_inner(&subcommand.p, &command_name);
|
||||
subcommands_detection_cases.push_str(&subcommand_subcommands_detection_cases);
|
||||
subcommands_cases.push_str(&subcommand_subcommands_cases);
|
||||
}
|
||||
|
||||
(subcommands_detection_cases, subcommands_cases)
|
||||
}
|
|
@ -11,15 +11,18 @@ pub enum Shell {
|
|||
Fish,
|
||||
/// Generates a completion file for the Z SHell (ZSH)
|
||||
Zsh,
|
||||
/// Generates a completion file for PowerShell
|
||||
PowerShell,
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
/// A list of possible variants in `&'static str` form
|
||||
pub fn variants() -> [&'static str; 3] {
|
||||
pub fn variants() -> [&'static str; 4] {
|
||||
[
|
||||
"zsh",
|
||||
"bash",
|
||||
"fish"
|
||||
"fish",
|
||||
"powershell"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +36,9 @@ impl FromStr for Shell {
|
|||
"ZSH" | _ if s.eq_ignore_ascii_case("zsh") => Ok(Shell::Zsh),
|
||||
"FISH" | _ if s.eq_ignore_ascii_case("fish") => Ok(Shell::Fish),
|
||||
"BASH" | _ if s.eq_ignore_ascii_case("bash") => Ok(Shell::Bash),
|
||||
"POWERSHELL" | _ if s.eq_ignore_ascii_case("powershell") => Ok(Shell::PowerShell),
|
||||
_ => Err(
|
||||
String::from("[valid values: bash, fish, zsh]")
|
||||
String::from("[valid values: bash, fish, zsh, powershell]")
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -43,9 +47,10 @@ impl FromStr for Shell {
|
|||
impl fmt::Display for Shell {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Shell::Bash => write!(f, "BASH"),
|
||||
Shell::Fish => write!(f, "FISH"),
|
||||
Shell::Zsh => write!(f, "ZSH"),
|
||||
Shell::Bash => write!(f, "BASH"),
|
||||
Shell::Fish => write!(f, "FISH"),
|
||||
Shell::Zsh => write!(f, "ZSH"),
|
||||
Shell::PowerShell => write!(f, "POWERSHELL"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue