Skip to content

Commit

Permalink
feat(Completions): adds completion support for PowerShell.
Browse files Browse the repository at this point in the history
Closes #729
  • Loading branch information
Arnavion authored and kbknapp committed Nov 5, 2016
1 parent 1118cc0 commit cff82c8
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
9 changes: 6 additions & 3 deletions src/completions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod macros;
mod bash;
mod fish;
mod zsh;
mod powershell;
mod shell;

// Std
Expand All @@ -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>
Expand All @@ -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),
}
}
}
Expand Down
115 changes: 115 additions & 0 deletions src/completions/powershell.rs
Original file line number Diff line number Diff line change
@@ -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)
}
17 changes: 11 additions & 6 deletions src/completions/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
}
}
Expand All @@ -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]")
),
}
}
Expand All @@ -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"),
}
}
}
Expand Down

0 comments on commit cff82c8

Please sign in to comment.