Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zsh: optional flag with takes_value breaks completions #1822

Closed
2 tasks done
kpcyrd opened this issue Apr 14, 2020 · 6 comments
Closed
2 tasks done

zsh: optional flag with takes_value breaks completions #1822

kpcyrd opened this issue Apr 14, 2020 · 6 comments
Labels
A-completion Area: completion generator C-bug Category: Updating dependencies E-medium Call for participation: Experience needed to fix: Medium / intermediate 💸 $5

Comments

@kpcyrd
Copy link
Contributor

kpcyrd commented Apr 14, 2020

Make sure you completed the following tasks

Code

use std::io::stdout;
use clap::{App, Arg, SubCommand, Shell};

fn app() -> App<'static, 'static> {
    App::new("completion-bug")
        .arg(
            Arg::with_name("foo")
                .short("f")
                .long("foo")
                .takes_value(true)
        )
        .subcommand(SubCommand::with_name("zsh"))
        .subcommand(SubCommand::with_name("bash"))
}

fn main() {
    let args = app().get_matches();
    eprintln!("{:?}", args);

    let shell = match args.subcommand() {
        ("zsh", _) => Shell::Zsh,
        ("bash", _) => Shell::Bash,
        _ => unreachable!(),
    };

    app().gen_completions_to("completion-bug", shell, &mut stdout());
}

Steps to reproduce the issue

cargo run -- zsh > ~/.zsh_completions/_completion-bug # you might need to tweak this for your setup
zsh

# this works
completion-bug z<tab>
# this doesn't
completion-bug -f x z<tab>
# this does something unexpected
completion-bug -f z<tab>

Version

  • Rust: 1.42.0
  • Clap: 2.33.0

Actual Behavior Summary

completion-bug -f x z<tab> doesn't complete anything.

This affects https://github.com/kpcyrd/sn0int because it has a lot of nested subcommands but it's very common to start the command with sn0int -w foo which breaks the completions for everything afterwards.

The bash completions work correctly.

Expected Behavior Summary

completion-bug -f x z<tab> should complete to completion-bug -f x zsh

Additional context

This bug was originally reported as TeXitoi/structopt#369 and then ported over to pure clap. The completions generated by the structopt program and the clap program are identical.

@kpcyrd kpcyrd added the C-bug Category: Updating dependencies label Apr 14, 2020
@CreepySkeleton CreepySkeleton added the A-completion Area: completion generator label Jun 30, 2020
@CreepySkeleton CreepySkeleton added this to the 3.1 milestone Jun 30, 2020
@pksunkara
Copy link
Member

@intgr I couldn't reproduce this when this issue was created. Do you want to take a look at this?

@intgr
Copy link
Contributor

intgr commented Aug 16, 2020

This is a bug indeed, the same issue affects zsh built-in git completions too, for example:

git --git-dir .git commi<TAB> should complete to commit but doesn't.

A workaround is using = in --arg= to separate argument value.

This works: git --git-dir=.git commi<TAB>

EDIT: Sorry, my bad, the above works with git but not clap's generated completions file.

I'll have to dig some more in zsh documentation to see if this is can be fixed in clap or if it's a bug in zsh itself.

If this is helpful for anyone, here's clap 3.x compatible version of the bug report's code:
use clap::{App, Arg};
use clap_generate::generators::{Bash, Zsh};
use std::io::stdout;

fn app() -> App<'static> {
    App::new("completion-bug")
        .arg(Arg::new("foo").short('f').long("foo").takes_value(true))
        .subcommand(App::new("zsh"))
        .subcommand(App::new("bash"))
}

fn main() {
    let args = app().get_matches();
    eprintln!("{:?}", args);

    match args.subcommand() {
        ("zsh", _) => {
            let mut app = app();
            clap_generate::generate::<Zsh, _>(&mut app, "completion-bug", &mut stdout());
        }
        ("bash", _) => {
            let mut app = app();
            clap_generate::generate::<Bash, _>(&mut app, "completion-bug", &mut stdout());
        }
        _ => unreachable!(),
    };
}

@intgr
Copy link
Contributor

intgr commented Aug 16, 2020

As far as I can tell, clap is annotating the arguments correctly per zsh documentation (http://zsh.sourceforge.net/Doc/Release/Completion-System.html)

From the generated file:

'-f+[]' \

Documentation:

The first argument may appear immediately after optname in the same word, or may appear as a separate word after the option. For example, ‘-foo+:...’ specifies that the completed option and argument will look like either ‘-fooarg’ or ‘-foo arg’.

'--foo=[]' \

The argument may appear as the next word, or in same word as the option name provided that it is separated from it by an equals sign, for example ‘-foo=arg’ or ‘-foo arg’.


I looked at git completions and I still don't understand why --git-dir=.git works with git completions but --foo=x doesn't work with clap's generated file. But in any case the behavior of --git-dir .git is wrong in zsh's built-in git completions as well.

I don't consume the sort of hard drugs necessary to get in the same frame of mind as zsh developers, I don't wish to investigate this further, sorry. :)

@pksunkara
Copy link
Member

Thanks for your help @intgr

@RubixDev
Copy link
Contributor

RubixDev commented Jul 8, 2023

It seems to work fine for me using the latest versions (clap v4.3.11, clap_complete v4.3.2).

Here is the updated code I used
use clap::{value_parser, Arg, Command};
use clap_complete::Shell;
use std::{io::stdout, path::PathBuf};

fn app() -> Command {
    Command::new("completion-bug")
        .arg(
            Arg::new("foo")
                .short('f')
                .long("foo")
                .value_parser(value_parser!(PathBuf)),
        )
        .subcommand(Command::new("zsh"))
        .subcommand(Command::new("bash"))
}

fn main() {
    let args = app().get_matches();
    eprintln!("{:?}", args);

    let shell = match args.subcommand() {
        Some(("zsh", _)) => Shell::Zsh,
        Some(("bash", _)) => Shell::Bash,
        _ => unreachable!(),
    };

    clap_complete::generate(shell, &mut app(), "completion-bug", &mut stdout());
}

@kpcyrd
Copy link
Contributor Author

kpcyrd commented Jul 8, 2023

I also tried again and can confirm zsh tab completions are now working correctly.

Thanks whoever did this! ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-completion Area: completion generator C-bug Category: Updating dependencies E-medium Call for participation: Experience needed to fix: Medium / intermediate 💸 $5
Projects
None yet
Development

No branches or pull requests

6 participants