aboutsummaryrefslogtreecommitdiffstats
path: root/commands/commands.go
diff options
context:
space:
mode:
Diffstat (limited to 'commands/commands.go')
-rw-r--r--commands/commands.go133
1 files changed, 104 insertions, 29 deletions
diff --git a/commands/commands.go b/commands/commands.go
index 0a7050e9..f50a1e8a 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -23,6 +23,16 @@ type Command interface {
Complete(*widgets.Aerc, []string) []string
}
+type OptionsProvider interface {
+ Command
+ Options() string
+}
+
+type OptionCompleter interface {
+ OptionsProvider
+ CompleteOption(*widgets.Aerc, rune, string) []string
+}
+
type Commands map[string]Command
func NewCommands() *Commands {
@@ -131,49 +141,98 @@ func (cmds *Commands) ExecuteCommand(
return NoSuchCommand(args[0])
}
-func (cmds *Commands) GetCompletions(aerc *widgets.Aerc, cmd string) []string {
- args, err := shlex.Split(cmd)
+// GetCompletions returns the completion options and the command prefix
+func (cmds *Commands) GetCompletions(
+ aerc *widgets.Aerc, cmd string,
+) (options []string, prefix string) {
+ log.Tracef("completing command: %s", cmd)
+
+ // start completion
+ args, err := splitCmd(cmd)
if err != nil {
- return nil
+ return
}
// nothing entered, list all commands
if len(args) == 0 {
- names := cmds.Names()
- sort.Strings(names)
- return names
+ options = cmds.Names()
+ sort.Strings(options)
+ return
+ }
+
+ // complete command name
+ spaceTerminated := cmd[len(cmd)-1] == ' '
+ if len(args) == 1 && !spaceTerminated {
+ for _, n := range cmds.Names() {
+ options = append(options, n+" ")
+ }
+ options = CompletionFromList(aerc, options, args)
+
+ return
+ }
+
+ // look for command in dictionary
+ c, ok := cmds.dict()[args[0]]
+ if !ok {
+ return
}
// complete options
- if len(args) > 1 || cmd[len(cmd)-1] == ' ' {
- if cmd, ok := cmds.dict()[args[0]]; ok {
- var completions []string
- if len(args) > 1 {
- completions = cmd.Complete(aerc, args[1:])
- } else {
- completions = cmd.Complete(aerc, []string{})
+ var spec string
+ if provider, ok := c.(OptionsProvider); ok {
+ spec = provider.Options()
+ }
+
+ parser, err := newParser(cmd, spec, spaceTerminated)
+ if err != nil {
+ log.Debugf("completion parser failed: %v", err)
+ return
+ }
+
+ switch parser.kind {
+ case SHORT_OPTION:
+ for _, r := range strings.ReplaceAll(spec, ":", "") {
+ if strings.ContainsRune(parser.flag, r) {
+ continue
}
- if completions != nil && len(completions) == 0 {
- return nil
+ option := string(r)
+ if strings.Contains(spec, option+":") {
+ option += " "
}
-
- options := make([]string, 0)
- for _, option := range completions {
- options = append(options, args[0]+" "+option)
+ options = append(options, option)
+ }
+ prefix = cmd
+ case OPTION_ARGUMENT:
+ cmpl, ok := c.(OptionCompleter)
+ if !ok {
+ return
+ }
+ stem := cmd
+ if parser.arg != "" {
+ stem = strings.TrimSuffix(cmd, parser.arg)
+ }
+ pad := ""
+ if !strings.HasSuffix(stem, " ") {
+ pad += " "
+ }
+ s := parser.flag
+ r := rune(s[len(s)-1])
+ for _, option := range cmpl.CompleteOption(aerc, r, parser.arg) {
+ options = append(options, pad+escape(option)+" ")
+ }
+ prefix = stem
+ case OPERAND:
+ stem := strings.Join(args[:parser.optind], " ")
+ for _, option := range c.Complete(aerc, args[1:]) {
+ if strings.Contains(option, " ") {
+ option = escape(option)
}
- return options
+ options = append(options, " "+option)
}
- return nil
+ prefix = stem
}
- // complete available commands
- names := cmds.Names()
- options := FilterList(names, args[0], "", aerc.SelectedAccountUiConfig().FuzzyComplete)
-
- if len(options) > 0 {
- return options
- }
- return nil
+ return
}
func GetFolders(aerc *widgets.Aerc, args []string) []string {
@@ -246,3 +305,19 @@ func hasUpper(s string) bool {
}
return false
}
+
+// splitCmd splits the command into arguments
+func splitCmd(cmd string) ([]string, error) {
+ args, err := shlex.Split(cmd)
+ if err != nil {
+ return nil, err
+ }
+ return args, nil
+}
+
+func escape(s string) string {
+ if strings.Contains(s, " ") {
+ return strings.ReplaceAll(s, " ", "\\ ")
+ }
+ return s
+}