diff options
34 files changed, 145 insertions, 88 deletions
diff --git a/app/aerc.go b/app/aerc.go index 107fc1f3..72a60562 100644 --- a/app/aerc.go +++ b/app/aerc.go @@ -11,7 +11,7 @@ import ( "time" "unicode" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rockorager/vaxis" "github.com/ProtonMail/go-crypto/openpgp" "github.com/emersion/go-message/mail" @@ -29,7 +29,7 @@ type Aerc struct { accounts map[string]*AccountView cmd func(string, *config.AccountConfig, *models.MessageInfo) error cmdHistory lib.History - complete func(cmd string) ([]string, string) + complete func(cmd string) ([]opt.Completion, string) focused ui.Interactive grid *ui.Grid simulating int @@ -54,7 +54,7 @@ type Choice struct { func (aerc *Aerc) Init( crypto crypto.Provider, cmd func(string, *config.AccountConfig, *models.MessageInfo) error, - complete func(cmd string) ([]string, string), cmdHistory lib.History, + complete func(cmd string) ([]opt.Completion, string), cmdHistory lib.History, deferLoop chan struct{}, ) { tabs := ui.NewTabs(func(d ui.Drawable) *config.UIConfig { @@ -317,7 +317,7 @@ func (aerc *Aerc) simulate(strokes []config.KeyStroke) { aerc.simulating -= 1 if exline, ok := aerc.focused.(*ExLine); ok { // we are still focused on the exline, turn on tab complete - exline.TabComplete(func(cmd string) ([]string, string) { + exline.TabComplete(func(cmd string) ([]opt.Completion, string) { return aerc.complete(cmd) }) if complete { @@ -633,14 +633,12 @@ func (aerc *Aerc) focus(item ui.Interactive) { func (aerc *Aerc) BeginExCommand(cmd string) { previous := aerc.focused - var tabComplete func(string) ([]string, string) + var tabComplete func(string) ([]opt.Completion, string) if aerc.simulating != 0 { // Don't try to draw completions for simulated events tabComplete = nil } else { - tabComplete = func(cmd string) ([]string, string) { - return aerc.complete(cmd) - } + tabComplete = aerc.complete } exline := NewExLine(cmd, func(cmd string) { err := aerc.cmd(cmd, nil, nil) @@ -673,7 +671,7 @@ func (aerc *Aerc) RegisterPrompt(prompt string, cmd string) { if err != nil { aerc.PushError(err.Error()) } - }, func(cmd string) ([]string, string) { + }, func(cmd string) ([]opt.Completion, string) { return nil, "" // TODO: completions }) aerc.prompts.Push(p) @@ -700,7 +698,7 @@ func (aerc *Aerc) RegisterChoices(choices []Choice) { if err != nil { aerc.PushError(err.Error()) } - }, func(cmd string) ([]string, string) { + }, func(cmd string) ([]opt.Completion, string) { return nil, "" // TODO: completions }) aerc.prompts.Push(p) @@ -10,6 +10,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" + "git.sr.ht/~rjarry/go-opt/v2" "github.com/ProtonMail/go-crypto/openpgp" ) @@ -18,7 +19,7 @@ var aerc Aerc func Init( crypto crypto.Provider, cmd func(string, *config.AccountConfig, *models.MessageInfo) error, - complete func(cmd string) ([]string, string), history lib.History, + complete func(cmd string) ([]opt.Completion, string), history lib.History, deferLoop chan struct{}, ) { aerc.Init(crypto, cmd, complete, history, deferLoop) diff --git a/app/exline.go b/app/exline.go index e8b0069e..5f8e4280 100644 --- a/app/exline.go +++ b/app/exline.go @@ -4,19 +4,20 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" "git.sr.ht/~rjarry/aerc/lib/ui" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rockorager/vaxis" ) type ExLine struct { commit func(cmd string) finish func() - tabcomplete func(cmd string) ([]string, string) + tabcomplete func(cmd string) ([]opt.Completion, string) cmdHistory lib.History input *ui.TextInput } func NewExLine(cmd string, commit func(cmd string), finish func(), - tabcomplete func(cmd string) ([]string, string), + tabcomplete func(cmd string) ([]opt.Completion, string), cmdHistory lib.History, ) *ExLine { input := ui.NewTextInput("", config.Ui).Prompt(":").Set(cmd) @@ -38,7 +39,7 @@ func NewExLine(cmd string, commit func(cmd string), finish func(), return exline } -func (x *ExLine) TabComplete(tabComplete func(string) ([]string, string)) { +func (x *ExLine) TabComplete(tabComplete func(string) ([]opt.Completion, string)) { x.input.TabComplete( tabComplete, config.Ui.CompletionDelay, @@ -48,7 +49,7 @@ func (x *ExLine) TabComplete(tabComplete func(string) ([]string, string)) { } func NewPrompt(prompt string, commit func(text string), - tabcomplete func(cmd string) ([]string, string), + tabcomplete func(cmd string) ([]opt.Completion, string), ) *ExLine { input := ui.NewTextInput("", config.Ui).Prompt(prompt) if config.Ui.CompletionPopovers { diff --git a/app/msgviewer.go b/app/msgviewer.go index 0af87e5e..0c2c1bef 100644 --- a/app/msgviewer.go +++ b/app/msgviewer.go @@ -23,7 +23,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/parse" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/models" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rockorager/vaxis" "git.sr.ht/~rockorager/vaxis/widgets/align" diff --git a/commands/account/cf.go b/commands/account/cf.go index 8d7d27ae..2c2cee53 100644 --- a/commands/account/cf.go +++ b/commands/account/cf.go @@ -10,7 +10,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/state" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) type ChangeFolder struct { diff --git a/commands/account/mkdir.go b/commands/account/mkdir.go index 13310665..d6a6f9f3 100644 --- a/commands/account/mkdir.go +++ b/commands/account/mkdir.go @@ -7,7 +7,7 @@ import ( "git.sr.ht/~rjarry/aerc/app" "git.sr.ht/~rjarry/aerc/commands" "git.sr.ht/~rjarry/aerc/worker/types" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) type MakeDir struct { diff --git a/commands/account/rmdir.go b/commands/account/rmdir.go index ff1463b6..3c5b24d7 100644 --- a/commands/account/rmdir.go +++ b/commands/account/rmdir.go @@ -9,7 +9,7 @@ import ( "git.sr.ht/~rjarry/aerc/commands" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) type RemoveDir struct { diff --git a/commands/commands.go b/commands/commands.go index 27e50bcf..e87cd802 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -9,7 +9,7 @@ import ( "strings" "unicode" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rjarry/aerc/app" "git.sr.ht/~rjarry/aerc/config" @@ -272,7 +272,7 @@ func GetTemplateCompletion( // GetCompletions returns the completion options and the command prefix func GetCompletions( cmd Command, args *opt.Args, -) (options []string, prefix string) { +) (options []opt.Completion, prefix string) { // copy zeroed struct tmp := reflect.New(reflect.TypeOf(cmd)).Interface().(Command) s, err := args.ArgSafe(0) diff --git a/commands/completion_helpers.go b/commands/completion_helpers.go index 92c33bea..8d293a32 100644 --- a/commands/completion_helpers.go +++ b/commands/completion_helpers.go @@ -32,7 +32,7 @@ func GetAddress(search string) []string { if cmpl != nil { addrList, _ := cmpl.ForHeader("to")(search) for _, full := range addrList { - addr, err := mail.ParseAddress(full) + addr, err := mail.ParseAddress(full.Value) if err != nil { continue } diff --git a/commands/compose/send.go b/commands/compose/send.go index aefeaa3c..1e30bd0e 100644 --- a/commands/compose/send.go +++ b/commands/compose/send.go @@ -19,6 +19,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/send" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" + "git.sr.ht/~rjarry/go-opt/v2" "github.com/emersion/go-message/mail" ) @@ -142,12 +143,13 @@ func (s Send) Execute(args []string) error { from, rcpts, tab.Name, s.CopyTo, s.Archive, copyToReplied) } - }, func(cmd string) ([]string, string) { + }, func(cmd string) ([]opt.Completion, string) { + var comps []opt.Completion if cmd == "" { - return []string{"y", "n"}, "" + comps = append(comps, opt.Completion{Value: "y"}) + comps = append(comps, opt.Completion{Value: "n"}) } - - return nil, "" + return comps, "" }, ) diff --git a/commands/menu.go b/commands/menu.go index 6ebba81f..08eec9cb 100644 --- a/commands/menu.go +++ b/commands/menu.go @@ -12,7 +12,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/log" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/models" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) type Menu struct { diff --git a/commands/patch/find.go b/commands/patch/find.go index a508551d..10b96ef2 100644 --- a/commands/patch/find.go +++ b/commands/patch/find.go @@ -11,7 +11,7 @@ import ( "git.sr.ht/~rjarry/aerc/commands/account" "git.sr.ht/~rjarry/aerc/lib/pama" "git.sr.ht/~rjarry/aerc/lib/pama/models" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) type Find struct { diff --git a/commands/patch/list.go b/commands/patch/list.go index 0451605b..b05a9bf3 100644 --- a/commands/patch/list.go +++ b/commands/patch/list.go @@ -13,7 +13,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/pama" "git.sr.ht/~rjarry/aerc/lib/pama/models" "git.sr.ht/~rjarry/aerc/lib/ui" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rockorager/vaxis" ) diff --git a/commands/patch/patch.go b/commands/patch/patch.go index 25d7850a..15fb35f4 100644 --- a/commands/patch/patch.go +++ b/commands/patch/patch.go @@ -5,7 +5,7 @@ import ( "fmt" "git.sr.ht/~rjarry/aerc/commands" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) var subCommands map[string]commands.Command @@ -68,7 +68,11 @@ func (p *Patch) CompleteSubArgs(arg string) []string { } // prepend arbitrary string to arg to work with sub-commands options, _ := commands.GetCompletions(p.SubCmd, opt.LexArgs("a "+arg)) - return options + completions := make([]string, 0, len(options)) + for _, o := range options { + completions = append(completions, o.Value) + } + return completions } func (p Patch) Execute(args []string) error { diff --git a/commands/prompt.go b/commands/prompt.go index 9ab2aac1..fe6632e9 100644 --- a/commands/prompt.go +++ b/commands/prompt.go @@ -1,7 +1,7 @@ package commands import ( - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rjarry/aerc/app" ) diff --git a/commands/util.go b/commands/util.go index bb20a204..8c1e497d 100644 --- a/commands/util.go +++ b/commands/util.go @@ -18,7 +18,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/xdg" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rockorager/vaxis" ) diff --git a/completer/completer.go b/completer/completer.go index 5a5dea10..c5b61528 100644 --- a/completer/completer.go +++ b/completer/completer.go @@ -13,7 +13,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/format" "git.sr.ht/~rjarry/aerc/lib/log" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) // A Completer is used to autocomplete text inputs based on the configured @@ -31,7 +31,7 @@ type Completer struct { // A CompleteFunc accepts a string to be completed and returns a slice of // completions candidates with a prefix to prepend to the chosen candidate -type CompleteFunc func(string) ([]string, string) +type CompleteFunc func(string) ([]opt.Completion, string) // New creates a new Completer with the specified address book command. func New(addressBookCmd string, errHandler func(error)) *Completer { @@ -51,11 +51,11 @@ func (c *Completer) ForHeader(h string) CompleteFunc { return nil } // wrap completeAddress in an error handler - return func(s string) ([]string, string) { + return func(s string) ([]opt.Completion, string) { completions, prefix, err := c.completeAddress(s) if err != nil { c.handleErr(err) - return []string{}, "" + return []opt.Completion{}, "" } return completions, prefix } @@ -80,7 +80,7 @@ var tooManyLines = fmt.Errorf("returned more than %d lines", maxCompletionLines) // completeAddress uses the configured address book completion command to fetch // completions for the specified string, returning a slice of completions and // a prefix to be prepended to the selected completion, or an error. -func (c *Completer) completeAddress(s string) ([]string, string, error) { +func (c *Completer) completeAddress(s string) ([]opt.Completion, string, error) { prefix, candidate := c.parseAddress(s) cmd, err := c.getAddressCmd(candidate) if err != nil { @@ -156,9 +156,9 @@ func (c *Completer) getAddressCmd(s string) (*exec.Cmd, error) { // must consist of tab-delimited fields. Only the first field (the email // address field) is required, the second field (the contact name) is optional, // and subsequent fields are ignored. -func readCompletions(r io.Reader) ([]string, error) { +func readCompletions(r io.Reader) ([]opt.Completion, error) { buf := bufio.NewReader(r) - completions := []string{} + var completions []opt.Completion for i := 0; i < maxCompletionLines; i++ { line, err := buf.ReadString('\n') if errors.Is(err, io.EOF) { @@ -180,7 +180,9 @@ func readCompletions(r io.Reader) ([]string, error) { if len(parts) > 1 { addr.Name = strings.TrimSpace(parts[1]) } - completions = append(completions, format.AddressForHumans(addr)) + completions = append(completions, opt.Completion{ + Value: format.AddressForHumans(addr), + }) } return completions, tooManyLines } diff --git a/config/style.go b/config/style.go index a8f17e8d..c81e7204 100644 --- a/config/style.go +++ b/config/style.go @@ -54,6 +54,7 @@ const ( STYLE_PART_MIMETYPE STYLE_COMPLETION_DEFAULT + STYLE_COMPLETION_DESCRIPTION STYLE_COMPLETION_GUTTER STYLE_COMPLETION_PILL @@ -105,9 +106,10 @@ var StyleNames = map[string]StyleObject{ "part_filename": STYLE_PART_FILENAME, "part_mimetype": STYLE_PART_MIMETYPE, - "completion_default": STYLE_COMPLETION_DEFAULT, - "completion_gutter": STYLE_COMPLETION_GUTTER, - "completion_pill": STYLE_COMPLETION_PILL, + "completion_default": STYLE_COMPLETION_DEFAULT, + "completion_description": STYLE_COMPLETION_DESCRIPTION, + "completion_gutter": STYLE_COMPLETION_GUTTER, + "completion_pill": STYLE_COMPLETION_PILL, "tab": STYLE_TAB, "stack": STYLE_STACK, @@ -337,9 +339,11 @@ selector_chooser.bold = true selector_focused.bold = true selector_focused.bg = 12 selector_focused.fg = 15 +completion_*.bg = 8 completion_pill.bg = 12 -completion_default.bg = 8 completion_default.fg = 15 +completion_description.fg = 15 +completion_description.dim = true ` func NewStyleSet() StyleSet { diff --git a/doc/aerc-stylesets.7.scd b/doc/aerc-stylesets.7.scd index ad05d69c..2a311db2 100644 --- a/doc/aerc-stylesets.7.scd +++ b/doc/aerc-stylesets.7.scd @@ -135,6 +135,8 @@ styling. : Attachment/part MIME type in the part switcher. | *completion_default* : The default style for the completion engine. +| *completion_description* +: Completion item descriptions. | *completion_gutter* : The completion gutter. | *completion_pill* @@ -384,9 +386,11 @@ selector_chooser.bold = true selector_focused.bold = true selector_focused.bg = 12 selector_focused.fg = 15 +completion_*.bg = 8 completion_pill.bg = 12 -completion_default.bg = 8 completion_default.fg = 15 +completion_description.fg = 15 +completion_description.dim = true [viewer] url.underline = true @@ -3,7 +3,7 @@ module git.sr.ht/~rjarry/aerc go 1.21 require ( - git.sr.ht/~rjarry/go-opt v1.4.0 + git.sr.ht/~rjarry/go-opt/v2 v2.0.1 git.sr.ht/~rockorager/go-jmap v0.5.0 git.sr.ht/~rockorager/vaxis v0.10.3 github.com/ProtonMail/go-crypto v1.0.0 @@ -1,5 +1,5 @@ -git.sr.ht/~rjarry/go-opt v1.4.0 h1:YHUKRXOuoy6d57Jt2b0DRSLbxq0mz5biq+6P/npFK5s= -git.sr.ht/~rjarry/go-opt v1.4.0/go.mod h1:oEPZUTJKGn1FVye0znaLoeskE/QTuyoJw5q+fjusdM4= +git.sr.ht/~rjarry/go-opt/v2 v2.0.1 h1:rNag0btxzpPN9FOPEqJfmFY70R9Zqf7M1lbNdy6+jvM= +git.sr.ht/~rjarry/go-opt/v2 v2.0.1/go.mod h1:ZIcXh1fUrJEE5bdfaOpx5Uk9YURsimePQ7JJpitDZq4= git.sr.ht/~rockorager/go-jmap v0.5.0 h1:Xs8NeqpA631HUz4uIe6V+0CpWt6b+nnHF7S14U2BVPA= git.sr.ht/~rockorager/go-jmap v0.5.0/go.mod h1:aOTCtwpZSINpDDSOkLGpHU0Kbbm5lcSDMcobX3ZtOjY= git.sr.ht/~rockorager/vaxis v0.10.3 h1:r9oHYKPfItWh05SQE/UJ4wDrmWRY9MDJKtA9HEl5nBI= @@ -77,6 +77,7 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= @@ -167,6 +168,7 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -174,6 +176,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/lib/open.go b/lib/open.go index 5ca819e0..edbc7ff6 100644 --- a/lib/open.go +++ b/lib/open.go @@ -6,7 +6,7 @@ import ( "runtime" "strings" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "github.com/danwakefield/fnmatch" "git.sr.ht/~rjarry/aerc/config" diff --git a/lib/send/sendmail.go b/lib/send/sendmail.go index b267721e..9d98cf8f 100644 --- a/lib/send/sendmail.go +++ b/lib/send/sendmail.go @@ -6,7 +6,7 @@ import ( "net/url" "os/exec" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "github.com/emersion/go-message/mail" "github.com/pkg/errors" ) diff --git a/lib/ui/textinput.go b/lib/ui/textinput.go index 6c4551b3..d52859b7 100644 --- a/lib/ui/textinput.go +++ b/lib/ui/textinput.go @@ -10,6 +10,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/log" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rockorager/vaxis" ) @@ -27,8 +28,8 @@ type TextInput struct { text []vaxis.Character change []func(ti *TextInput) focusLost []func(ti *TextInput) - tabcomplete func(s string) ([]string, string) - completions []string + tabcomplete func(s string) ([]opt.Completion, string) + completions []opt.Completion prefix string completeIndex int completeDelay time.Duration @@ -62,7 +63,7 @@ func (ti *TextInput) Prompt(prompt string) *TextInput { } func (ti *TextInput) TabComplete( - tabcomplete func(s string) ([]string, string), + tabcomplete func(s string) ([]opt.Completion, string), d time.Duration, minChars int, key *config.KeyStroke, ) *TextInput { ti.tabcomplete = tabcomplete @@ -142,8 +143,24 @@ func (ti *TextInput) drawPopover(ctx *Context) { if len(ti.completions) == 0 { return } - cmp := &completions{ti: ti} - width := maxLen(ti.completions) + 3 + + valWidth := 0 + descWidth := 0 + for _, c := range ti.completions { + valWidth = max(valWidth, runewidth.StringWidth(unquote(c.Value))) + descWidth = max(descWidth, runewidth.StringWidth(c.Description)) + } + descWidth = min(descWidth, 80) + // one space padding + width := 1 + valWidth + if descWidth != 0 { + // two spaces padding + parentheses + width += 2 + descWidth + 2 + } + // one space padding + gutter + width += 2 + + cmp := &completions{ti: ti, valWidth: valWidth, descWidth: descWidth} height := len(ti.completions) pos := len(ti.prefix) - ti.scroll @@ -275,7 +292,7 @@ func (ti *TextInput) backspace() { func (ti *TextInput) executeCompletion() { if len(ti.completions) > 0 { - ti.Set(ti.prefix + ti.completions[ti.completeIndex] + ti.StringRight()) + ti.Set(ti.prefix + ti.completions[ti.completeIndex].Value + ti.StringRight()) } } @@ -402,7 +419,9 @@ func (ti *TextInput) Event(event vaxis.Event) bool { } type completions struct { - ti *TextInput + ti *TextInput + valWidth int + descWidth int } func unquote(s string) string { @@ -412,22 +431,13 @@ func unquote(s string) string { return s } -func maxLen(ss []string) int { - max := 0 - for _, s := range ss { - l := runewidth.StringWidth(unquote(s)) - if l > max { - max = l - } - } - return max -} - func (c *completions) Draw(ctx *Context) { bg := c.ti.uiConfig.GetStyle(config.STYLE_COMPLETION_DEFAULT) + bgDesc := c.ti.uiConfig.GetStyle(config.STYLE_COMPLETION_DESCRIPTION) gutter := c.ti.uiConfig.GetStyle(config.STYLE_COMPLETION_GUTTER) pill := c.ti.uiConfig.GetStyle(config.STYLE_COMPLETION_PILL) sel := c.ti.uiConfig.GetStyleSelected(config.STYLE_COMPLETION_DEFAULT) + selDesc := c.ti.uiConfig.GetStyleSelected(config.STYLE_COMPLETION_DESCRIPTION) ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', bg) @@ -445,11 +455,20 @@ func (c *completions) Draw(ctx *Context) { if idx > endIdx { continue } + val := runewidth.FillRight(unquote(opt.Value), c.valWidth) + desc := opt.Description + if desc != "" { + if runewidth.StringWidth(desc) > c.descWidth { + desc = runewidth.Truncate(desc, c.descWidth, "…") + } + desc = " " + runewidth.FillRight("("+desc+")", c.descWidth+2) + } if c.index() == idx { - ctx.Fill(0, idx-startIdx, ctx.Width(), 1, ' ', sel) - ctx.Printf(0, idx-startIdx, sel, " %s ", unquote(opt)) + n := ctx.Printf(0, idx-startIdx, sel, " %s", val) + ctx.Printf(n, idx-startIdx, selDesc, "%s ", desc) } else { - ctx.Printf(0, idx-startIdx, bg, " %s ", unquote(opt)) + n := ctx.Printf(0, idx-startIdx, bg, " %s", val) + ctx.Printf(n, idx-startIdx, bgDesc, "%s ", desc) } } @@ -548,23 +567,23 @@ func (c *completions) stem(stem string) { c.ti.index = len(vaxis.Characters(c.ti.prefix + stem)) } -func findStem(words []string) string { +func findStem(words []opt.Completion) string { if len(words) == 0 { return "" } if len(words) == 1 { - return words[0] + return words[0].Value } var stem string stemLen := 1 - firstWord := []rune(words[0]) + firstWord := []rune(words[0].Value) for { if len(firstWord) < stemLen { return stem } var r rune = firstWord[stemLen-1] for _, word := range words[1:] { - runes := []rune(word) + runes := []rune(word.Value) if len(runes) < stemLen { return stem } @@ -11,7 +11,7 @@ import ( "sync" "time" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" "git.sr.ht/~rjarry/aerc/app" "git.sr.ht/~rjarry/aerc/commands" @@ -50,24 +50,36 @@ func execCommand( return err } -func getCompletions(cmdline string) ([]string, string) { +func getCompletions(cmdline string) ([]opt.Completion, string) { // complete template terms if options, prefix, ok := commands.GetTemplateCompletion(cmdline); ok { sort.Strings(options) - return options, prefix + completions := make([]opt.Completion, 0, len(options)) + for _, o := range options { + completions = append(completions, opt.Completion{ + Value: o, + Description: "Template", + }) + } + return completions, prefix } args := opt.LexArgs(cmdline) if args.Count() < 2 && args.TrailingSpace() == "" { // complete command names - var completions []string + var completions []opt.Completion for _, name := range commands.ActiveCommandNames() { if strings.HasPrefix(name, cmdline) { - completions = append(completions, name+" ") + completions = append(completions, opt.Completion{ + Value: name + " ", + Description: "", + }) } } - sort.Strings(completions) + sort.Slice(completions, func(i, j int) bool { + return completions[i].Value < completions[j].Value + }) return completions, "" } diff --git a/stylesets/blue b/stylesets/blue index 4fffca0a..7c1169fb 100644 --- a/stylesets/blue +++ b/stylesets/blue @@ -43,7 +43,6 @@ part_*.selected.fg=#ffffff part_*.selected.bg=#005f87 part_filename.selected.bold=true -completion_pill.reverse=true selector_focused.bold=true selector_focused.bg=#005f87 selector_focused.fg=white @@ -54,7 +53,9 @@ default.selected.bold=true default.selected.fg=white default.selected.bg=#005f87 +completion_pill.reverse=true completion_default.selected.bg=#005f87 +completion_description.dim=true [viewer] *.default=true diff --git a/stylesets/default b/stylesets/default index dd5f4a8d..22ea1701 100644 --- a/stylesets/default +++ b/stylesets/default @@ -48,9 +48,11 @@ #selector_focused.bg = 12 #selector_focused.fg = 15 +#completion_*.bg = 8 #completion_pill.bg = 12 -#completion_default.bg = 8 #completion_default.fg = 15 +#completion_description.fg = 15 +#completion_description.dim = true #[viewer] # Uncomment these two lines to reset all attributes in the [viewer] section. diff --git a/stylesets/monochrome b/stylesets/monochrome index 42d5352d..00b7f51b 100644 --- a/stylesets/monochrome +++ b/stylesets/monochrome @@ -30,6 +30,7 @@ part_filename.selected.bold = true selector_focused.reverse = true selector_chooser.bold = true +completion_description.dim = true [viewer] *.default = true diff --git a/stylesets/nord b/stylesets/nord index 3d1d76a3..d8388b94 100644 --- a/stylesets/nord +++ b/stylesets/nord @@ -18,6 +18,7 @@ statusline_default.reverse=true statusline_error.reverse=true completion_pill.reverse=true +completion_description.dim=true border.fg = #49576b diff --git a/stylesets/pink b/stylesets/pink index 87428874..65ef2fe2 100644 --- a/stylesets/pink +++ b/stylesets/pink @@ -43,7 +43,6 @@ part_*.selected.fg=#ffffff part_*.selected.bg=#de4e85 part_filename.selected.bold=true -completion_pill.reverse=true selector_focused.bold=true selector_focused.bg=#de4e85 selector_focused.fg=white @@ -54,7 +53,9 @@ default.selected.bold=true default.selected.fg=white default.selected.bg=#de4e85 +completion_pill.reverse=true completion_default.selected.bg=#de4e85 +completion_description.dim=true [viewer] *.default=true diff --git a/stylesets/solarized b/stylesets/solarized index 29717c72..9308d785 100644 --- a/stylesets/solarized +++ b/stylesets/solarized @@ -8,6 +8,7 @@ *error.bold=true border.reverse=true completion_pill.reverse=true +completion_description.dim=true error.fg=#dc322f # red header.bold=true selector_chooser.bold=true diff --git a/worker/imap/search.go b/worker/imap/search.go index 970d4db6..4bcff69a 100644 --- a/worker/imap/search.go +++ b/worker/imap/search.go @@ -6,7 +6,7 @@ import ( "github.com/emersion/go-imap" "git.sr.ht/~rjarry/aerc/worker/types" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) func translateSearch(c *types.SearchCriteria) *imap.SearchCriteria { diff --git a/worker/lib/search.go b/worker/lib/search.go index b98e2bbb..9715c4a1 100644 --- a/worker/lib/search.go +++ b/worker/lib/search.go @@ -10,7 +10,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/rfc822" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) func Search(messages []rfc822.RawMessage, criteria *types.SearchCriteria) ([]models.UID, error) { diff --git a/worker/notmuch/search.go b/worker/notmuch/search.go index a84711eb..ddbb13cd 100644 --- a/worker/notmuch/search.go +++ b/worker/notmuch/search.go @@ -9,7 +9,7 @@ import ( "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" - "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rjarry/go-opt/v2" ) type queryBuilder struct { |