Skip to content

Should I expect completion of nested command's flags to work? #1916

Description

@dearchap

Discussed in #1915

Originally posted by ldelossa May 25, 2024
Hello,

I have a root command called 'cmd'.
Now I have a sub command hanging off this called "sub-command".
The cli.Command for "sub-command" defines 2 flags "--a, --b".
All commands have the "EnableShellCompletion" boolean set to true.

I'm using zsh. Should I expect to see completion for cmd subcommand --<Tab>.
My assumption was to see completion for --a, --b but instead the only completion I get is for --h, -help essentially.

Activity

  1. dearchap commented on May 25, 2024

    @dearchap
    ContributorAuthor

    @ldelossa what release are you using ?

  2. ldelossa commented on May 26, 2024

    @ldelossa

    Hey there @dearchap

    module github.com/ldelossa/cmds
    
    go 1.22.2
    
    require (
    	github.com/urfave/cli/v3 v3.0.0-alpha9
    	gopkg.in/yaml.v3 v3.0.1
    )
    

    So v3.

    You can see my code here: https://github.com/ldelossa/cmds/blob/main/main.go#L129

    Tho its a bit funky, since I'm generating a command graph based off a yaml config.

  3. dearchap commented on May 26, 2024

    @dearchap
    ContributorAuthor

    @ldelossa Its okay to generate the cmd graph from a yaml. I dont see any real issues with your implementation. It should work. You sourced the zsh completion script for this app correct ? Everytime you hit on the terminal for completion(for this app) the app will be run with the --generate-shell-completion arg so that the app can output the cmd/flags as needed. Have you tried putting this through a debugger to see whats going on ?

  4. ldelossa commented on May 27, 2024

    @ldelossa

    @dearchap Here is a screencap of the issue, as you can see, I can not get tab completion for any flag hanging off the sub command 'automation'

    2024-05-27T11.21.00-04.00.mp4

    I'm not sure how I can debug the simulation of hitting tab at a particular location in the desired command.

    For example, I want my debugger to stop at the code that generates the shell completion, but only after I have the string cmds automation rocky-linux -- as the prefix to completion. Does the --generate-shell-completion command take a string prefix?

    Here is the output from just running the flag:

    cmds --generate-shell-completion
    extract-scripts:extracts all embedded scripts to XDG_CONFIG_DIR/cmds/scripts/
    kernel:Various commands to assist with Linux Kernel development
    ker:Various commands to assist with Linux Kernel development
    rsync:Various Rsync helpers
    rs:Various Rsync helpers
    debug:Various helpers related to debugging software
    automation:Scripts for automating server setup.
    git:Various git helpers
    g:Various git helpers
    k8s:Various Kubernetes helpers
    k:Various Kubernetes helpers
    help:Shows a list of commands or help for one command
    h:Shows a list of commands or help for one command
    

    By the way, this is only a problem with flags, completion of arbitrarily nested sub-command names work fine.

  5. ldelossa commented on May 30, 2024

    @ldelossa

    @dearchap

    Are you sure I should be seeing completion for flags?

    ~/go/cmds main*
    01:38:11PM 🖳  ./cmds automation rocky-linux --generate-shell-completion
    
    ~/go/cmds main*
    01:38:26PM 🖳  ./cmds automation --generate-shell-completion  
    rocky-linux:Setup a Rocky Linux instance for development
    
    ~/go/cmds main*
    01:38:32PM 🖳  ./cmds automation rocky-linux                            
    NAME:
       cmds automation rocky-linux - Setup a Rocky Linux instance for development
    
    USAGE:
       cmds automation rocky-linux [arguments...]
    
    DESCRIPTION:
       Connects to a remote Rocky Linux instance and applies an automated setup.
       Once the setup is finished the instance is capable of compiling and running a custom Linux kernel.
       The automated setup process will interactively prompt for a password for the provided user.
    
    
    OPTIONS:
       --remote value, -r value  The remote host
       --user value, -u value    The non-root user created for general usage
       --help, -h                show help (default: false)
    2024/05/30 13:38:49 Required flags "remote, user" not set
    

    I think the above demonstrates clearly that no flags are being generated for completion.

    I debugged it a bit, but I never see the -- characters being submitted for any completion func to evaluate flags.

  6. ldelossa commented on May 30, 2024

    @ldelossa

    @dearchap

    Here is a repo with a very lean reproduction: https://github.com/ldelossa/urfacecli_test

    Just

    go build -o main .
    source ./main_autocomplete
    

    And try to perform autocompletion for the flag hanging off subcommand in a ZSH instance. It does not work.

  7. dearchap commented on May 30, 2024

    @dearchap
    ContributorAuthor

    @ldelossa I tried out your lean code and it works for me. Are you in a zsh first of all ? also how are you invoking the program main or ./main . For me main is throwing some vague output whereas ./main works just fine with tab completion

    % echo $ZSH_NAME
    zsh
    % echo $ZSH_VERSION
    5.8
    
  8. ldelossa commented on May 30, 2024

    @ldelossa

    Very interesting!

    Okay thank you for confirming this is an issue locally.

    I always invoke with ./main.

    But now, let me enter a zsh with absolute minimum config and see if it works.

  9. ldelossa commented on May 30, 2024

    @ldelossa

    Okay @dearchap

    So I am on zsh 5.9 and this does not work. Here is an example on my system:

    2024-05-30T15.44.59-04.00.mp4

    And here is the script:

    03:44:53PM 🖳  zsh -f
    fedora% echo $ZSH_VERSION 
    5.9
    fedora% autoload -Uz compinit
    fedora% compinit
    fedora% source ./main_autocomplete
    fedora% 
    fedora% 
    fedora% ./main command subcommand 
    NAME:
       cli command subcommand
    
    USAGE:
       cli command subcommand [command [command options]] [arguments...]
    
    COMMANDS:
       help, h  Shows a list of commands or help for one command
    
    OPTIONS:
       --flag value, -f value  flag
       --help, -h              show help (default: false)
    fedora% ./main command subcommand --generate-shell-completion
    help:Shows a list of commands or help for one command
    h:Shows a list of commands or help for one command
    fedora% 
    
  10. ldelossa commented on May 30, 2024

    @ldelossa

    Tried the same thing in a zsh 5.8 container:

    03:50:38PM 🖳  docker run -v $(pwd):/src -it --rm zshusers/zsh:5.8
    6befea125665# cd /src 
    6befea125665# ls -la
    total 4444
    drwxr-xr-x. 1 1000 1000      88 May 30 18:13 .
    drwxr-xr-x. 1 root root     158 May 30 19:50 ..
    drwxr-xr-x. 1 1000 1000     196 May 30 18:14 .git
    -rw-r--r--. 1 1000 1000     154 May 30 18:05 go.mod
    -rw-r--r--. 1 1000 1000    1078 May 30 18:05 go.sum
    -rwxr-xr-x. 1 1000 1000 4528215 May 30 18:12 main
    -rw-r--r--. 1 1000 1000     662 May 30 18:12 main.go
    -rw-r--r--. 1 1000 1000     750 May 30 18:11 main_autocomplete
    6befea125665# autoload -Uz compinit
    6befea125665# compinit
    6befea125665# source ./main_autocomplete 
    6befea125665# ./main command subcommand --
    NAME:
       cli command subcommand
    
    USAGE:
       cli command subcommand [command [command options]] [arguments...]
    
    COMMANDS:
       help, h  Shows a list of commands or help for one command
    
    OPTIONS:
       --flag value, -f value  flag
       --help, -h              show help (default: false)
    6befea125665# ./main command subcommand --generate-shell-completion
    help
    h
    6befea125665# 
    
  11. dearchap commented on May 30, 2024

    @dearchap
    ContributorAuthor

    Are you hitting TAB after the command and you see the subcommand ? If you hit the TAB after subcommand you should see h for help. The generate-shell-completion arg is automatically passed to the program when TAB is hit. You shouldn't need to type in --generate-shell-completion manually unless you are testing the autocomplete behavior . So what exactly is the problem here ? I'm confused

  12. ldelossa commented on May 30, 2024

    @ldelossa

    @dearchap

    I'm trying to get completion for the flag hanging off 'subcommand'.

    For instance if I do this:

    ./main command subcommand -<TAB>
    

    I expect to see completion suggestions like this:

    -f --flag
    

    That was the original question created in the discussion topic.

  13. dearchap commented on May 31, 2024

    @dearchap
    ContributorAuthor

    @ldelossa Ok I understand now. Yes looks like there is an issue. Let me see what I can do

  14. dearchap commented on May 31, 2024

    @dearchap
    ContributorAuthor

    @ldelossa Can you do export URFAVE_CLI_TRACING="on" and then try hitting the TAB during subcommand .?

  15. ldelossa commented on May 31, 2024

    @ldelossa

    No problem @dearchap

    The output was very large so put it in a pastebin: https://pastebin.com/Y84QDy2h

  16. 4 remaining items

  17. ldelossa commented on May 31, 2024

    @ldelossa

    No problem, looking forward to seeing it fixed :-D. Thanks for working with me on explaining this situation.

  18. ldelossa commented on May 31, 2024

    @ldelossa

    @dearchap

    If i comment out:

    		if cmd != nil && cmd.flagSet != nil && cmd.parent != nil {
    			args = cmd.Args().Slice()
    			tracef("running default complete with flags[%v] on command %[1]q", args, cmd.Name)
    		} else {
    			tracef("running default complete with os.Args flags")
    		}
    

    Things begin to work. Maybe this is a bad heuristic, since having defined flags does not necessary mean cmd.Args().Slice() returns anything. IIUC cmd.Args() returns explicitly defined arguments on the command structure.

  19. ldelossa commented on Jun 1, 2024

    @ldelossa

    @dearchap

    Unfortunately, your fix does not fix the issue.

    Like I mentioned above, the replacement of 'args' here:

    	if cmd != nil && cmd.flagSet != nil && cmd.parent != nil {
    		args = cmd.Args().Slice()
    		tracef("running default complete with flags[%v] on command %[2]q", args, cmd.Name)
    	} else {
    		tracef("running default complete with os.Args flags[%v]", args)
    	}
    

    Is breaking things.

    As soon as I do this:

    func DefaultCompleteWithFlags(ctx context.Context, cmd *Command) {
    	args := os.Args
    	if cmd != nil && cmd.flagSet != nil && cmd.parent != nil {
    		// args = cmd.Args().Slice()
    		tracef("running default complete with flags[%v] on command %[2]q", args, cmd.Name)
    	} else {
    		tracef("running default complete with os.Args flags[%v]", args)
    	}
    	argsLen := len(args)
    	lastArg := ""
    	// parent command will have --generate-shell-completion so we need
    	// to account for that
    	if argsLen > 1 {
    		lastArg = args[argsLen-2]
    	} else if argsLen > 0 {
    		lastArg = args[argsLen-1]
    	}
    
    	if strings.HasPrefix(lastArg, "-") {
    		tracef("printing flag suggestion for flag[%v] on command %[1]q", lastArg, cmd.Name)
    		printFlagSuggestions(lastArg, cmd.Flags, cmd.Root().Writer)
    		return
    	}
    
    	if cmd != nil {
    		tracef("printing command suggestions on command %[1]q", cmd.Name)
    		printCommandSuggestions(cmd.Commands, cmd.Root().Writer)
    		return
    	}
    }
    

    Everything works.

    2024-06-01T15.58.15-04.00.mp4
  20. dearchap commented on Jun 1, 2024

    @dearchap
    ContributorAuthor

    @ldelossa did you use the latest fix I just merged ?

  21. ldelossa commented on Jun 1, 2024

    @ldelossa

    Yes, dont you see the code present in the video I just sent? That is latest.

  22. dearchap commented on Jun 2, 2024

    @dearchap
    ContributorAuthor

    @ldelossa sorry yes I was reading the comments in my email and didnt watch the video. Let me dig into this a bit more.

  23. dearchap commented on Jun 2, 2024

    @dearchap
    ContributorAuthor

    @ldelossa The problem is much more complex than I first thought. Can you try with latest code(without your changes) and try just "-" and TAB instead of "--" and TAB ?

  24. ldelossa commented on Jun 2, 2024

    @ldelossa

    @dearchap yup, I tried both, both do not complete.

  25. dearchap commented on Jun 2, 2024

    @dearchap
    ContributorAuthor

    @ldelossa if you comment out the args it might work with flags but will fail with subcommands.

    main command <TAB>
    
  26. ldelossa commented on Jun 2, 2024

    @ldelossa

    Not for me. Everything works when i comment that out. This, i don't know about positional argument competition. I don't use that only flags

  27. dearchap commented on Jun 2, 2024

    @dearchap
    ContributorAuthor

    @ldelossa can you try fix from latest PR ? Kind of kludgy but works

  28. ldelossa commented on Jun 3, 2024

    @ldelossa

    hey @dearchap running out of cycles to keep testing this unfortunately, I provided the test example which should determine if your fixes work or not. I would just ensure to check for all tab completion "-", "--", arguments and subcommands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions