Repository: clap-rs/clap (language: rust)

Issue:
Single Variant ValueEnum Produces \{ in Fish Shell Completions
### Please complete the following tasks

- [X] I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
- [X] I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues

### Rust Version

rustc 1.83.0 (90b35a623 2024-11-26)

### Clap Version

4.5.23

### Minimal reproducible code

```rust
use clap::CommandFactory;

#[derive(clap::Parser)]
struct MyCommand {
    #[arg(long)]
    flag: Flag,
}

#[derive(Clone, clap::ValueEnum)]
enum Flag {
    /// Only single value
    Foo,
}

fn main() {
    clap_complete::generate(
        clap_complete::shells::Fish,
        &mut MyCommand::command(),
        "ls",
        &mut std::io::stdout(),
    );
```


### Steps to reproduce the bug with the above code

```fish
$ cargo run | source
$ ls --flag=<TAB>
```

### Actual Behaviour

```fish
$ ls --flag \{foo
```

### Expected Behaviour

```fish
$ ls --flag foo
```

### Additional Context

Zsh and Bash completions behave as expected.

### Debug Output

_No response_


Discussion / hints:

Fix patch:
diff --git a/clap_complete/src/aot/shells/fish.rs b/clap_complete/src/aot/shells/fish.rs
index 647e856f1e9..0c6404a97a0 100644
--- a/clap_complete/src/aot/shells/fish.rs
+++ b/clap_complete/src/aot/shells/fish.rs
@@ -275,10 +275,10 @@ fn value_completion(option: &Arg) -> String {
     }
 
     if let Some(data) = utils::possible_values(option) {
-        // We return the possible values with their own empty description e.g. {a\t,b\t}
+        // We return the possible values with their own empty description e.g. "a\t''\nb\t''"
         // this makes sure that a and b don't get the description of the option or argument
         format!(
-            " -r -f -a \"{{{}}}\"",
+            " -r -f -a \"{}\"",
             data.iter()
                 .filter_map(|value| if value.is_hide_set() {
                     None
@@ -292,7 +292,7 @@ fn value_completion(option: &Arg) -> String {
                     ))
                 })
                 .collect::<Vec<_>>()
-                .join(",")
+                .join("\n")
         )
     } else {
         // NB! If you change this, please also update the table in `ValueHint` documentation.


Test patch:
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
index 926edab8ac8..a45106e891c 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
@@ -24,8 +24,12 @@ function __fish_exhaustive_using_subcommand
 	contains -- $cmd[1] $argv
 end
 
-complete -c exhaustive -n "__fish_exhaustive_needs_command" -l generate -d 'generate' -r -f -a "{bash\t'',elvish\t'',fish\t'',powershell\t'',zsh\t''}"
-complete -c exhaustive -n "__fish_exhaustive_needs_command" -l empty-choice -r -f -a "{}"
+complete -c exhaustive -n "__fish_exhaustive_needs_command" -l generate -d 'generate' -r -f -a "bash\t''
+elvish\t''
+fish\t''
+powershell\t''
+zsh\t''"
+complete -c exhaustive -n "__fish_exhaustive_needs_command" -l empty-choice -r -f -a ""
 complete -c exhaustive -n "__fish_exhaustive_needs_command" -s h -l help -d 'Print help'
 complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "empty"
 complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "global"
@@ -55,11 +59,15 @@ complete -c exhaustive -n "__fish_exhaustive_using_subcommand global; and __fish
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand global; and __fish_seen_subcommand_from help" -f -a "two"
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand global; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l set -d 'value' -r
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l choice -d 'enum' -r -f -a "{first\t'',second\t''}"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l choice -d 'enum' -r -f -a "first\t''
+second\t''"
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l set-true -d 'bool'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l count -d 'number'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -s h -l help -d 'Print help'
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l choice -r -f -a "{another shell\t'something with a space',bash\t'bash (shell)',fish\t'fish shell',zsh\t'zsh shell'}"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l choice -r -f -a "another shell\t'something with a space'
+bash\t'bash (shell)'
+fish\t'fish shell'
+zsh\t'zsh shell'"
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l single-quotes -d 'Can be \'always\', \'auto\', or \'never\''
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l double-quotes -d 'Can be "always", "auto", or "never"'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l backticks -d 'For more information see `echo test`'
@@ -107,7 +115,9 @@ complete -c exhaustive -n "__fish_exhaustive_using_subcommand last" -s h -l help
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand alias" -s o -s O -l option -l opt -d 'cmd option' -r
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand alias" -s f -s F -l flag -l flg -d 'cmd flag'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand alias" -s h -l help -d 'Print help'
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l choice -r -f -a "{bash\t'',fish\t'',zsh\t''}"
+complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l choice -r -f -a "bash\t''
+fish\t''
+zsh\t''"
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l unknown -r
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l other -r -f
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s p -l path -r -F
diff --git a/clap_complete/tests/snapshots/sub_subcommands.fish b/clap_complete/tests/snapshots/sub_subcommands.fish
index 62afbe5c3ab..41ca7cec320 100644
--- a/clap_complete/tests/snapshots/sub_subcommands.fish
+++ b/clap_complete/tests/snapshots/sub_subcommands.fish
@@ -38,7 +38,8 @@ complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and not __fish_s
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and not __fish_seen_subcommand_from sub_cmd help" -s V -l version -d 'Print version'
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and not __fish_seen_subcommand_from sub_cmd help" -f -a "sub_cmd" -d 'sub-subcommand'
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and not __fish_seen_subcommand_from sub_cmd help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
-complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and __fish_seen_subcommand_from sub_cmd" -l config -d 'the other case to test' -r -f -a "{Lest quotes\, aren\'t escaped.\t'help,with,comma',Second to trigger display of options\t''}"
+complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and __fish_seen_subcommand_from sub_cmd" -l config -d 'the other case to test' -r -f -a "Lest quotes\, aren\'t escaped.\t'help,with,comma'
+Second to trigger display of options\t''"
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and __fish_seen_subcommand_from sub_cmd" -s h -l help -d 'Print help (see more with \'--help\')'
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and __fish_seen_subcommand_from sub_cmd" -s V -l version -d 'Print version'
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd; and __fish_seen_subcommand_from help" -f -a "sub_cmd" -d 'sub-subcommand'
@@ -47,7 +48,8 @@ complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and not __
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and not __fish_seen_subcommand_from sub_cmd help" -s V -l version -d 'Print version'
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and not __fish_seen_subcommand_from sub_cmd help" -f -a "sub_cmd" -d 'sub-subcommand'
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and not __fish_seen_subcommand_from sub_cmd help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
-complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and __fish_seen_subcommand_from sub_cmd" -l config -d 'the other case to test' -r -f -a "{Lest quotes\, aren\'t escaped.\t'help,with,comma',Second to trigger display of options\t''}"
+complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and __fish_seen_subcommand_from sub_cmd" -l config -d 'the other case to test' -r -f -a "Lest quotes\, aren\'t escaped.\t'help,with,comma'
+Second to trigger display of options\t''"
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and __fish_seen_subcommand_from sub_cmd" -s h -l help -d 'Print help (see more with \'--help\')'
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and __fish_seen_subcommand_from sub_cmd" -s V -l version -d 'Print version'
 complete -c my-app -n "__fish_my_app_using_subcommand some_cmd_alias; and __fish_seen_subcommand_from help" -f -a "sub_cmd" -d 'sub-subcommand'
diff --git a/clap_complete/tests/snapshots/value_hint.fish b/clap_complete/tests/snapshots/value_hint.fish
index acddbf88836..a298833d0e4 100644
--- a/clap_complete/tests/snapshots/value_hint.fish
+++ b/clap_complete/tests/snapshots/value_hint.fish
@@ -1,4 +1,6 @@
-complete -c my-app -l choice -r -f -a "{bash\t'',fish\t'',zsh\t''}"
+complete -c my-app -l choice -r -f -a "bash\t''
+fish\t''
+zsh\t''"
 complete -c my-app -l unknown -r
 complete -c my-app -l other -r -f
 complete -c my-app -s p -l path -r -F
diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs
index a788debb98e..0b4d3ece074 100644
--- a/clap_complete/tests/testsuite/fish.rs
+++ b/clap_complete/tests/testsuite/fish.rs
@@ -187,7 +187,7 @@ CHANGELOG.md  examples/        LICENSE-MIT     src/
     let actual = runtime.complete(input, &term).unwrap();
     let expected = snapbox::str![[r#"
 % exhaustive quote --choice 
-another shell  (something with a space)  bash  (bash (shell))  fish  (fish shell)  zsh  (zsh shell)
+another  bash  (bash (shell))  fish  (fish shell)  shell  (something with a space)  zsh  (zsh shell)
 "#]];
     assert_data_eq!(actual, expected);
 }