aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2024-10-12 00:21:44 +0200
committerRobin Jarry <robin@jarry.cc>2024-10-23 10:22:51 +0200
commit3c9ad93801ce9bb71d76fa398d7d94f1afd2c2cb (patch)
treedcc715e090fedf65e2b0f800d4d969b4606f7a5c
parent63db443949b92eed5f5d2ded5f430bda96312b43 (diff)
downloadaerc-3c9ad93801ce9bb71d76fa398d7d94f1afd2c2cb.tar.gz
completion: display descriptions next to choices
Use go-opt v2 new completion API which returns items descriptions along with their text values. Display the descriptions after the items separated by two spaces. Wrap the descriptions in parentheses to better indicate that they are not part of the completion choices. Limit the description length to 80 characters to avoid display issues. Add a new style object completion_description in stylesets. By default, the object will be rendered with a dimmed terminal attribute. Update all stylesets and documentation accordingly. Implements: https://todo.sr.ht/~rjarry/aerc/271 Link: https://git.sr.ht/~rjarry/go-opt/commit/ebeb82538395a Changelog-added: Command completion now displays descriptions next to completion items. Changelog-added: New `completion_description` style object in style sets used for rendering completion item descriptions. Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Bojan Gabric <bojan@bojangabric.com> Tested-by: Jason Cox <me@jasoncarloscox.com> Acked-by: Tim Culverhouse <tim@timculverhouse.com>
-rw-r--r--app/aerc.go18
-rw-r--r--app/app.go3
-rw-r--r--app/exline.go9
-rw-r--r--app/msgviewer.go2
-rw-r--r--commands/account/cf.go2
-rw-r--r--commands/account/mkdir.go2
-rw-r--r--commands/account/rmdir.go2
-rw-r--r--commands/commands.go4
-rw-r--r--commands/completion_helpers.go2
-rw-r--r--commands/compose/send.go10
-rw-r--r--commands/menu.go2
-rw-r--r--commands/patch/find.go2
-rw-r--r--commands/patch/list.go2
-rw-r--r--commands/patch/patch.go8
-rw-r--r--commands/prompt.go2
-rw-r--r--commands/util.go2
-rw-r--r--completer/completer.go18
-rw-r--r--config/style.go12
-rw-r--r--doc/aerc-stylesets.7.scd6
-rw-r--r--go.mod2
-rw-r--r--go.sum7
-rw-r--r--lib/open.go2
-rw-r--r--lib/send/sendmail.go2
-rw-r--r--lib/ui/textinput.go69
-rw-r--r--main.go24
-rw-r--r--stylesets/blue3
-rw-r--r--stylesets/default4
-rw-r--r--stylesets/monochrome1
-rw-r--r--stylesets/nord1
-rw-r--r--stylesets/pink3
-rw-r--r--stylesets/solarized1
-rw-r--r--worker/imap/search.go2
-rw-r--r--worker/lib/search.go2
-rw-r--r--worker/notmuch/search.go2
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)
diff --git a/app/app.go b/app/app.go
index 37444c8b..071aac74 100644
--- a/app/app.go
+++ b/app/app.go
@@ -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
diff --git a/go.mod b/go.mod
index d34a80ca..9f82c8e3 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index 607f91b7..6b6ca806 100644
--- a/go.sum
+++ b/go.sum
@@ -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
}
diff --git a/main.go b/main.go
index 66c2ed1a..0d2bb0e4 100644
--- a/main.go
+++ b/main.go
@@ -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 {