DEV Community

Cover image for A pragmatic approach to shell completion
rsteube
rsteube

Posted on

A pragmatic approach to shell completion

Top comments (11)

Collapse
 
rsteube profile image
rsteube

@anderssonpeter feel free to export the subcommands/flags from the completers for that if it's any help

btw. powershell should work quite alright:
asciicast

Collapse
 
anderssonpeter profile image
Peter • Edited

Hi you have al lot of completers there, i have almost no knowledge of go but is there any way the definition of them could be converted/exported to json, xml, yml or some other easily read file format?

If then i could convert them to my own definition format.

Mine can easily be converted to other formats (excluding dynamic lists like for example git checkout lists the braches in the current directory).

So if i ever implement a completer that you lack feel free to convert it and add it to your tool! (if you need help doing the conversion to json, xml or yml dont hesitate to ask), currently i only have git and its not done yet, has alot of commands and parameters..

Collapse
 
rsteube profile image
rsteube • Edited

Yes, thats what i mean. It's a bit of work but you could add a Run function to the root command and traverse over the subcommands and flags like this:

    Run: func(cmd *cobra.Command, args []string) {
      for _, c := range cmd.Commands() {
          c.LocalFlags().VisitAll(func(f *pflag.Flag) {

          })
      }
    },
Enter fullscreen mode Exit fullscreen mode

Either do some basic printing or create a custom struct and marshal it as json.

Thread Thread
 
rsteube profile image
rsteube

It's actually pretty simple so I added an export:

carapace chown export
Enter fullscreen mode Exit fullscreen mode
 {"Name":"chown","Description":"change file owner and group","LocalFlags":[{"Shorthand":"H","Description":"if a command line argument is a symbolic link to a directory, traverse it","Type":"bool"},{"Shorthand":"L","Description":"traverse every symbolic link to a directory encountered","Type":"bool"},{"Shorthand":"P","Description":"do not traverse any symbolic links (default)","Type":"bool"},{"Longhand":"changes","Shorthand":"c","Description":"like verbose but report only when a change is made","Type":"bool"},{"Longhand":"dereference","Description":"affect the referent of each symbolic link","Type":"bool"},{"Longhand":"from","Description":"change the owner and/or group of each file only if its current owner and/or group match those specified here.","Type":"string"},{"Longhand":"help","Description":"display this help and exit","Type":"bool"},{"Longhand":"no-dereference","Shorthand":"h","Description":"affect symbolic links instead of any referenced file","Type":"bool"},{"Longhand":"no-preserve-root","Description":"do not treat '/' specially (the default)","Type":"bool"},{"Longhand":"preserve-root","Description":"fail to operate recursively on '/'","Type":"bool"},{"Longhand":"recursive","Shorthand":"R","Description":"operate on files and directories recursively","Type":"bool"},{"Longhand":"reference","Description":"use RFILE's owner and group rather than specifying OWNER:GROUP values","Type":"string"},{"Longhand":"silent","Shorthand":"f","Description":"suppress most error messages","Type":"string"},{"Longhand":"verbose","Shorthand":"v","Description":"output a diagnostic for every file processed","Type":"bool"},{"Longhand":"version","Description":"output version information and exit","Type":"bool"}]}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
anderssonpeter profile image
Peter • Edited

I started writing a export function but my lacking knowledge of go stopped me..
So thanks I will try out your export function instead :)

But just because I'm curious what did I do wrong here?

type Element struct {
    Type  string `json:"type"`
    Use   string `json:"user"`
    Short string `json:"short"`
    Long  string `json:"long"`

    Name      string `json:"name"`
    Shorthand string `json:"shorthand"`
    Usage     string `json:"usage"`

    Elements []Element `json:"elements"`
}
Enter fullscreen mode Exit fullscreen mode
Run: func(cmd *cobra.Command, args []string) {
    var rootElement = Element{Type: "root", Use: cmd.Use, Short: cmd.Short, Long: cmd.Long}

    for _, c := range cmd.Commands() {
        var commandElement = Element{Type: "command", Use: c.Use, Short: c.Short, Long: c.Long}
        rootElement.Elements = append(rootElement.Elements, commandElement)
        c.LocalFlags().VisitAll(func(f *pflag.Flag) {
            var flagElement = Element{Type: f.Value.Type(), Name: f.Name, Shorthand: f.Shorthand, Usage: f.Usage}
            commandElement.Elements = append(commandElement.Elements, flagElement)
        })
    }
    bytes, err := json.Marshal(rootElement)
    if err != nil {
        fmt.Printf("Error: %s", err)
    }
    fmt.Println(string(bytes))
},
Enter fullscreen mode Exit fullscreen mode

my rootElement is correct it has all the command elements as expected, but my commandElements have lost all their flags? they are there while inside c.LocalFlags().VisitAll( but when I get to bytes, err := json.Marshal(rootElement) they are gone?

Thread Thread
 
rsteube profile image
rsteube • Edited

Not too sure with only a quick view on it, but my first guess would be that it's probably the difference of a *Element reference to a Element copy.
Meaning you are altering command.Element but rootElement.Elements might contain a different object.
Try putting rootElement.Elements = append(rootElement.Elements, commandElement) after the VisitAll.

Thread Thread
 
anderssonpeter profile image
Peter

Thanks for your help I managed to get a export running after finding the carapace-bin repo!!

Thread Thread
 
rsteube profile image
rsteube

Ah yes, might get a bit lost in the text. For everyone else: github.com/rsteube/carapace-bin

Thread Thread
 
anderssonpeter profile image
Peter

After thinking about my go issue i think you are right, when i added the command element to the root command, it took a copy of my variable and stored it.
So all the changes i did after got lost. Not that it matters your solution was much better than mine :).

Collapse
 
anderssonpeter profile image
Peter

I am trying to solve the same issue but for powershell with github.com/AnderssonPeter/PowerType

Collapse
 
sso profile image
Sall

Nice post, your knowledge could enhance user experience of
z-shell.pages.dev :)