Repository: clap-rs/clap
Pull request #2276 (2020-12-27 22:39:33)

Fix zsh subcommand help escaping
Hi!

Previously, subcommand help texts were strings encapsulated by a `"` rather than a `'`, which is used anywhere else.

This PR fixes this and switches to `'`, which also allows us to use the same `escape_help` function that's used in the rest of the zsh code. 
I tested this with some completion scripts of my own projects and it fixes the problem of unescaped `"` characters.

Related to #1596

What's the best approach for writing tests when contributing to this crate?
Touching the generic test App, which is used for all shell completion tests is somewhat tricky, since it forces me to adjust the tests for other shells as well. But I've no idea, if those adjustments are correct.

Would you accept a PR, which creates fixtures for each shell in their respective module?
That way, we can properly test a single shell and adjust those tests, without breaking stuff for other shells we don't know anything about.

pksunkara: Yup, go ahead with PRs. Refactoring tests is not a big thing to think about.
Nukesor: Ok.

This PR now contains 4 commits, this got somehow out of hand.

1. Each shell completion test module now has their own test-app creation code. I also added a long commit message in case somebody wonders why this has been done.
2. Fix a zsh character escaping issue for subcommand help texts
3. Add a test for previous fix.
4. Fix a styling issue, which resulted in
```
local commands; commands=(
        
    )
```
instead of:
`local commands; commands=()`
That commit also adjusts variable names for a few functions. The code is quite hard to parse with all those single-character variable names :sweat_smile: 

Patch:
diff --git a/clap_generate/src/generators/shells/zsh.rs b/clap_generate/src/generators/shells/zsh.rs
index 010b73c3..21fbeb89 100644
--- a/clap_generate/src/generators/shells/zsh.rs
+++ b/clap_generate/src/generators/shells/zsh.rs
@@ -72,7 +72,7 @@ _{name} \"$@\"",
 // (( $+functions[_rustup_commands] )) ||
 // _rustup_commands() {
 //     local commands; commands=(
-//         'show:Show the active and installed toolchains'
+//      'show:Show the active and installed toolchains'
 //      'update:Update Rust toolchains'
 //      # ... snip for brevity
 //      'help:Prints this message or the help of the given subcommand(s)'
