about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorChristopher Vittal <christopher.vittal@gmail.com>2019-08-19 21:56:12 -0400
committerDrew DeVault <sir@cmpwn.com>2019-08-26 09:48:39 +0900
commitecd803aae4ea1ba23d291325d52a53c19216e64b (patch)
treea560d5a88b332a39c0519a21ae05f5dc309545a3
parentea4fe713607e40cf19df37cdd0699cacba7313a3 (diff)
downloadaerc-ecd803aae4ea1ba23d291325d52a53c19216e64b.tar.gz
Add :prompt command
Usage:
    :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.
-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() {}