From cff82c880e21064fca63351507b80350df6caadf Mon Sep 17 00:00:00 2001 From: Arnavion Date: Sat, 5 Nov 2016 02:47:23 -0700 Subject: [PATCH 1/4] feat(Completions): adds completion support for PowerShell. Closes #729 --- src/app/parser.rs | 2 +- src/completions/mod.rs | 9 ++- src/completions/powershell.rs | 115 ++++++++++++++++++++++++++++++++++ src/completions/shell.rs | 17 +++-- 4 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 src/completions/powershell.rs diff --git a/src/app/parser.rs b/src/app/parser.rs index e5523bd1..80e4d453 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -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)) { diff --git a/src/completions/mod.rs b/src/completions/mod.rs index f7ba2a7e..0c33bdb6 100644 --- a/src/completions/mod.rs +++ b/src/completions/mod.rs @@ -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(&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), } } } diff --git a/src/completions/powershell.rs b/src/completions/powershell.rs new file mode 100644 index 00000000..98314f2f --- /dev/null +++ b/src/completions/powershell.rs @@ -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(&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) +} diff --git a/src/completions/shell.rs b/src/completions/shell.rs index 9c9e2a39..91774578 100644 --- a/src/completions/shell.rs +++ b/src/completions/shell.rs @@ -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"), } } } From 71e679b3ba7fd941841c884da8bc2e2c1f8815de Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sat, 5 Nov 2016 19:53:10 -0400 Subject: [PATCH 2/4] chore: adds a justfile for storing some common commands --- justfile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 justfile diff --git a/justfile b/justfile new file mode 100644 index 00000000..5ea0ac25 --- /dev/null +++ b/justfile @@ -0,0 +1,9 @@ +update-contributors: + @echo 'Removing old CONTRIBUTORS.md' + @mv CONTRIBUTORS.md CONTRIBUTORS.md.bak + @echo 'Downloading a list of new contributors' + @echo "The following is a list of contributors in alphabetical order:" > CONTRIBUTORS.md + @echo "" >> CONTRIBUTORS.md + @echo "" >> CONTRIBUTORS.md + @githubcontrib --owner kbknapp --repo clap-rs --sha master --cols 6 --format md --showlogin true --sortBy login >> CONTRIBUTORS.md + @rm CONTRIBUTORS.md.bak From b620323299f7c4db46e647e060ec330a48468411 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sat, 5 Nov 2016 19:58:19 -0400 Subject: [PATCH 3/4] chore: updates the contributors list --- CONTRIBUTORS.md | 73 ++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 9015671b..a7027f01 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,49 +1,48 @@ -The following is a list of contributors to the [clap](https://github.com/kbknapp/clap-rs) project, in alphabetical order: +The following is a list of contributors in alphabetical order: -[afiune](https://github.com/afiune) |[alex-gulyas](https://github.com/alex-gulyas) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[archer884](https://github.com/archer884) | -:---: |:---: |:---: |:---: |:---: | -[afiune](https://github.com/afiune) |[alex-gulyas](https://github.com/alex-gulyas) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[archer884](https://github.com/archer884) | -[Bilalh](https://github.com/Bilalh) |[birkenfeld](https://github.com/birkenfeld) |[bradurani](https://github.com/bradurani) |[brennie](https://github.com/brennie) |[brianp](https://github.com/brianp) | -:---: |:---: |:---: |:---: |:---: | -[Bilalh](https://github.com/Bilalh) |[birkenfeld](https://github.com/birkenfeld) |[bradurani](https://github.com/bradurani) |[brennie](https://github.com/brennie) |[brianp](https://github.com/brianp) | +[afiune](https://github.com/afiune) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[AluisioASG](https://github.com/AluisioASG) |[archer884](https://github.com/archer884) |[Arnavion](https://github.com/Arnavion) | +:---: |:---: |:---: |:---: |:---: |:---: | +[afiune](https://github.com/afiune) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[AluisioASG](https://github.com/AluisioASG) |[archer884](https://github.com/archer884) |[Arnavion](https://github.com/Arnavion) | -[Byron](https://github.com/Byron) |[cite-reader](https://github.com/cite-reader) |[cstorey](https://github.com/cstorey) |[daboross](https://github.com/daboross) |[davidszotten](https://github.com/davidszotten) | -:---: |:---: |:---: |:---: |:---: | -[Byron](https://github.com/Byron) |[cite-reader](https://github.com/cite-reader) |[cstorey](https://github.com/cstorey) |[daboross](https://github.com/daboross) |[davidszotten](https://github.com/davidszotten) | +[Bilalh](https://github.com/Bilalh) |[birkenfeld](https://github.com/birkenfeld) |[bradurani](https://github.com/bradurani) |[brennie](https://github.com/brennie) |[brianp](https://github.com/brianp) |[Byron](https://github.com/Byron) | +:---: |:---: |:---: |:---: |:---: |:---: | +[Bilalh](https://github.com/Bilalh) |[birkenfeld](https://github.com/birkenfeld) |[bradurani](https://github.com/bradurani) |[brennie](https://github.com/brennie) |[brianp](https://github.com/brianp) |[Byron](https://github.com/Byron) | -[dguo](https://github.com/dguo) |[flying-sheep](https://github.com/flying-sheep) |[Geogi](https://github.com/Geogi) |[gohyda](https://github.com/gohyda) |[GrappigPanda](https://github.com/GrappigPanda) | -:---: |:---: |:---: |:---: |:---: | -[dguo](https://github.com/dguo) |[flying-sheep](https://github.com/flying-sheep) |[Geogi](https://github.com/Geogi) |[gohyda](https://github.com/gohyda) |[GrappigPanda](https://github.com/GrappigPanda) | +[cstorey](https://github.com/cstorey) |[daboross](https://github.com/daboross) |[davidszotten](https://github.com/davidszotten) |[dguo](https://github.com/dguo) |[flying-sheep](https://github.com/flying-sheep) |[Geogi](https://github.com/Geogi) | +:---: |:---: |:---: |:---: |:---: |:---: | +[cstorey](https://github.com/cstorey) |[daboross](https://github.com/daboross) |[davidszotten](https://github.com/davidszotten) |[dguo](https://github.com/dguo) |[flying-sheep](https://github.com/flying-sheep) |[Geogi](https://github.com/Geogi) | -[grossws](https://github.com/grossws) |[hexjelly](https://github.com/hexjelly) |[hgrecco](https://github.com/hgrecco) |[homu](https://github.com/homu) |[hoodie](https://github.com/hoodie) | -:---: |:---: |:---: |:---: |:---: | -[grossws](https://github.com/grossws) |[hexjelly](https://github.com/hexjelly) |[hgrecco](https://github.com/hgrecco) |[homu](https://github.com/homu) |[hoodie](https://github.com/hoodie) | +[gohyda](https://github.com/gohyda) |[GrappigPanda](https://github.com/GrappigPanda) |[grossws](https://github.com/grossws) |[hexjelly](https://github.com/hexjelly) |[hgrecco](https://github.com/hgrecco) |[homu](https://github.com/homu) | +:---: |:---: |:---: |:---: |:---: |:---: | +[gohyda](https://github.com/gohyda) |[GrappigPanda](https://github.com/GrappigPanda) |[grossws](https://github.com/grossws) |[hexjelly](https://github.com/hexjelly) |[hgrecco](https://github.com/hgrecco) |[homu](https://github.com/homu) | -[huonw](https://github.com/huonw) |[idmit](https://github.com/idmit) |[iliekturtles](https://github.com/iliekturtles) |[james-darkfox](https://github.com/james-darkfox) |[japaric](https://github.com/japaric) | -:---: |:---: |:---: |:---: |:---: | -[huonw](https://github.com/huonw) |[idmit](https://github.com/idmit) |[iliekturtles](https://github.com/iliekturtles) |[james-darkfox](https://github.com/james-darkfox) |[japaric](https://github.com/japaric) | +[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[idmit](https://github.com/idmit) |[iliekturtles](https://github.com/iliekturtles) |[james-darkfox](https://github.com/james-darkfox) |[japaric](https://github.com/japaric) | +:---: |:---: |:---: |:---: |:---: |:---: | +[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[idmit](https://github.com/idmit) |[iliekturtles](https://github.com/iliekturtles) |[james-darkfox](https://github.com/james-darkfox) |[japaric](https://github.com/japaric) | -[jespino](https://github.com/jespino) |[jimmycuadra](https://github.com/jimmycuadra) |[joshtriplett](https://github.com/joshtriplett) |[kbknapp](https://github.com/kbknapp) |[Keats](https://github.com/Keats) | -:---: |:---: |:---: |:---: |:---: | -[jespino](https://github.com/jespino) |[jimmycuadra](https://github.com/jimmycuadra) |[joshtriplett](https://github.com/joshtriplett) |[kbknapp](https://github.com/kbknapp) |[Keats](https://github.com/Keats) | +[jespino](https://github.com/jespino) |[jimmycuadra](https://github.com/jimmycuadra) |[joshtriplett](https://github.com/joshtriplett) |[joshtriplett](https://github.com/joshtriplett) |[kbknapp](https://github.com/kbknapp) |[Keats](https://github.com/Keats) | +:---: |:---: |:---: |:---: |:---: |:---: | +[jespino](https://github.com/jespino) |[jimmycuadra](https://github.com/jimmycuadra) |[joshtriplett](https://github.com/joshtriplett) |[joshtriplett](https://github.com/joshtriplett) |[kbknapp](https://github.com/kbknapp) |[Keats](https://github.com/Keats) | -[messense](https://github.com/messense) |[N-006](https://github.com/N-006) |[nabijaczleweli](https://github.com/nabijaczleweli) |[nelsonjchen](https://github.com/nelsonjchen) |[nicompte](https://github.com/nicompte) | -:---: |:---: |:---: |:---: |:---: | -[messense](https://github.com/messense) |[N-006](https://github.com/N-006) |[nabijaczleweli](https://github.com/nabijaczleweli) |[nelsonjchen](https://github.com/nelsonjchen) |[nicompte](https://github.com/nicompte) | +[matthiasbeyer](https://github.com/matthiasbeyer) |[messense](https://github.com/messense) |[mvaude](https://github.com/mvaude) |[N-006](https://github.com/N-006) |[nabijaczleweli](https://github.com/nabijaczleweli) |[nelsonjchen](https://github.com/nelsonjchen) | +:---: |:---: |:---: |:---: |:---: |:---: | +[matthiasbeyer](https://github.com/matthiasbeyer) |[messense](https://github.com/messense) |[mvaude](https://github.com/mvaude) |[N-006](https://github.com/N-006) |[nabijaczleweli](https://github.com/nabijaczleweli) |[nelsonjchen](https://github.com/nelsonjchen) | -[nvzqz](https://github.com/nvzqz) |[ogham](https://github.com/ogham) |[panicbit](https://github.com/panicbit) |[rnelson](https://github.com/rnelson) |[rtaycher](https://github.com/rtaycher) | -:---: |:---: |:---: |:---: |:---: | -[nvzqz](https://github.com/nvzqz) |[ogham](https://github.com/ogham) |[panicbit](https://github.com/panicbit) |[rnelson](https://github.com/rnelson) |[rtaycher](https://github.com/rtaycher) | +[Nemo157](https://github.com/Nemo157) |[nicompte](https://github.com/nicompte) |[nvzqz](https://github.com/nvzqz) |[ogham](https://github.com/ogham) |[panicbit](https://github.com/panicbit) |[rnelson](https://github.com/rnelson) | +:---: |:---: |:---: |:---: |:---: |:---: | +[Nemo157](https://github.com/Nemo157) |[nicompte](https://github.com/nicompte) |[nvzqz](https://github.com/nvzqz) |[ogham](https://github.com/ogham) |[panicbit](https://github.com/panicbit) |[rnelson](https://github.com/rnelson) | -[SirVer](https://github.com/SirVer) |[sru](https://github.com/sru) |[SShrike](https://github.com/SShrike) |[starkat99](https://github.com/starkat99) |[swatteau](https://github.com/swatteau) | -:---: |:---: |:---: |:---: |:---: | -[SirVer](https://github.com/SirVer) |[sru](https://github.com/sru) |[SShrike](https://github.com/SShrike) |[starkat99](https://github.com/starkat99) |[swatteau](https://github.com/swatteau) | +[rtaycher](https://github.com/rtaycher) |[SirVer](https://github.com/SirVer) |[sru](https://github.com/sru) |[SShrike](https://github.com/SShrike) |[starkat99](https://github.com/starkat99) |[swatteau](https://github.com/swatteau) | +:---: |:---: |:---: |:---: |:---: |:---: | +[rtaycher](https://github.com/rtaycher) |[SirVer](https://github.com/SirVer) |[sru](https://github.com/sru) |[SShrike](https://github.com/SShrike) |[starkat99](https://github.com/starkat99) |[swatteau](https://github.com/swatteau) | + +[tanakh](https://github.com/tanakh) |[th4t](https://github.com/th4t) |[tormol](https://github.com/tormol) |[tshepang](https://github.com/tshepang) |[untitaker](https://github.com/untitaker) |[Vinatorul](https://github.com/Vinatorul) | +:---: |:---: |:---: |:---: |:---: |:---: | +[tanakh](https://github.com/tanakh) |[th4t](https://github.com/th4t) |[tormol](https://github.com/tormol) |[tshepang](https://github.com/tshepang) |[untitaker](https://github.com/untitaker) |[Vinatorul](https://github.com/Vinatorul) | + +[vks](https://github.com/vks) |[volks73](https://github.com/volks73) |[wdv4758h](https://github.com/wdv4758h) | +:---: |:---: |:---: | +[vks](https://github.com/vks) |[volks73](https://github.com/volks73) |[wdv4758h](https://github.com/wdv4758h) | -[tanakh](https://github.com/tanakh) |[th4t](https://github.com/th4t) |[tormol](https://github.com/tormol) |[tshepang](https://github.com/tshepang) |[untitaker](https://github.com/untitaker) | -:---: |:---: |:---: |:---: |:---: | -[tanakh](https://github.com/tanakh) |[th4t](https://github.com/th4t) |[tormol](https://github.com/tormol) |[tshepang](https://github.com/tshepang) |[untitaker](https://github.com/untitaker) | -[Vinatorul](https://github.com/Vinatorul) |[vks](https://github.com/vks) |[volks73](https://github.com/volks73) |[wdv4758h](https://github.com/wdv4758h) | Yo-Bot | -:---: |:---: |:---: |:---: | -[Vinatorul](https://github.com/Vinatorul) |[vks](https://github.com/vks) |[volks73](https://github.com/volks73) |[wdv4758h](https://github.com/wdv4758h) | [Yo-Bot](https://github.com/yo-bot) | \ No newline at end of file From 475c21814e0131847d1f6c3d1ea79eaf6229edca Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sat, 5 Nov 2016 19:58:30 -0400 Subject: [PATCH 4/4] chore: increase version --- CHANGELOG.md | 10 ++++++++++ Cargo.toml | 2 +- README.md | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0da2589b..c899466a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ + +## v2.18.0 (2016-11-05) + + +#### Features + +* **Completions:** adds completion support for PowerShell. ([cff82c88](https://github.com/kbknapp/clap-rs/commit/cff82c880e21064fca63351507b80350df6caadf), closes [#729](https://github.com/kbknapp/clap-rs/issues/729)) + + + ### v2.17.1 (2016-11-02) diff --git a/Cargo.toml b/Cargo.toml index fcd7db4d..00aa940f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clap" -version = "2.17.1" +version = "2.18.0" authors = ["Kevin K. "] exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"] repository = "https://github.com/kbknapp/clap-rs.git" diff --git a/README.md b/README.md index 2023df77..4ed687d6 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,10 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc) ## What's New +Here's what's new in v2.18.0 + +* **Completions:** Adds completion support for Microsoft PowerShell! (Thanks to @Arnavion) + Here's what's new in v2.17.1 * Fixes a bug where using low index multiples was propagated to subcommands