@@ -84,20 +84,21 @@ fn subcommand_details(p: &App) -> String {
 
     let name = p.get_bin_name().unwrap();
 
+    let mut ret = vec![];
+
     // First we do ourself
-    let mut ret = vec![format!(
+    let parent_text = format!(
         "\
 (( $+functions[_{bin_name_underscore}_commands] )) ||
 _{bin_name_underscore}_commands() {{
-    local commands; commands=(
-        {subcommands_and_args}
-    )
+    local commands; commands=({subcommands_and_args})
     _describe -t commands '{bin_name} commands' commands \"$@\"
 }}",
         bin_name_underscore = name.replace(" ", "__"),
         bin_name = name,
         subcommands_and_args = subcommands_of(p)
-    )];
+    );
+    ret.push(parent_text);
 
     // Next we start looping through all the children, grandchildren, etc.
     let mut all_subcommands = Zsh::all_subcommands(p);
@@ -112,9 +113,7 @@ _{bin_name_underscore}_commands() {{
             "\
 (( $+functions[_{bin_name_underscore}_commands] )) ||
 _{bin_name_underscore}_commands() {{
-    local commands; commands=(
-        {subcommands_and_args}
-    )
+    local commands; commands=({subcommands_and_args})
     _describe -t commands '{bin_name} commands' commands \"$@\"
 }}",
             bin_name_underscore = bin_name.replace(" ", "__"),
@@ -141,38 +140,42 @@ _{bin_name_underscore}_commands() {{
 fn subcommands_of(p: &App) -> String {
     debug!("subcommands_of");
 
-    let mut ret = vec![];
+    let mut segments = vec![];
 
-    fn add_sc(sc: &App, n: &str, ret: &mut Vec<String>) {
-        debug!("add_sc");
+    fn add_subcommands(subcommand: &App, name: &str, ret: &mut Vec<String>) {
+        debug!("add_subcommands");
 
-        let s = format!(
-            "\"{name}:{help}\" \\",
-            name = n,
-            help = sc
-                .get_about()
-                .unwrap_or("")
-                .replace("[", "\\[")
-                .replace("]", "\\]")
+        let text = format!(
+            "'{name}:{help}' \\",
+            name = name,
+            help = escape_help(subcommand.get_about().unwrap_or(""))
         );
 
-        if !s.is_empty() {
-            ret.push(s);
+        if !text.is_empty() {
+            ret.push(text);
         }
     }
 
     // The subcommands
-    for sc in p.get_subcommands() {
-        debug!("subcommands_of:iter: subcommand={}", sc.get_name());
+    for command in p.get_subcommands() {
+        debug!("subcommands_of:iter: subcommand={}", command.get_name());
 
-        add_sc(sc, &sc.get_name(), &mut ret);
+        add_subcommands(command, &command.get_name(), &mut segments);
 
-        for alias in sc.get_visible_aliases() {
-            add_sc(sc, alias, &mut ret);
+        for alias in command.get_visible_aliases() {
+            add_subcommands(command, alias, &mut segments);
         }
     }
 
-    ret.join("\n")
+    // Surround the text with newlines for proper formatting.
+    // We need this to prevent weirdly formatted `command=(\n        \n)` sections.
+    // When there are no (sub-)commands.
+    if !segments.is_empty() {
+        segments.insert(0, "".to_string());
+        segments.push("    ".to_string());
+    }
+
+    segments.join("\n")
 }
 
 // Get's the subcommand section of a completion file
@@ -204,42 +207,45 @@ fn subcommands_of(p: &App) -> String {
 //    [name_hyphen] = The full space delineated bin_name, but replace spaces with hyphens
 //    [repeat] = From the same recursive calls, but for all subcommands
 //    [subcommand_args] = The same as zsh::get_args_of
-fn get_subcommands_of(p: &App) -> String {
+fn get_subcommands_of(parent: &App) -> String {
     debug!(
         "get_subcommands_of: Has subcommands...{:?}",
-        p.has_subcommands()
+        parent.has_subcommands()
     );
 
-    if !p.has_subcommands() {
+    if !parent.has_subcommands() {
         return String::new();
     }
 
-    let sc_names = Zsh::subcommands(p);
-    let mut subcmds = vec![];
+    let subcommand_names = Zsh::subcommands(parent);
+    let mut all_subcommands = vec![];
 
-    for &(ref name, ref bin_name) in &sc_names {
+    for &(ref name, ref bin_name) in &subcommand_names {
         debug!(
-            "get_subcommands_of:iter: p={}, name={}, bin_name={}",
-            p.get_name(),
+            "get_subcommands_of:iter: parent={}, name={}, bin_name={}",
+            parent.get_name(),
             name,
             bin_name,
         );
-        let mut v = vec![format!("({})", name)];
-        let subcommand_args =
-            get_args_of(parser_of(p, &*bin_name).expect(INTERNAL_ERROR_MSG), Some(p));
+        let mut segments = vec![format!("({})", name)];
+        let subcommand_args = get_args_of(
+            parser_of(parent, &*bin_name).expect(INTERNAL_ERROR_MSG),
+            Some(parent),
+        );
 
         if !subcommand_args.is_empty() {
-            v.push(subcommand_args);
+            segments.push(subcommand_args);
         }
 
-        let subcommands = get_subcommands_of(parser_of(p, &*bin_name).expect(INTERNAL_ERROR_MSG));
+        // Get the help text of all child subcommands.
+        let children = get_subcommands_of(parser_of(parent, &*bin_name).expect(INTERNAL_ERROR_MSG));
 
-        if !subcommands.is_empty() {
-            v.push(subcommands);
+        if !children.is_empty() {
+            segments.push(children);
         }
 
-        v.push(String::from(";;"));
-        subcmds.push(v.join("\n"));
+        segments.push(String::from(";;"));
+        all_subcommands.push(segments.join("\n"));
     }
 
     format!(
@@ -253,10 +259,10 @@ fn get_subcommands_of(p: &App) -> String {
         esac
     ;;
 esac",
-        name = p.get_name(),
-        name_hyphen = p.get_bin_name().unwrap().replace(" ", "-"),
-        subcommands = subcmds.join("\n"),
-        pos = p.get_positionals().count() + 1
+        name = parent.get_name(),
+        name_hyphen = parent.get_bin_name().unwrap().replace(" ", "-"),
+        subcommands = all_subcommands.join("\n"),
+        pos = parent.get_positionals().count() + 1
     )
 }
 
@@ -264,15 +270,15 @@ esac",
 //
 // Given the bin_name "a b c" and the App for "a" this returns the "c" App.
 // Given the bin_name "a b c" and the App for "b" this returns the "c" App.
-fn parser_of<'help, 'app>(p: &'app App<'help>, bin_name: &str) -> Option<&'app App<'help>> {
-    debug!("parser_of: p={}, bin_name={}", p.get_name(), bin_name);
+fn parser_of<'help, 'app>(parent: &'app App<'help>, bin_name: &str) -> Option<&'app App<'help>> {
+    debug!("parser_of: p={}, bin_name={}", parent.get_name(), bin_name);
 
-    if bin_name == p.get_bin_name().unwrap_or(&String::new()) {
-        return Some(p);
+    if bin_name == parent.get_bin_name().unwrap_or(&String::new()) {
+        return Some(parent);
     }
 
-    for sc in p.get_subcommands() {
-        if let Some(ret) = parser_of(sc, bin_name) {
+    for subcommand in parent.get_subcommands() {
+        if let Some(ret) = parser_of(subcommand, bin_name) {
             return Some(ret);
         }
     }
@@ -300,51 +306,39 @@ fn parser_of<'help, 'app>(p: &'app App<'help>, bin_name: &str) -> Option<&'app A
 //    -C: modify the $context internal variable
 //    -s: Allow stacking of short args (i.e. -a -b -c => -abc)
 //    -S: Do not complete anything after '--' and treat those as argument values
-fn get_args_of(p: &App, p_global: Option<&App>) -> String {
+fn get_args_of(parent: &App, p_global: Option<&App>) -> String {
     debug!("get_args_of");
 
-    let mut ret = vec![String::from("_arguments \"${_arguments_options[@]}\" \\")];
-    let opts = write_opts_of(p, p_global);
-    let flags = write_flags_of(p, p_global);
-    let positionals = write_positionals_of(p);
-
-    let sc_or_a = if p.has_subcommands() {
-        format!(
-            "\":: :_{name}_commands\" \\",
-            name = p.get_bin_name().as_ref().unwrap().replace(" ", "__")
-        )
-    } else {
-        String::new()
-    };
-
-    let sc = if p.has_subcommands() {
-        format!("\"*::: :->{name}\" \\", name = p.get_name())
-    } else {
-        String::new()
-    };
+    let mut segments = vec![String::from("_arguments \"${_arguments_options[@]}\" \\")];
+    let opts = write_opts_of(parent, p_global);
+    let flags = write_flags_of(parent, p_global);
+    let positionals = write_positionals_of(parent);
 
     if !opts.is_empty() {
-        ret.push(opts);
+        segments.push(opts);
     }
 
     if !flags.is_empty() {
-        ret.push(flags);
+        segments.push(flags);
     }
 
     if !positionals.is_empty() {
-        ret.push(positionals);
+        segments.push(positionals);
     }
 
-    if !sc_or_a.is_empty() {
-        ret.push(sc_or_a);
-    }
+    if parent.has_subcommands() {
+        let subcommand_bin_name = format!(
+            "\":: :_{name}_commands\" \\",
+            name = parent.get_bin_name().as_ref().unwrap().replace(" ", "__")
+        );
+        segments.push(subcommand_bin_name);
 
-    if !sc.is_empty() {
-        ret.push(sc);
-    }
+        let subcommand_text = format!("\"*::: :->{name}\" \\", name = parent.get_name());
+        segments.push(subcommand_text);
+    };
 
-    ret.push(String::from("&& ret=0"));
-    ret.join("\n")
+    segments.push(String::from("&& ret=0"));
+    segments.join("\n")
 }
 
 // Uses either `possible_vals` or `value_hint` to give hints about possible argument values
@@ -383,7 +377,7 @@ fn value_completion(arg: &Arg) -> Option<String> {
     }
 }
 
-// Escape help string inside single quotes and brackets
+/// Escape help string inside single quotes and brackets
 fn escape_help(string: &str) -> String {
     string
         .replace("\\", "\\\\")
@@ -392,7 +386,7 @@ fn escape_help(string: &str) -> String {
         .replace("]", "\\]")
 }
 
-// Escape value string inside single quotes and parentheses
+/// Escape value string inside single quotes and parentheses
 fn escape_value(string: &str) -> String {
     string
         .replace("\\", "\\\\")


Test patch:
diff --git a/clap_generate/tests/completions/bash.rs b/clap_generate/tests/completions/bash.rs
index 3e061dd5..51ea58a9 100644
--- a/clap_generate/tests/completions/bash.rs
+++ b/clap_generate/tests/completions/bash.rs
@@ -1,5 +1,27 @@
 use super::*;
 
+fn build_app() -> App<'static> {
+    build_app_with_name("myapp")
+}
+
+fn build_app_with_name(s: &'static str) -> App<'static> {
+    App::new(s)
+        .about("Tests completions")
+        .arg(
+            Arg::new("file")
+                .value_hint(ValueHint::FilePath)
+                .about("some input file"),
+        )
+        .subcommand(
+            App::new("test").about("tests things").arg(
+                Arg::new("case")
+                    .long("case")
+                    .takes_value(true)
+                    .about("the case to test"),
+            ),
+        )
+}
+
 #[test]
 fn bash() {
     let mut app = build_app();
@@ -95,6 +117,19 @@ fn bash_with_special_commands() {
     common::<Bash>(&mut app, "my_app", BASH_SPECIAL_CMDS);
 }
 
+fn build_app_special_commands() -> App<'static> {
+    build_app_with_name("my_app")
+        .subcommand(
+            App::new("some_cmd").about("tests other things").arg(
+                Arg::new("config")
+                    .long("--config")
+                    .takes_value(true)
+                    .about("the other case to test"),
+            ),
+        )
+        .subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
+}
+
 static BASH_SPECIAL_CMDS: &str = r#"_my_app() {
     local i cur prev opts cmds
     COMPREPLY=()
diff --git a/clap_generate/tests/completions/elvish.rs b/clap_generate/tests/completions/elvish.rs
index 7b1dfcd0..6a1212e3 100644
--- a/clap_generate/tests/completions/elvish.rs
+++ b/clap_generate/tests/completions/elvish.rs
@@ -1,5 +1,27 @@
 use super::*;
 
+fn build_app() -> App<'static> {
+    build_app_with_name("myapp")
+}
+
+fn build_app_with_name(s: &'static str) -> App<'static> {
+    App::new(s)
+        .about("Tests completions")
+        .arg(
+            Arg::new("file")
+                .value_hint(ValueHint::FilePath)
+                .about("some input file"),
+        )
+        .subcommand(
+            App::new("test").about("tests things").arg(
+                Arg::new("case")
+                    .long("case")
+                    .takes_value(true)
+                    .about("the case to test"),
+            ),
+        )
+}
+
 #[test]
 fn elvish() {
     let mut app = build_app();
@@ -54,6 +76,19 @@ fn elvish_with_special_commands() {
     common::<Elvish>(&mut app, "my_app", ELVISH_SPECIAL_CMDS);
 }
 
+fn build_app_special_commands() -> App<'static> {
+    build_app_with_name("my_app")
+        .subcommand(
+            App::new("some_cmd").about("tests other things").arg(
+                Arg::new("config")
+                    .long("--config")
+                    .takes_value(true)
+                    .about("the other case to test"),
+            ),
+        )
+        .subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
+}
+
 static ELVISH_SPECIAL_CMDS: &str = r#"
 edit:completion:arg-completer[my_app] = [@words]{
     fn spaces [n]{
diff --git a/clap_generate/tests/completions/fish.rs b/clap_generate/tests/completions/fish.rs
index 528a6436..e398de0b 100644
--- a/clap_generate/tests/completions/fish.rs
+++ b/clap_generate/tests/completions/fish.rs
@@ -1,5 +1,27 @@
 use super::*;
 
+fn build_app() -> App<'static> {
+    build_app_with_name("myapp")
+}
+
+fn build_app_with_name(s: &'static str) -> App<'static> {
+    App::new(s)
+        .about("Tests completions")
+        .arg(
+            Arg::new("file")
+                .value_hint(ValueHint::FilePath)
+                .about("some input file"),
+        )
+        .subcommand(
+            App::new("test").about("tests things").arg(
+                Arg::new("case")
+                    .long("case")
+                    .takes_value(true)
+                    .about("the case to test"),
+            ),
+        )
+}
+
 #[test]
 fn fish() {
     let mut app = build_app();
@@ -23,6 +45,19 @@ fn fish_with_special_commands() {
     common::<Fish>(&mut app, "my_app", FISH_SPECIAL_CMDS);
 }
 
+fn build_app_special_commands() -> App<'static> {
+    build_app_with_name("my_app")
+        .subcommand(
+            App::new("some_cmd").about("tests other things").arg(
+                Arg::new("config")
+                    .long("--config")
+                    .takes_value(true)
+                    .about("the other case to test"),
+            ),
+        )
+        .subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
+}
+
 static FISH_SPECIAL_CMDS: &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'
@@ -47,6 +82,36 @@ fn fish_with_special_help() {
     common::<Fish>(&mut app, "my_app", FISH_SPECIAL_HELP);
 }
 
+fn build_app_special_help() -> App<'static> {
+    App::new("my_app")
+        .arg(
+            Arg::new("single-quotes")
+                .long("single-quotes")
+                .about("Can be 'always', 'auto', or 'never'"),
+        )
+        .arg(
+            Arg::new("double-quotes")
+                .long("double-quotes")
+                .about("Can be \"always\", \"auto\", or \"never\""),
+        )
+        .arg(
+            Arg::new("backticks")
+                .long("backticks")
+                .about("For more information see `echo test`"),
+        )
+        .arg(Arg::new("backslash").long("backslash").about("Avoid '\\n'"))
+        .arg(
+            Arg::new("brackets")
+                .long("brackets")
+                .about("List packages [filter]"),
+        )
+        .arg(
+            Arg::new("expansions")
+                .long("expansions")
+                .about("Execute the shell command with $SHELL"),
+        )
+}
+
 static FISH_SPECIAL_HELP: &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`'
diff --git a/clap_generate/tests/completions/mod.rs b/clap_generate/tests/completions/mod.rs
index 6e79f78a..b48a656f 100644
--- a/clap_generate/tests/completions/mod.rs
+++ b/clap_generate/tests/completions/mod.rs
@@ -23,75 +23,6 @@ macro_rules! assert_eq {
     };
 }
 
-fn build_app() -> App<'static> {
-    build_app_with_name("myapp")
-}
-
-fn build_app_with_name(s: &'static str) -> App<'static> {
-    App::new(s)
-        .about("Tests completions")
-        .arg(
-            Arg::new("file")
-                .value_hint(ValueHint::FilePath)
-                .about("some input file"),
-        )
-        .subcommand(
-            App::new("test").about("tests things").arg(
-                Arg::new("case")
-                    .long("case")
-                    .takes_value(true)
-                    .about("the case to test"),
-            ),
-        )
-}
-
-fn build_app_special_commands() -> App<'static> {
-    build_app_with_name("my_app")
-        .subcommand(
-            App::new("some_cmd").about("tests other things").arg(
-                Arg::new("config")
-                    .long("--config")
-                    .takes_value(true)
-                    .about("the other case to test"),
-            ),
-        )
-        .subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
-}
-
-fn build_app_special_help() -> App<'static> {
-    App::new("my_app")
-        .arg(
-            Arg::new("single-quotes")
-                .long("single-quotes")
-                .about("Can be 'always', 'auto', or 'never'"),
-        )
-        .arg(
-            Arg::new("double-quotes")
-                .long("double-quotes")
-                .about("Can be \"always\", \"auto\", or \"never\""),
-        )
-        .arg(
-            Arg::new("backticks")
-                .long("backticks")
-                .about("For more information see `echo test`"),
-        )
-        .arg(Arg::new("backslash").long("backslash").about("Avoid '\\n'"))
-        .arg(
-            Arg::new("brackets")
-                .long("brackets")
-                .about("List packages [filter]"),
-        )
-        .arg(
-            Arg::new("expansions")
-                .long("expansions")
-                .about("Execute the shell command with $SHELL"),
-        )
-}
-
-fn build_app_nested_subcommands() -> App<'static> {
-    App::new("first").subcommand(App::new("second").subcommand(App::new("third")))
-}
-
 pub fn common<G: Generator>(app: &mut App, name: &str, fixture: &str) {
     let mut buf = vec![];
     generate::<G, _>(app, name, &mut buf);
diff --git a/clap_generate/tests/completions/powershell.rs b/clap_generate/tests/completions/powershell.rs
index 248b4b89..7c94396d 100644
--- a/clap_generate/tests/completions/powershell.rs
+++ b/clap_generate/tests/completions/powershell.rs
@@ -1,5 +1,27 @@
 use super::*;
 
+fn build_app() -> App<'static> {
+    build_app_with_name("myapp")
+}
+
+fn build_app_with_name(s: &'static str) -> App<'static> {
+    App::new(s)
+        .about("Tests completions")
+        .arg(
+            Arg::new("file")
+                .value_hint(ValueHint::FilePath)
+                .about("some input file"),
+        )
+        .subcommand(
+            App::new("test").about("tests things").arg(
+                Arg::new("case")
+                    .long("case")
+                    .takes_value(true)
+                    .about("the case to test"),
+            ),
+        )
+}
+
 #[test]
 fn powershell() {
     let mut app = build_app();
@@ -64,6 +86,19 @@ fn powershell_with_special_commands() {
     common::<PowerShell>(&mut app, "my_app", POWERSHELL_SPECIAL_CMDS);
 }
 
+fn build_app_special_commands() -> App<'static> {
+    build_app_with_name("my_app")
+        .subcommand(
+            App::new("some_cmd").about("tests other things").arg(
+                Arg::new("config")
+                    .long("--config")
+                    .takes_value(true)
+                    .about("the other case to test"),
+            ),
+        )
+        .subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
+}
+
 static POWERSHELL_SPECIAL_CMDS: &str = r#"
 using namespace System.Management.Automation
 using namespace System.Management.Automation.Language
diff --git a/clap_generate/tests/completions/zsh.rs b/clap_generate/tests/completions/zsh.rs
index 81a8f66e..4e4ee953 100644
--- a/clap_generate/tests/completions/zsh.rs
+++ b/clap_generate/tests/completions/zsh.rs
@@ -1,5 +1,27 @@
 use super::*;
 
+fn build_app() -> App<'static> {
+    build_app_with_name("myapp")
+}
+
+fn build_app_with_name(s: &'static str) -> App<'static> {
+    App::new(s)
+        .about("Tests completions")
+        .arg(
+            Arg::new("file")
+                .value_hint(ValueHint::FilePath)
+                .about("some input file"),
+        )
+        .subcommand(
+            App::new("test").about("tests things").arg(
+                Arg::new("case")
+                    .long("case")
+                    .takes_value(true)
+                    .about("the case to test"),
+            ),
+        )
+}
+
 #[test]
 fn zsh() {
     let mut app = build_app();
@@ -62,23 +84,19 @@ esac
 (( $+functions[_myapp_commands] )) ||
 _myapp_commands() {
     local commands; commands=(
-        "test:tests things" \
-"help:Prints this message or the help of the given subcommand(s)" \
+'test:tests things' \
+'help:Prints this message or the help of the given subcommand(s)' \
     )
     _describe -t commands 'myapp commands' commands "$@"
 }
 (( $+functions[_myapp__help_commands] )) ||
 _myapp__help_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'myapp help commands' commands "$@"
 }
 (( $+functions[_myapp__test_commands] )) ||
 _myapp__test_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'myapp test commands' commands "$@"
 }
 
@@ -90,6 +108,23 @@ fn zsh_with_special_commands() {
     common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_CMDS);
 }
 
+fn build_app_special_commands() -> App<'static> {
+    build_app_with_name("my_app")
+        .subcommand(
+            App::new("some_cmd").about("tests other things").arg(
+                Arg::new("config")
+                    .long("--config")
+                    .takes_value(true)
+                    .about("the other case to test"),
+            ),
+        )
+        .subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
+        .subcommand(
+            App::new("some_cmd_with_special_characters")
+                .about("This 'is' a \"special\" [character] string \\"),
+        )
+}
+
 static ZSH_SPECIAL_CMDS: &str = r#"#compdef my_app
 
 autoload -U is-at-least
@@ -147,6 +182,14 @@ _arguments "${_arguments_options[@]}" \
 '--version[Prints version information]' \
 && ret=0
 ;;
+(some_cmd_with_special_characters)
+_arguments "${_arguments_options[@]}" \
+'-h[Prints help information]' \
+'--help[Prints help information]' \
+'-V[Prints version information]' \
+'--version[Prints version information]' \
+&& ret=0
+;;
 (help)
 _arguments "${_arguments_options[@]}" \
 '-h[Prints help information]' \
@@ -163,39 +206,37 @@ esac
 (( $+functions[_my_app_commands] )) ||
 _my_app_commands() {
     local commands; commands=(
-        "test:tests things" \
-"some_cmd:tests other things" \
-"some-cmd-with-hypens:" \
-"help:Prints this message or the help of the given subcommand(s)" \
+'test:tests things' \
+'some_cmd:tests other things' \
+'some-cmd-with-hypens:' \
+'some_cmd_with_special_characters:This '\''is'\'' a "special" \[character\] string \\' \
+'help:Prints this message or the help of the given subcommand(s)' \
     )
     _describe -t commands 'my_app commands' commands "$@"
 }
 (( $+functions[_my_app__help_commands] )) ||
 _my_app__help_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'my_app help commands' commands "$@"
 }
 (( $+functions[_my_app__some-cmd-with-hypens_commands] )) ||
 _my_app__some-cmd-with-hypens_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'my_app some-cmd-with-hypens commands' commands "$@"
 }
 (( $+functions[_my_app__some_cmd_commands] )) ||
 _my_app__some_cmd_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'my_app some_cmd commands' commands "$@"
 }
+(( $+functions[_my_app__some_cmd_with_special_characters_commands] )) ||
+_my_app__some_cmd_with_special_characters_commands() {
+    local commands; commands=()
+    _describe -t commands 'my_app some_cmd_with_special_characters commands' commands "$@"
+}
 (( $+functions[_my_app__test_commands] )) ||
 _my_app__test_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'my_app test commands' commands "$@"
 }
 
@@ -207,6 +248,36 @@ fn zsh_with_special_help() {
     common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_HELP);
 }
 
+fn build_app_special_help() -> App<'static> {
+    App::new("my_app")
+        .arg(
+            Arg::new("single-quotes")
+                .long("single-quotes")
+                .about("Can be 'always', 'auto', or 'never'"),
+        )
+        .arg(
+            Arg::new("double-quotes")
+                .long("double-quotes")
+                .about("Can be \"always\", \"auto\", or \"never\""),
+        )
+        .arg(
+            Arg::new("backticks")
+                .long("backticks")
+                .about("For more information see `echo test`"),
+        )
+        .arg(Arg::new("backslash").long("backslash").about("Avoid '\\n'"))
+        .arg(
+            Arg::new("brackets")
+                .long("brackets")
+                .about("List packages [filter]"),
+        )
+        .arg(
+            Arg::new("expansions")
+                .long("expansions")
+                .about("Execute the shell command with $SHELL"),
+        )
+}
+
 static ZSH_SPECIAL_HELP: &str = r#"#compdef my_app
 
 autoload -U is-at-least
@@ -240,9 +311,7 @@ _my_app() {
 
 (( $+functions[_my_app_commands] )) ||
 _my_app_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'my_app commands' commands "$@"
 }
 
@@ -254,6 +323,10 @@ fn zsh_with_nested_subcommands() {
     common::<Zsh>(&mut app, "my_app", ZSH_NESTED_SUBCOMMANDS);
 }
 
+fn build_app_nested_subcommands() -> App<'static> {
+    App::new("first").subcommand(App::new("second").subcommand(App::new("third")))
+}
+
 static ZSH_NESTED_SUBCOMMANDS: &str = r#"#compdef my_app
 
 autoload -U is-at-least
@@ -327,30 +400,26 @@ esac
 (( $+functions[_my_app_commands] )) ||
 _my_app_commands() {
     local commands; commands=(
-        "second:" \
-"help:Prints this message or the help of the given subcommand(s)" \
+'second:' \
+'help:Prints this message or the help of the given subcommand(s)' \
     )
     _describe -t commands 'my_app commands' commands "$@"
 }
 (( $+functions[_my_app__help_commands] )) ||
 _my_app__help_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'my_app help commands' commands "$@"
 }
 (( $+functions[_my_app__second_commands] )) ||
 _my_app__second_commands() {
     local commands; commands=(
-        "third:" \
+'third:' \
     )
     _describe -t commands 'my_app second commands' commands "$@"
 }
 (( $+functions[_my_app__second__third_commands] )) ||
 _my_app__second__third_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'my_app second third commands' commands "$@"
 }
 
diff --git a/clap_generate/tests/value_hints.rs b/clap_generate/tests/value_hints.rs
index f050646e..5391a202 100644
--- a/clap_generate/tests/value_hints.rs
+++ b/clap_generate/tests/value_hints.rs
@@ -124,9 +124,7 @@ _my_app() {
 
 (( $+functions[_my_app_commands] )) ||
 _my_app_commands() {
-    local commands; commands=(
-        
-    )
+    local commands; commands=()
     _describe -t commands 'my_app commands' commands "$@"
 }