~sircmpwn/aerc

ecd803aae4ea1ba23d291325d52a53c19216e64b — Christopher Vittal 1 year, 5 months ago ea4fe71
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.
4 files changed, 108 insertions(+), 6 deletions(-)

A commands/prompt.go
M doc/aerc.1.scd
M widgets/aerc.go
M widgets/exline.go
A commands/prompt.go => commands/prompt.go +33 -0
@@ 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
}

M doc/aerc.1.scd => doc/aerc.1.scd +6 -0
@@ 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.


M widgets/aerc.go => widgets/aerc.go +32 -2
@@ 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 {

M widgets/exline.go => widgets/exline.go +37 -4
@@ 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() {}