~rjarry/go-opt

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>
Add shell command lexer

Add a basic shell lexer that splits arguments, interpreting quotes and
escape sequences. It behaves similarly to shlex.Split but preserves the
original command line.

Signed-off-by: Robin Jarry <robin@jarry.cc>
Add base project files

Signed-off-by: Robin Jarry <robin@jarry.cc>
Next