about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--commands/prompt.go33
-rw-r--r--doc/aerc.1.scd6
-rw-r--r--widgets/aerc.go34
-rw-r--r--widgets/exline.go41
4 files changed, 108 insertions, 6 deletions
diff --git a/commands/prompt.go b/commands/prompt.go
new file mode 100644
index 0000000..3734881
--- /dev/null
+++ b/commands/prompt.go
@@ -0,0 +1,33 @@
+package commands
+
+import (
+	"errors"
+	"fmt"
+
+	"git.sr.ht/~sircmpwn/aerc/widgets"
+)
+
+type Prompt struct{}
+
+func init() {
+	register(Prompt{})
+}
+
+func (_ Prompt) Aliases() []string {
+	return []string{"prompt"}
+}
+
+func (_ Prompt) Complete(aerc *widgets.Aerc, args []string) []string {
+	return nil // TODO: add completions
+}
+
+func (_ Prompt) Execute(aerc *widgets.Aerc, args []string) error {
+	if len(args) < 3 {
+		return errors.New(fmt.Sprintf("Usage: %s <prompt> <cmd>", args[0]))
+	}
+
+	prompt := args[1]
+	cmd := args[2:]
+	aerc.RegisterPrompt(prompt, cmd)
+	return nil
+}
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
index 4aa777c..225ded7 100644
--- a/doc/aerc.1.scd
+++ b/doc/aerc.1.scd
@@ -67,6 +67,12 @@ These commands work in any context.
 	Cycles to the previous or next tab in the list, repeating n times
 	(default: 1).
 
+*prompt* <prompt> <command...>
+	Displays the prompt on the status bar, waits for user input, then appends
+	that input as the last argument to the command and executes it. The input is
+	passed as one argument to the command, unless it is empty, in which case no
+	extra argument is added.
+
 *quit*
 	Exits aerc.
 
diff --git a/widgets/aerc.go b/widgets/aerc.go
index 5a7914a..345f3ea 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -30,6 +30,7 @@ type Aerc struct {
 	statusbar   *libui.Stack
 	statusline  *StatusLine
 	pendingKeys []config.KeyStroke
+	prompts     *libui.Stack
 	tabs        *libui.Tabs
 	beep        func() error
 }
@@ -65,6 +66,7 @@ func NewAerc(conf *config.AercConfig, logger *log.Logger,
 		logger:     logger,
 		statusbar:  statusbar,
 		statusline: statusline,
+		prompts:    libui.NewStack(),
 		tabs:       tabs,
 	}
 
@@ -105,6 +107,20 @@ func (aerc *Aerc) Tick() bool {
 	for _, acct := range aerc.accounts {
 		more = acct.Tick() || more
 	}
+
+	if len(aerc.prompts.Children()) > 0 {
+		more = true
+		previous := aerc.focused
+		prompt := aerc.prompts.Pop().(*ExLine)
+		prompt.finish = func() {
+			aerc.statusbar.Pop()
+			aerc.focus(previous)
+		}
+
+		aerc.statusbar.Push(prompt)
+		aerc.focus(prompt)
+	}
+
 	return more
 }
 
@@ -358,8 +374,6 @@ func (aerc *Aerc) BeginExCommand() {
 		if aerc.simulating == 0 {
 			aerc.cmdHistory.Add(cmd)
 		}
-		aerc.statusbar.Pop()
-		aerc.focus(previous)
 	}, func() {
 		aerc.statusbar.Pop()
 		aerc.focus(previous)
@@ -370,6 +384,22 @@ func (aerc *Aerc) BeginExCommand() {
 	aerc.focus(exline)
 }
 
+func (aerc *Aerc) RegisterPrompt(prompt string, cmd []string) {
+	p := NewPrompt(prompt, func(text string) {
+		if text != "" {
+			cmd = append(cmd, text)
+		}
+		err := aerc.cmd(cmd)
+		if err != nil {
+			aerc.PushStatus(" "+err.Error(), 10*time.Second).
+				Color(tcell.ColorDefault, tcell.ColorRed)
+		}
+	}, func(cmd string) []string {
+		return nil // TODO: completions
+	})
+	aerc.prompts.Push(p)
+}
+
 func (aerc *Aerc) Mailto(addr *url.URL) error {
 	acct := aerc.SelectedAccount()
 	if acct == nil {
diff --git a/widgets/exline.go b/widgets/exline.go
index be1cde1..8ec69d6 100644
--- a/widgets/exline.go
+++ b/widgets/exline.go
@@ -9,21 +9,21 @@ import (
 
 type ExLine struct {
 	ui.Invalidatable
-	cancel      func()
 	commit      func(cmd string)
+	finish      func()
 	tabcomplete func(cmd string) []string
 	cmdHistory  lib.History
 	input       *ui.TextInput
 }
 
-func NewExLine(commit func(cmd string), cancel func(),
+func NewExLine(commit func(cmd string), finish func(),
 	tabcomplete func(cmd string) []string,
 	cmdHistory lib.History) *ExLine {
 
 	input := ui.NewTextInput("").Prompt(":").TabComplete(tabcomplete)
 	exline := &ExLine{
-		cancel:      cancel,
 		commit:      commit,
+		finish:      finish,
 		tabcomplete: tabcomplete,
 		cmdHistory:  cmdHistory,
 		input:       input,
@@ -34,6 +34,22 @@ func NewExLine(commit func(cmd string), cancel func(),
 	return exline
 }
 
+func NewPrompt(prompt string, commit func(text string),
+	tabcomplete func(cmd string) []string) *ExLine {
+
+	input := ui.NewTextInput("").Prompt(prompt).TabComplete(tabcomplete)
+	exline := &ExLine{
+		commit:      commit,
+		tabcomplete: tabcomplete,
+		cmdHistory:  &nullHistory{input: input},
+		input:       input,
+	}
+	input.OnInvalidate(func(d ui.Drawable) {
+		exline.Invalidate()
+	})
+	return exline
+}
+
 func (ex *ExLine) Invalidate() {
 	ex.DoInvalidate(ex)
 }
@@ -54,6 +70,7 @@ func (ex *ExLine) Event(event tcell.Event) bool {
 			cmd := ex.input.String()
 			ex.input.Focus(false)
 			ex.commit(cmd)
+			ex.finish()
 		case tcell.KeyUp:
 			ex.input.Set(ex.cmdHistory.Prev())
 			ex.Invalidate()
@@ -63,10 +80,26 @@ func (ex *ExLine) Event(event tcell.Event) bool {
 		case tcell.KeyEsc, tcell.KeyCtrlC:
 			ex.input.Focus(false)
 			ex.cmdHistory.Reset()
-			ex.cancel()
+			ex.finish()
 		default:
 			return ex.input.Event(event)
 		}
 	}
 	return true
 }
+
+type nullHistory struct {
+	input *ui.TextInput
+}
+
+func (_ *nullHistory) Add(string) {}
+
+func (h *nullHistory) Next() string {
+	return h.input.String()
+}
+
+func (h *nullHistory) Prev() string {
+	return h.input.String()
+}
+
+func (_ *nullHistory) Reset() {}