Repository: kbknapp/clap-rs
Pull request #1218 (2018-03-19 21:02:58)

backporting 2.x fixes and features


<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/kbknapp/clap-rs/1218)
<!-- Reviewable:end -->



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 844dc5b8..bd0012f7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -632,7 +632,7 @@ Minimum version of Rust is now v1.13.0 (Stable)
 
 #### Bug Fixes
 
-* **Low Index Multiples:**  fixes a bug where using low index multiples was propgated to subcommands ([33924e88](https://github.com/kbknapp/clap-rs/commit/33924e884461983c4e6b5ea1330fecc769a4ade7), closes [#725](https://github.com/kbknapp/clap-rs/issues/725))
+* **Low Index Multiples:**  fixes a bug where using low index multiples was propagated to subcommands ([33924e88](https://github.com/kbknapp/clap-rs/commit/33924e884461983c4e6b5ea1330fecc769a4ade7), closes [#725](https://github.com/kbknapp/clap-rs/issues/725))
 
 
 
diff --git a/Cargo.toml b/Cargo.toml
index d0723e0a..2a11f4e3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -86,7 +86,7 @@ debug = false
 rpath = false
 lto = true
 debug-assertions = false
-# codegen-units ignored with lto=true
+codegen-units = 1
 
 [profile.dev]
 opt-level = 0
@@ -110,6 +110,7 @@ debug = false
 rpath = false
 lto = true
 debug-assertions = false
+codegen-units = 1
 
 [profile.doc]
 opt-level = 0
diff --git a/README.md b/README.md
index 21acec56..3f73cb45 100644
--- a/README.md
+++ b/README.md
@@ -483,6 +483,7 @@ features = [ "suggestions", "color" ]
 
 * **"yaml"**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
 * **"unstable"**: Enables unstable `clap` features that may change from release to release
+* **"wrap_help"**: Turns on the help text wrapping feature, based on the terminal size. (builds dependency `term-size`)
 
 ### Dependencies Tree
 
diff --git a/src/app/help.rs b/src/app/help.rs
index be588891..ccae8c76 100644
--- a/src/app/help.rs
+++ b/src/app/help.rs
@@ -949,7 +949,7 @@ fn copy_and_capture<R: Read, W: Write>(
 
                 // The end of the reader was found without finding the closing tag.
                 // Write the opening byte and captured text to the writer.
-                // Return 0 indicating that nothing was caputred but the reader still contains data.
+                // Return 0 indicating that nothing was captured but the reader still contains data.
                 DelimiterNotFound(not_tag_length) => match w.write(b"{") {
                     Err(e) => Some(Err(e)),
                     _ => match w.write(&tag_buffer.get_ref()[0..not_tag_length]) {
@@ -991,7 +991,7 @@ impl<'w> Help<'w> {
         let mut tmplr = Cursor::new(&template);
         let mut tag_buf = Cursor::new(vec![0u8; 15]);
 
-        // The strategy is to copy the template from the the reader to wrapped stream
+        // The strategy is to copy the template from the reader to wrapped stream
         // until a tag is found. Depending on its value, the appropriate content is copied
         // to the wrapped stream.
         // The copy from template is then resumed, repeating this sequence until reading
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 9bbed4cc..a1097a2d 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -693,7 +693,7 @@ impl<'a, 'b> App<'a, 'b> {
         self
     }
 
-    /// Adds multiple [arguments] to the list of valid possibilties
+    /// Adds multiple [arguments] to the list of valid possibilities
     ///
     /// # Examples
     ///
@@ -829,7 +829,7 @@ impl<'a, 'b> App<'a, 'b> {
     /// Meaning any of the arguments that belong to that group will cause a failure if present with
     /// the conflicting argument.
     ///
-    /// Another added benfit of [`ArgGroup`]s is that you can extract a value from a group instead
+    /// Another added benefit of [`ArgGroup`]s is that you can extract a value from a group instead
     /// of determining exactly which argument was used.
     ///
     /// Finally, using [`ArgGroup`]s to ensure exclusion between arguments is another very common
@@ -1009,7 +1009,7 @@ impl<'a, 'b> App<'a, 'b> {
     /// [`-h` (short)]: ./struct.Arg.html#method.help
     /// [`--help` (long)]: ./struct.Arg.html#method.long_help
     pub fn print_help(&mut self) -> ClapResult<()> {
-        // If there are global arguments, or settings we need to propgate them down to subcommands
+        // If there are global arguments, or settings we need to propagate them down to subcommands
         // before parsing incase we run into a subcommand
         self._build(Propagation::NextLevel);
 
@@ -1036,7 +1036,7 @@ impl<'a, 'b> App<'a, 'b> {
     /// [`-h` (short)]: ./struct.Arg.html#method.help
     /// [`--help` (long)]: ./struct.Arg.html#method.long_help
     pub fn print_long_help(&mut self) -> ClapResult<()> {
-        // If there are global arguments, or settings we need to propgate them down to subcommands
+        // If there are global arguments, or settings we need to propagate them down to subcommands
         // before parsing incase we run into a subcommand
         self._build(Propagation::NextLevel);
 
diff --git a/src/app/settings.rs b/src/app/settings.rs
index 4e0147e4..368559a8 100644
--- a/src/app/settings.rs
+++ b/src/app/settings.rs
@@ -503,7 +503,7 @@ pub enum AppSettings {
     DeriveDisplayOrder,
 
     /// Specifies to use the version of the current command for all child [`SubCommand`]s.
-    /// (Defaults to `false`; subcommands have independant version strings from their parents.)
+    /// (Defaults to `false`; subcommands have independent version strings from their parents.)
     ///
     /// **NOTE:** The version for the current command **and** this setting must be set **prior** to
     /// adding any child subcommands
@@ -551,7 +551,7 @@ pub enum AppSettings {
     /// designing CLIs which allow inferred subcommands and have potential positional/free
     /// arguments whose values could start with the same characters as subcommands. If this is the
     /// case, it's recommended to use settings such as [`AppSeettings::ArgsNegateSubcommands`] in
-    /// conjuction with this setting.
+    /// conjunction with this setting.
     ///
     /// # Examples
     ///
diff --git a/src/args/arg.rs b/src/args/arg.rs
index a42cc79a..80fcb5dd 100644
--- a/src/args/arg.rs
+++ b/src/args/arg.rs
@@ -1879,9 +1879,9 @@ impl<'a, 'b> Arg<'a, 'b> {
     ///
     /// **NOTE:** This does *not* implicitly set [`Arg::multiple(true)`]. This is because
     /// `-o val -o val` is multiple occurrences but a single value and `-o val1 val2` is a single
-    /// occurence with multiple values. For positional arguments this **does** set
+    /// occurrence with multiple values. For positional arguments this **does** set
     /// [`Arg::multiple(true)`] because there is no way to determine the difference between multiple
-    /// occurences and multiple values.
+    /// occurrences and multiple values.
     ///
     /// # Examples
     ///
@@ -1943,9 +1943,9 @@ impl<'a, 'b> Arg<'a, 'b> {
     ///
     /// **NOTE:** This does not implicitly set [`Arg::multiple(true)`]. This is because
     /// `-o val -o val` is multiple occurrences but a single value and `-o val1 val2` is a single
-    /// occurence with multiple values. For positional arguments this **does** set
+    /// occurrence with multiple values. For positional arguments this **does** set
     /// [`Arg::multiple(true)`] because there is no way to determine the difference between multiple
-    /// occurences and multiple values.
+    /// occurrences and multiple values.
     ///
     /// # Examples
     ///
@@ -2194,7 +2194,7 @@ impl<'a, 'b> Arg<'a, 'b> {
     /// **NOTE:** This implicitly sets [`Arg::takes_value(true)`].
     ///
     /// **NOTE:** This setting effectively disables `AppSettings::ArgRequiredElseHelp` if used in
-    /// conjuction as it ensures that some argument will always be present.
+    /// conjunction as it ensures that some argument will always be present.
     ///
     /// # Examples
     ///
@@ -2436,7 +2436,7 @@ impl<'a, 'b> Arg<'a, 'b> {
     /// ```
     ///
     /// We can also see that these values are applied in order, and if more than one condition is
-    /// true, only the first evaluatd "wins"
+    /// true, only the first evaluated "wins"
     ///
     /// ```rust
     /// # use clap::{App, Arg};
@@ -2491,7 +2491,7 @@ impl<'a, 'b> Arg<'a, 'b> {
     /// will return `0` even though the [`ArgMatches::value_of`] will return the default specified.
     ///
     /// **NOTE:** If the user *does not* use this argument at runtime [`ArgMatches::is_present`] will
-    /// return `true` if the variable is present in the environemnt . If you wish to determine whether
+    /// return `true` if the variable is present in the environment . If you wish to determine whether
     /// the argument was used at runtime or not, consider [`ArgMatches::occurrences_of`] which will
     /// return `0` if the argument was *not* used at runtime.
     ///
diff --git a/src/completions/fish.rs b/src/completions/fish.rs
index 6a0ac9c6..e90cfc59 100644
--- a/src/completions/fish.rs
+++ b/src/completions/fish.rs
@@ -13,25 +13,8 @@ impl<'a, 'b> FishGen<'a, 'b> {
 
     pub fn generate_to<W: Write>(&self, buf: &mut W) {
         let command = self.0.bin_name.as_ref().unwrap();
-
-        // function to detect subcommand
-        let detect_subcommand_function = r#"function __fish_using_command
-    set cmd (commandline -opc)
-    if [ (count $cmd) -eq (count $argv) ]
-        for i in (seq (count $argv))
-            if [ $cmd[$i] != $argv[$i] ]
-                return 1
-            end
-        end
-        return 0
-    end
-    return 1
-end
-
-"#.to_string();
-
-        let mut buffer = detect_subcommand_function;
-        gen_fish_inner(command, self, &command.to_string(), &mut buffer);
+        let mut buffer = String::new();
+        gen_fish_inner(command, self, command, &mut buffer);
         w!(buf, buffer.as_bytes());
     }
 }
@@ -39,7 +22,7 @@ end
 // Escape string inside single quotes
 fn escape_string(string: &str) -> String { string.replace("\\", "\\\\").replace("'", "\\'") }
 
-fn gen_fish_inner(root_command: &str, comp_gen: &FishGen, parent_cmds: &str, buffer: &mut String) {
+fn gen_fish_inner(root_command: &str, comp_gen: &FishGen, subcommand: &str, buffer: &mut String) {
     debugln!("FishGen::gen_fish_inner;");
     // example :
     //
@@ -51,12 +34,15 @@ fn gen_fish_inner(root_command: &str, comp_gen: &FishGen, parent_cmds: &str, buf
     //      -a "{possible_arguments}"
     //      -r # if require parameter
     //      -f # don't use file completion
-    //      -n "__fish_using_command myprog subcmd1" # complete for command "myprog subcmd1"
+    //      -n "__fish_use_subcommand"               # complete for command "myprog"
+    //      -n "__fish_seen_subcommand_from subcmd1" # complete for command "myprog subcmd1"
 
-    let basic_template = format!(
-        "complete -c {} -n \"__fish_using_command {}\"",
-        root_command, parent_cmds
-    );
+    let mut basic_template = format!("complete -c {} -n ", root_command);
+    if root_command == subcommand {
+        basic_template.push_str("\"__fish_use_subcommand\"");
+    } else {
+        basic_template.push_str(format!("\"__fish_seen_subcommand_from {}\"", subcommand).as_str());
+    }
 
     for option in opts!(comp_gen.0) {
         let mut template = basic_template.clone();
@@ -103,14 +89,8 @@ fn gen_fish_inner(root_command: &str, comp_gen: &FishGen, parent_cmds: &str, buf
     }
 
     // generate options of subcommands
-    for subcommand in subcommands!(comp_gen.0) {
+    for subcommand in &comp_gen.0.subcommands {
         let sub_comp_gen = FishGen::new(&subcommand);
-        // make new "parent_cmds" for different subcommands
-        let mut sub_parent_cmds = parent_cmds.to_string();
-        if !sub_parent_cmds.is_empty() {
-            sub_parent_cmds.push_str(" ");
-        }
-        sub_parent_cmds.push_str(&subcommand.name);
-        gen_fish_inner(root_command, &sub_comp_gen, &sub_parent_cmds, buffer);
+        gen_fish_inner(root_command, &sub_comp_gen, &subcommand.to_string(), buffer);
     }
 }
diff --git a/src/completions/powershell.rs b/src/completions/powershell.rs
index bf64529d..e11f9846 100644
--- a/src/completions/powershell.rs
+++ b/src/completions/powershell.rs
@@ -16,55 +16,37 @@ impl<'a, 'b> PowerShellGen<'a, 'b> {
         let bin_name = self.0.bin_name.as_ref().unwrap();
 
         let mut names = vec![];
-        let (subcommands_detection_cases, subcommands_cases) =
+        let subcommands_cases =
             generate_inner(self.0, "", &mut names);
 
-        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}
-                    default {{
-                        break
-                    }}
-                }}
-            }}
-
-        $completions = @()
-
-        switch ($command) {{
-{subcommands_cases}
+using namespace System.Management.Automation
+using namespace System.Management.Automation.Language
+
+Register-ArgumentCompleter -Native -CommandName '{bin_name}' -ScriptBlock {{
+    param($wordToComplete, $commandAst, $cursorPosition)
+
+    $commandElements = $commandAst.CommandElements
+    $command = @(
+        '{bin_name}'
+        for ($i = 1; $i -lt $commandElements.Count; $i++) {{
+            $element = $commandElements[$i]
+            if ($element -isnot [StringConstantExpressionAst] -or
+                $element.StringConstantType -ne [StringConstantType]::BareWord -or
+                $element.Value.StartsWith('-')) {{
+                break
         }}
+        $element.Value
+    }}) -join ';'
 
-        $completions |
-            ?{{ $_ -like "$wordToComplete*" }} |
-            Sort-Object |
-            %{{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }}
-    }}
+    $completions = @(switch ($command) {{{subcommands_cases}
+    }})
+
+    $completions.Where{{ $_.CompletionText -like "$wordToComplete*" }} |
+        Sort-Object -Property ListItemText
 }}
 "#,
-            bin_names = bin_names,
             bin_name = bin_name,
-            subcommands_detection_cases = subcommands_detection_cases,
             subcommands_cases = subcommands_cases
         );
 
@@ -72,64 +54,83 @@ impl<'a, 'b> PowerShellGen<'a, 'b> {
     }
 }
 
+// Escape string inside single quotes
+fn escape_string(string: &str) -> String { string.replace("'", "''") }
+
+fn get_tooltip<T : ToString>(help: Option<&str>, data: T) -> String {
+    match help {
+        Some(help) => escape_string(&help),
+        _ => data.to_string()
+    }
+}
+
 fn generate_inner<'a, 'b, 'p>(
     p: &'p App<'a, 'b>,
     previous_command_name: &str,
     names: &mut Vec<&'p str>,
-) -> (String, String) {
+) -> String {
     debugln!("PowerShellGen::generate_inner;");
     let command_name = if previous_command_name.is_empty() {
-        format!(
-            "{}_{}",
-            previous_command_name,
-            &p.bin_name.as_ref().expect(INTERNAL_ERROR_MSG)
-        )
-    } else {
-        format!("{}_{}", previous_command_name, &p.name)
-    };
-
-    let mut subcommands_detection_cases = if !names.contains(&&*p.name) {
-        names.push(&*p.name);
-        format!(
-            r"
-                    '{0}' {{
-                        $command += '_{0}'
-                        break
-                    }}
-",
-            &p.name
-        )
+        p.bin_name.as_ref().expect(INTERNAL_ERROR_MSG).clone()
     } else {
-        String::new()
+        format!("{};{}", previous_command_name, &p.name)
     };
 
     let mut completions = String::new();
-    for subcommand in &p.subcommands {
-        completions.push_str(&format!("'{}', ", &subcommand.name));
+    let preamble = String::from("\n            [CompletionResult]::new(");
+
+    for option in opts!(p) {
+        if let Some(data) = option.short {
+            let tooltip = get_tooltip(option.help, data);
+            completions.push_str(&preamble);
+            completions.push_str(format!("'-{}', '{}', {}, '{}')",
+                                         data, data, "[CompletionResultType]::ParameterName", tooltip).as_str());
+        }
+        if let Some(data) = option.long {
+            let tooltip = get_tooltip(option.help, data);
+            completions.push_str(&preamble);
+            completions.push_str(format!("'--{}', '{}', {}, '{}')",
+                                         data, data, "[CompletionResultType]::ParameterName", tooltip).as_str());
+        }
     }
-    for short in shorts!(p) {
-        completions.push_str(&format!("'-{}', ", short));
+
+    for flag in flags!(p) {
+        if let Some(data) = flag.short {
+            let tooltip = get_tooltip(flag.help, data);
+            completions.push_str(&preamble);
+            completions.push_str(format!("'-{}', '{}', {}, '{}')",
+                                         data, data, "[CompletionResultType]::ParameterName", tooltip).as_str());
+        }
+        if let Some(data) = flag.long {
+            let tooltip = get_tooltip(flag.help, data);
+            completions.push_str(&preamble);
+            completions.push_str(format!("'--{}', '{}', {}, '{}')",
+                                         data, data, "[CompletionResultType]::ParameterName", tooltip).as_str());
+        }
     }
-    for long in longs!(p) {
-        completions.push_str(&format!("'--{}', ", long));
+
+    for subcommand in subcommands!(p) {
+        let data = &subcommand.name;
+        let tooltip = get_tooltip(subcommand.about, data);
+        completions.push_str(&preamble);
+        completions.push_str(format!("'{}', '{}', {}, '{}')",
+                                     data, data, "[CompletionResultType]::ParameterValue", tooltip).as_str());
     }
 
     let mut subcommands_cases = format!(
         r"
-            '{}' {{
-                $completions = @({})
-            }}
-",
+        '{}' {{{}
+            break
+        }}",
         &command_name,
-        completions.trim_right_matches(", ")
+        completions
     );
 
-    for subcommand in subcommands!(p) {
-        let (subcommand_subcommands_detection_cases, subcommand_subcommands_cases) =
+    for subcommand in &p.subcommands {
+        let subcommand_subcommands_cases =
             generate_inner(&subcommand, &command_name, names);
-        subcommands_detection_cases.push_str(&subcommand_subcommands_detection_cases);
         subcommands_cases.push_str(&subcommand_subcommands_cases);
     }
 
-    (subcommands_detection_cases, subcommands_cases)
+    subcommands_cases
 }
diff --git a/src/errors.rs b/src/errors.rs
index beacba8a..fb0b8abc 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -13,6 +13,7 @@ use fmt::{ColorWhen, Colorizer, ColorizerOption};
 use suggestions;
 
 /// Short hand for [`Result`] type
+///
 /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
 pub type Result<T> = StdResult<T, Error>;
 
@@ -77,7 +78,7 @@ pub enum ErrorKind {
 
     /// Occurs when the user provides an unrecognized [`SubCommand`] which either
     /// doesn't meet the threshold for being similar enough to an existing subcommand,
-    /// or the 'sggestions' feature is disabled.
+    /// or the 'suggestions' feature is disabled.
     /// Otherwise the more detailed [`InvalidSubcommand`] error is returned.
     ///
     /// This error typically happens when passing additional subcommand names to the `help`
@@ -294,7 +295,7 @@ pub enum ErrorKind {
     /// Occurs when the user provides a value containing invalid UTF-8 for an argument and
     /// [`AppSettings::StrictUtf8`] is set.
     ///
-    /// # Platform Speicific
+    /// # Platform Specific
     ///
     /// Non-Windows platforms only (such as Linux, Unix, OSX, etc.)
     ///
@@ -372,7 +373,7 @@ pub enum ErrorKind {
 /// Command Line Argument Parser Error
 #[derive(Debug)]
 pub struct Error {
-    /// Formated error message
+    /// Formatted error message
     pub message: String,
     /// The type of error
     pub kind: ErrorKind,
diff --git a/src/lib.rs b/src/lib.rs
index 6f289ad3..5fa720aa 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -404,7 +404,7 @@
 //! ```
 //!
 //! Alternatively, if you have [`just`](https://github.com/casey/just) installed you can run the
-//! prebuilt recipies. *Not* using `just` is prfeclty fine as well, it simply bundles commands
+//! prebuilt recipies. *Not* using `just` is perfectly fine as well, it simply bundles commands
 //! automatically.
 //!
 //! For example, to test the code, as above simply run:
diff --git a/src/macros.rs b/src/macros.rs
index 3fd9fa02..ed30178d 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -9,7 +9,7 @@
 ///
 /// # Examples
 ///
-/// The following example shows how to load a properly formatted YAML file to build an instnace
+/// The following example shows how to load a properly formatted YAML file to build an instance
 /// of an `App` struct.
 ///
 /// ```ignore
@@ -607,10 +607,10 @@ macro_rules! app_from_crate {
 /// * Three dots (`...`) sets [`Arg::multiple(true)`]
 /// * Angled brackets after either a short or long will set [`Arg::value_name`] and
 /// `Arg::required(true)` such as `--config <FILE>` = `Arg::value_name("FILE")` and
-/// `Arg::required(true)
+/// `Arg::required(true)`
 /// * Square brackets after either a short or long will set [`Arg::value_name`] and
 /// `Arg::required(false)` such as `--config [FILE]` = `Arg::value_name("FILE")` and
-/// `Arg::required(false)
+/// `Arg::required(false)`
 /// * There are short hand syntaxes for Arg methods that accept booleans
 ///   * A plus sign will set that method to `true` such as `+required` = `Arg::required(true)`
 ///   * An exclamation will set that method to `false` such as `!required` = `Arg::required(false)`


Test patch:
diff --git a/tests/completions.rs b/tests/completions.rs
index b6ad74ef..03c38b17 100644
--- a/tests/completions.rs
+++ b/tests/completions.rs
@@ -165,229 +165,135 @@ _myapp__test_commands() {
 
 _myapp "$@""#;
 
-static FISH: &'static str = r#"function __fish_using_command
-    set cmd (commandline -opc)
-    if [ (count $cmd) -eq (count $argv) ]
-        for i in (seq (count $argv))
-            if [ $cmd[$i] != $argv[$i] ]
-                return 1
-            end
-        end
-        return 0
-    end
-    return 1
-end
-
-complete -c myapp -n "__fish_using_command myapp" -s h -l help -d 'Prints help information'
-complete -c myapp -n "__fish_using_command myapp" -s V -l version -d 'Prints version information'
-complete -c myapp -n "__fish_using_command myapp" -f -a "test" -d 'tests things'
-complete -c myapp -n "__fish_using_command myapp" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
-complete -c myapp -n "__fish_using_command myapp test" -l case -d 'the case to test'
-complete -c myapp -n "__fish_using_command myapp test" -s h -l help -d 'Prints help information'
-complete -c myapp -n "__fish_using_command myapp test" -s V -l version -d 'Prints version information'
-complete -c myapp -n "__fish_using_command myapp help" -s h -l help -d 'Prints help information'
-complete -c myapp -n "__fish_using_command myapp help" -s V -l version -d 'Prints version information'
+static FISH: &'static str = r#"complete -c myapp -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
+complete -c myapp -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
+complete -c myapp -n "__fish_use_subcommand" -f -a "test" -d 'tests things'
+complete -c myapp -n "__fish_use_subcommand" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
+complete -c myapp -n "__fish_seen_subcommand_from test" -l case -d 'the case to test'
+complete -c myapp -n "__fish_seen_subcommand_from test" -s h -l help -d 'Prints help information'
+complete -c myapp -n "__fish_seen_subcommand_from test" -s V -l version -d 'Prints version information'
+complete -c myapp -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
+complete -c myapp -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'
 "#;
 
-#[allow(dead_code)]
-#[cfg(not(target_os="windows"))]
 static POWERSHELL: &'static str = r#"
-@('myapp', './myapp') | %{
-    Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {
-        param($wordToComplete, $commandAst, $cursorPosition)
-
-        $command = '_myapp'
-        $commandAst.CommandElements |
-            Select-Object -Skip 1 |
-            %{
-                switch ($_.ToString()) {
-
-                    'test' {
-                        $command += '_test'
-                        break
-                    }
-
-                    'help' {
-                        $command += '_help'
-                        break
-                    }
-
-                }
-            }
-
-        $completions = @()
-
-        switch ($command) {
-
-            '_myapp' {
-                $completions = @('test', 'help', '-h', '-V', '--help', '--version')
-            }
-
-            '_myapp_test' {
-                $completions = @('-h', '-V', '--case', '--help', '--version')
-            }
-
-            '_myapp_help' {
-                $completions = @('-h', '-V', '--help', '--version')
-            }
-
+using namespace System.Management.Automation
+using namespace System.Management.Automation.Language
+
+Register-ArgumentCompleter -Native -CommandName 'my_app' -ScriptBlock {
+    param($wordToComplete, $commandAst, $cursorPosition)
+
+    $commandElements = $commandAst.CommandElements
+    $command = @(
+        'my_app'
+        for ($i = 1; $i -lt $commandElements.Count; $i++) {
+            $element = $commandElements[$i]
+            if ($element -isnot [StringConstantExpressionAst] -or
+                $element.StringConstantType -ne [StringConstantType]::BareWord -or
+                $element.Value.StartsWith('-')) {
+                break
         }
-
-        $completions |
-            ?{ $_ -like "$wordToComplete*" } |
-            Sort-Object |
-            %{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }
-    }
-}
-"#;
-
-#[allow(dead_code)]
-#[cfg(target_os="windows")]
-static POWERSHELL: &'static str = r#"
-@('myapp', './myapp', 'myapp.exe', '.\myapp', '.\myapp.exe', './myapp.exe') | %{
-    Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {
-        param($wordToComplete, $commandAst, $cursorPosition)
-        $command = '_myapp'
-        $commandAst.CommandElements |
-            Select-Object -Skip 1 |
-            %{
-                switch ($_.ToString()) {
-                    'test' {
-                        $command += '_test'
-                        break
-                    }
-                    'help' {
-                        $command += '_help'
-                        break
-                    }
-                }
-            }
-        $completions = @()
-        switch ($command) {
-            '_myapp' {
-                $completions = @('test', 'help', '-h', '-V', '--help', '--version')
-            }
-            '_myapp_test' {
-                $completions = @('-h', '-V', '--case', '--help', '--version')
-            }
-            '_myapp_help' {
-                $completions = @('-h', '-V', '--help', '--version')
-            }
+        $element.Value
+    }) -join ';'
+
+    $completions = @(switch ($command) {
+        'my_app' {
+            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, 'tests things')
+            [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Prints this message or the help of the given subcommand(s)')
+            break
         }
-        $completions |
-            ?{ $_ -like "$wordToComplete*" } |
-            Sort-Object |
-            %{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }
-    }
-}
-"#;
-
-#[allow(dead_code)]
-#[cfg(not(target_os="windows"))]
-static POWERSHELL_SPECIAL_CMDS: &'static str = r#"
-@('my_app', './my_app') | %{
-    Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {
-        param($wordToComplete, $commandAst, $cursorPosition)
-
-        $command = '_my_app'
-        $commandAst.CommandElements |
-            Select-Object -Skip 1 |
-            %{
-                switch ($_.ToString()) {
-
-                    'test' {
-                        $command += '_test'
-                        break
-                    }
-
-                    'some_cmd' {
-                        $command += '_some_cmd'
-                        break
-                    }
-
-                    'help' {
-                        $command += '_help'
-                        break
-                    }
-
-                }
-            }
-
-        $completions = @()
-
-        switch ($command) {
-
-            '_my_app' {
-                $completions = @('test', 'some_cmd', 'help', '-h', '-V', '--help', '--version')
-            }
-
-            '_my_app_test' {
-                $completions = @('-h', '-V', '--case', '--help', '--version')
-            }
-
-            '_my_app_some_cmd' {
-                $completions = @('-h', '-V', '--config', '--help', '--version')
-            }
-
-            '_my_app_help' {
-                $completions = @('-h', '-V', '--help', '--version')
-            }
-
+        'my_app;test' {
+            [CompletionResult]::new('--case', 'case', [CompletionResultType]::ParameterName, 'the case to test')
+            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
+            break
+        }
+        'my_app;help' {
+            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
+            break
         }
+    })
 
-        $completions |
-            ?{ $_ -like "$wordToComplete*" } |
-            Sort-Object |
-            %{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }
-    }
+    $completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
+        Sort-Object -Property ListItemText
 }
 "#;
 
-#[allow(dead_code)]
-#[cfg(target_os="windows")]
 static POWERSHELL_SPECIAL_CMDS: &'static str = r#"
-@('my_app', './my_app', 'my_app.exe', '.\my_app', '.\my_app.exe', './my_app.exe') | %{
-    Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {
-        param($wordToComplete, $commandAst, $cursorPosition)
-        $command = '_my_app'
-        $commandAst.CommandElements |
-            Select-Object -Skip 1 |
-            %{
-                switch ($_.ToString()) {
-                    'test' {
-                        $command += '_test'
-                        break
-                    }
-                    'some_cmd' {
-                        $command += '_some_cmd'
-                        break
-                    }
-                    'help' {
-                        $command += '_help'
-                        break
-                    }
-                }
-            }
-        $completions = @()
-        switch ($command) {
-            '_my_app' {
-                $completions = @('test', 'some_cmd', 'help', '-h', '-V', '--help', '--version')
-            }
-            '_my_app_test' {
-                $completions = @('-h', '-V', '--case', '--help', '--version')
-            }
-            '_my_app_some_cmd' {
-                $completions = @('-h', '-V', '--config', '--help', '--version')
-            }
-            '_my_app_help' {
-                $completions = @('-h', '-V', '--help', '--version')
-            }
+using namespace System.Management.Automation
+using namespace System.Management.Automation.Language
+
+Register-ArgumentCompleter -Native -CommandName 'my_app' -ScriptBlock {
+    param($wordToComplete, $commandAst, $cursorPosition)
+
+    $commandElements = $commandAst.CommandElements
+    $command = @(
+        'my_app'
+        for ($i = 1; $i -lt $commandElements.Count; $i++) {
+            $element = $commandElements[$i]
+            if ($element -isnot [StringConstantExpressionAst] -or
+                $element.StringConstantType -ne [StringConstantType]::BareWord -or
+                $element.Value.StartsWith('-')) {
+                break
         }
-        $completions |
-            ?{ $_ -like "$wordToComplete*" } |
-            Sort-Object |
-            %{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }
-    }
+        $element.Value
+    }) -join ';'
+
+    $completions = @(switch ($command) {
+        'my_app' {
+            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, 'tests things')
+            [CompletionResult]::new('some_cmd', 'some_cmd', [CompletionResultType]::ParameterValue, 'tests other things')
+            [CompletionResult]::new('some-cmd-with-hypens', 'some-cmd-with-hypens', [CompletionResultType]::ParameterValue, 'some-cmd-with-hypens')
+            [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Prints this message or the help of the given subcommand(s)')
+            break
+        }
+        'my_app;test' {
+            [CompletionResult]::new('--case', 'case', [CompletionResultType]::ParameterName, 'the case to test')
+            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
+            break
+        }
+        'my_app;some_cmd' {
+            [CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'the other case to test')
+            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
+            break
+        }
+        'my_app;some-cmd-with-hypens' {
+            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
+            break
+        }
+        'my_app;help' {
+            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
+            [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
+            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
+            break
+        }
+    })
+
+    $completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
+        Sort-Object -Property ListItemText
 }
 "#;
 
@@ -502,35 +408,22 @@ _my_app__test_commands() {
 
 _my_app "$@""#;
 
-static FISH_SPECIAL_CMDS: &'static str = r#"function __fish_using_command
-    set cmd (commandline -opc)
-    if [ (count $cmd) -eq (count $argv) ]
-        for i in (seq (count $argv))
-            if [ $cmd[$i] != $argv[$i] ]
-                return 1
-            end
-        end
-        return 0
-    end
-    return 1
-end
-
-complete -c my_app -n "__fish_using_command my_app" -s h -l help -d 'Prints help information'
-complete -c my_app -n "__fish_using_command my_app" -s V -l version -d 'Prints version information'
-complete -c my_app -n "__fish_using_command my_app" -f -a "test" -d 'tests things'
-complete -c my_app -n "__fish_using_command my_app" -f -a "some_cmd" -d 'tests other things'
-complete -c my_app -n "__fish_using_command my_app" -f -a "some-cmd-with-hypens"
-complete -c my_app -n "__fish_using_command my_app" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
-complete -c my_app -n "__fish_using_command my_app test" -l case -d 'the case to test'
-complete -c my_app -n "__fish_using_command my_app test" -s h -l help -d 'Prints help information'
-complete -c my_app -n "__fish_using_command my_app test" -s V -l version -d 'Prints version information'
-complete -c my_app -n "__fish_using_command my_app some_cmd" -l config -d 'the other case to test'
-complete -c my_app -n "__fish_using_command my_app some_cmd" -s h -l help -d 'Prints help information'
-complete -c my_app -n "__fish_using_command my_app some_cmd" -s V -l version -d 'Prints version information'
-complete -c my_app -n "__fish_using_command my_app some-cmd-with-hypens" -s h -l help -d 'Prints help information'
-complete -c my_app -n "__fish_using_command my_app some-cmd-with-hypens" -s V -l version -d 'Prints version information'
-complete -c my_app -n "__fish_using_command my_app help" -s h -l help -d 'Prints help information'
-complete -c my_app -n "__fish_using_command my_app help" -s V -l version -d 'Prints version information'
+static FISH_SPECIAL_CMDS: &'static str = r#"complete -c my_app -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
+complete -c my_app -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
+complete -c my_app -n "__fish_use_subcommand" -f -a "test" -d 'tests things'
+complete -c my_app -n "__fish_use_subcommand" -f -a "some_cmd" -d 'tests other things'
+complete -c my_app -n "__fish_use_subcommand" -f -a "some-cmd-with-hypens"
+complete -c my_app -n "__fish_use_subcommand" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
+complete -c my_app -n "__fish_seen_subcommand_from test" -l case -d 'the case to test'
+complete -c my_app -n "__fish_seen_subcommand_from test" -s h -l help -d 'Prints help information'
+complete -c my_app -n "__fish_seen_subcommand_from test" -s V -l version -d 'Prints version information'
+complete -c my_app -n "__fish_seen_subcommand_from some_cmd" -l config -d 'the other case to test'
+complete -c my_app -n "__fish_seen_subcommand_from some_cmd" -s h -l help -d 'Prints help information'
+complete -c my_app -n "__fish_seen_subcommand_from some_cmd" -s V -l version -d 'Prints version information'
+complete -c my_app -n "__fish_seen_subcommand_from some-cmd-with-hypens" -s h -l help -d 'Prints help information'
+complete -c my_app -n "__fish_seen_subcommand_from some-cmd-with-hypens" -s V -l version -d 'Prints version information'
+complete -c my_app -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
+complete -c my_app -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'
 "#;
 
 static BASH_SPECIAL_CMDS: &'static str = r#"_my_app() {
@@ -656,27 +549,14 @@ static BASH_SPECIAL_CMDS: &'static str = r#"_my_app() {
 complete -F _my_app -o bashdefault -o default my_app
 "#;
 
-static FISH_SPECIAL_HELP: &'static str = r#"function __fish_using_command
-    set cmd (commandline -opc)
-    if [ (count $cmd) -eq (count $argv) ]
-        for i in (seq (count $argv))
-            if [ $cmd[$i] != $argv[$i] ]
-                return 1
-            end
-        end
-        return 0
-    end
-    return 1
-end
-
-complete -c my_app -n "__fish_using_command my_app" -l single-quotes -d 'Can be \'always\', \'auto\', or \'never\''
-complete -c my_app -n "__fish_using_command my_app" -l double-quotes -d 'Can be "always", "auto", or "never"'
-complete -c my_app -n "__fish_using_command my_app" -l backticks -d 'For more information see `echo test`'
-complete -c my_app -n "__fish_using_command my_app" -l backslash -d 'Avoid \'\\n\''
-complete -c my_app -n "__fish_using_command my_app" -l brackets -d 'List packages [filter]'
-complete -c my_app -n "__fish_using_command my_app" -l expansions -d 'Execute the shell command with $SHELL'
-complete -c my_app -n "__fish_using_command my_app" -s h -l help -d 'Prints help information'
-complete -c my_app -n "__fish_using_command my_app" -s V -l version -d 'Prints version information'
+static FISH_SPECIAL_HELP: &'static str = r#"complete -c my_app -n "__fish_use_subcommand" -l single-quotes -d 'Can be \'always\', \'auto\', or \'never\''
+complete -c my_app -n "__fish_use_subcommand" -l double-quotes -d 'Can be "always", "auto", or "never"'
+complete -c my_app -n "__fish_use_subcommand" -l backticks -d 'For more information see `echo test`'
+complete -c my_app -n "__fish_use_subcommand" -l backslash -d 'Avoid \'\\n\''
+complete -c my_app -n "__fish_use_subcommand" -l brackets -d 'List packages [filter]'
+complete -c my_app -n "__fish_use_subcommand" -l expansions -d 'Execute the shell command with $SHELL'
+complete -c my_app -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
+complete -c my_app -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
 "#;
 
 static ZSH_SPECIAL_HELP: &'static str = r#"#compdef my_app
@@ -813,27 +693,25 @@ fn fish() {
     assert!(compare(&*string, FISH));
 }
 
-// Disabled until I figure out this windows line ending and AppVeyor issues
-//#[test]
-// fn powershell() {
-//     let mut app = build_app();
-//     let mut buf = vec![];
-//     app.gen_completions_to("myapp", Shell::PowerShell, &mut buf);
-//     let string = String::from_utf8(buf).unwrap();
-//
-//     assert!(compare(&*string, POWERSHELL));
-// }
-
-// Disabled until I figure out this windows line ending and AppVeyor issues
-//#[test]
-// fn powershell_with_special_commands() {
-//     let mut app = build_app_special_commands();
-//     let mut buf = vec![];
-//     app.gen_completions_to("my_app", Shell::PowerShell, &mut buf);
-//     let string = String::from_utf8(buf).unwrap();
-//
-//     assert!(compare(&*string, POWERSHELL_SPECIAL_CMDS));
-// }
+#[test]
+fn powershell() {
+    let mut app = build_app();
+    let mut buf = vec![];
+    app.gen_completions_to("my_app", Shell::PowerShell, &mut buf);
+    let string = String::from_utf8(buf).unwrap();
+
+    assert!(compare(&*string, POWERSHELL));
+}
+
+#[test]
+fn powershell_with_special_commands() {
+    let mut app = build_app_special_commands();
+    let mut buf = vec![];
+    app.gen_completions_to("my_app", Shell::PowerShell, &mut buf);
+    let string = String::from_utf8(buf).unwrap();
+
+    assert!(compare(&*string, POWERSHELL_SPECIAL_CMDS));
+}
 
 #[test]
 fn bash_with_special_commands() {