spec: handle custom actions for remainder arguments Do not ignore the action method for remainder and remainder split argument. Signed-off-by: Robin Jarry <robin@jarry.cc>
spec: fix remainder arguments handling after -- After --, only arguments are considered positional. However, if one of them is a remainder argument (i.e. "..."), it needs to be handled in a special way. Check if the current arg is "--" before considering the next argument spec. Also, do not ignore successive "--". Only ignore the first one. Other ones will be assumed as positional arguments. Changing this revealed corner cases with short and long flag handling. Fix them along the way. Adapt unit tests accordingly. Fixes: 69d124e7453e ("Add argument parser based on struct tags") Fixes: a1592749779f ("Add support for long options") Fixes: 4bd5fff1499e ("spec: ignore flags after the special argument --") Signed-off-by: Robin Jarry <robin@jarry.cc>
completion: return descriptions from callback values When an option has a completion callback, include the option description along with the items returned by the callback. Signed-off-by: Robin Jarry <robin@jarry.cc>
mod: add v2 suffix The previous patch modifies the signature of CmdSpec.GetCompletions to return an array of struct Completion objects instead of strings. This is a breaking change. Change the package path to add a /v2 suffix in preparation for the 2.x version series. Signed-off-by: Robin Jarry <robin@jarry.cc>
spec: add description field to arguments Update the autocompletion logic to return an array of Completion structures: type Completion struct { Value string Description string } This will allow application that uses autocompletion to do what it wants with the description. Update test cases to ensure that the expected completions are of type `Completion`. Add test cases to verify that the `description` field is correctly integrated into the autocompletion output. Implements: https://todo.sr.ht/~rjarry/aerc/271 Signed-off-by: Bojan Gabric <bojan@bojangabric.com> Acked-by: Robin Jarry <robin@jarry.cc>
spec: ignore flags after the special argument -- Ignore flags after the special option delimiter argument ('--') and interprete them as non-flag, positional arguments as getopt(3) does. Example: type Foo struct { Force bool `opt:"-f"` Name string `opt:"name"` } Current behavior: $ foo -- -f main.Foo{Force:true, Name:"--"} Expected behavior with this patch: $ foo -- -f main.Foo{Force:false, Name:"-f"} and $ foo -f -- -f main.Foo{Force:true, Name:"-f"} Signed-off-by: Koni Marti <koni.marti@gmail.com> Acked-by: Robin Jarry <robin@jarry.cc>
complete: do not sort completions Let user callbacks sort the completion choices if needed. The flag choices will always be at the end in the order defined in the options struct. Signed-off-by: Robin Jarry <robin@jarry.cc>
shlex: better detect end of arguments Record the actual end index of all arguments in a command line (i.e. the character index after which the closing quote is). Use that information for shifting, cutting, extending and prepending stuff. Also use it for detecting if we are completing a new argument or the current one. Also use it to expose two new API functions to get the leading and trailing whitespace of a command line. Add a basic test case that also checks that an argument with an unclose quote that ends with a space is not assumed to be trailing whitespace. Signed-off-by: Robin Jarry <robin@jarry.cc>
shlex: ignore errors We are not building a shell. We absolutely don't care if a quote is not closed properly. On the contrary, it may hinder the capacity to do argument completion on an unterminated argument. Ignore if the last argument of a command line is not valid from a shell lexical point of view. Signed-off-by: Robin Jarry <robin@jarry.cc>
shlex: remove comment support This library is not meant to build a shell, we don't care about comments. Signed-off-by: Robin Jarry <robin@jarry.cc>
shlex: cosmetic changes Rename constants to upper case with less confusing terminology. Signed-off-by: Robin Jarry <robin@jarry.cc>
treewide: fix (some) linter errors Fix golang-ci lint errors and warnings that make sense to me. Signed-off-by: Robin Jarry <robin@jarry.cc>
complete: add trailing space after flags Always append a trailing space after each completion for flags. It makes it a more natural user experience. Signed-off-by: Robin Jarry <robin@jarry.cc>
completion: be smarter at detecting current argument Record the list of seen arguments in the order in which they appear on the command line. Store the argument indexes along with each option spec. Use this information to determine what argument should be completed instead of trying to guess the option spec based on the last or previous argument. Signed-off-by: Robin Jarry <robin@jarry.cc>
args: add SplitArgs This can be a direct replacement for shlex.Split. Signed-off-by: Robin Jarry <robin@jarry.cc>
args: rename SplitArgs to LexArgs The function name is confusing. It is not actually splitting anything. It is only interpreting shell quotes. Signed-off-by: Robin Jarry <robin@jarry.cc>
readme: add contributing guidelines Allow sending patches to aerc-devel since it is the single user of that lib (yet). Signed-off-by: Robin Jarry <robin@jarry.cc>
Add argument completion support Reuse the argument parser to record what options were seen but ignore all parsing errors. Add support for a new "complete" tag that can be set on struct fields. If present, it should be a method with a pointer receiver to the structure itself with the following signature func(string) []string. It will be invoked with the last argument value before any trailing space. It should return a list of completions associated with this option/argument. By default, only the flags/options will be completed. Signed-off-by: Robin Jarry <robin@jarry.cc>
Add support for long options Add parsing of long options. Arguments can have either short and/or long flags associated with a struct field. These syntaxes are supported and equivalent: -j8 -j 8 --jobs 8 --jobs=8 Signed-off-by: Robin Jarry <robin@jarry.cc>
Add argument parser based on struct tags Use runtime reflection to dynamically generate argument parsers based on struct fields tags. The basic usage is as follows: ```go package main import ( "fmt" "log" "git.sr.ht/~rjarry/go-opt" ) type Foo struct { Delay time.Duration `opt:"-t" action:"ParseDelay" default:"1s"` Force bool `opt:"-f"` Name string `opt:"name" required:"false" metavar:"FOO"` Cmd []string `opt:"..."` } func (f *Foo) ParseDelay(arg string) error { d, err := time.ParseDuration(arg) if err != nil { return err } f.Delay = d return nil } func main() { var foo Foo err := opt.CmdlineToStruct("foo -f bar baz 'xy z' ", &foo) if err != nil { log.Fatalf("error: %s\n", err) } fmt.Printf("%#v\n", foo) } ``` ```console $ foo -f bar baz 'xy z' Foo{Delay: 1000000000, Force: true, Name: "bar", Cmd: []string{"baz", "xy z"}} $ foo -t error: -t takes a value. Usage: foo [-t <delay>] [-f] [<name>] [<cmd>...] ``` There is a set of tags that can be set on struct fields: `opt:"-f"` Registers that this field is associated to the specified flag. Unless a custom `action` method is specified, the flag value will be automatically converted from string to the field type (only basic scalar types are supported: all integers (both signed and unsigned), all floats and strings. If the field type is `bool`, the flag will take no value. Only supports short flags for now. `opt:"blah"` Field is associated to a positional argument. `opt:"..."` Field will be mapped to all remaining arguments. If the field is `string`, the raw command line will be stored preserving any shell quoting, otherwise the field needs to be `[]string` and will receive the remaining arguments after interpreting shell quotes. `opt:"-"` Special value to indicate that this command will accept any argument without any check nor parsing. The field on which it is set will not be updated and should be called `Unused struct{}` as a convention. The field name must start with an upper case to avoid linter warnings because of unused fields. `action:"ParseFoo"` Custom method to be used instead of the default automatic conversion. Needs to be a method with a pointer receiver to the struct itself, takes a single `string` argument and may return an `error` to abort parsing. The `action` method is responsible of updating the struct. `default:"foobaz"` Default `string` value if not specified by the user. Will be processed by the same conversion/parsing as any other argument. `metavar:"foo|bar|baz"` Displayed name for argument values in the generated usage help. `required:"true|false"` By default, flag arguments are optional and positional arguments are required. Using this tag allows changing that default behaviour. If an argument is not required, it will be surrounded by square brackets in the generated usage help. `aliases:"cmd1,cmd2"` By default, arguments are interpreted for all command aliases. If this is specified, this field/option will only be applicable to the specified command aliases. Caveats: Depending on field types, the argument string values are parsed using the appropriate conversion function. If no `opt` tag is set on a field, it will be excluded from automated argument parsing. It can still be updated indirectly via a custom `action` method. Short flags can be combined like with getopt(): * Flags with no value: -abc is equivalent to -a -b -c * Flags with a value (options): -j9 is equivalent to -j 9 Signed-off-by: Robin Jarry <robin@jarry.cc>