diff --git a/tea.go b/tea.go index c397c8d..b195c35 100644 --- a/tea.go +++ b/tea.go @@ -9,7 +9,7 @@ import ( "github.com/muesli/termenv" ) -// Msg represents an action. It's used by Update to update the UI. +// Msg represents an action. It's used signal Update to update the UI. type Msg interface{} // Model contains the updatable data for an application @@ -18,6 +18,14 @@ type Model interface{} // Cmd is an IO operation. If it's nil it's considered a no-op. type Cmd func(Model) Msg +// Batch peforms a bunch of commands concurrently with no ordering guarantees +// about the results. +func Batch(cmds ...Cmd) Cmd { + return func(_ Model) Msg { + return batchMsg(cmds) + } +} + // Sub is an event subscription. If it returns nil it's considered a no-op, // but there's really no reason to have a nil subscription. type Sub func(Model) Msg @@ -78,6 +86,7 @@ type ErrMsg struct { error } +// String implements String() on the error interface for ErrMsg func (e ErrMsg) String() string { return e.Error() } @@ -104,6 +113,9 @@ func Quit(_ Model) Msg { // Signals that the program should quit type quitMsg struct{} +// batchMsg is used to perform a bunch of commands +type batchMsg []Cmd + // NewProgram creates a new Program func NewProgram(init Init, update Update, view View, subs Subscriptions) *Program { return &Program{ @@ -175,11 +187,20 @@ func (p *Program) Start() error { for { select { case msg := <-msgs: + + // Handle quit message if _, ok := msg.(quitMsg); ok { close(done) return nil } + // Process batch commands + if b, ok := msg.(batchMsg); ok { + for _, cmd := range b { + cmds <- cmd + } + } + p.model, cmd = p.update(msg, p.model) // run update cmds <- cmd // process command (if any) subs = p.processSubs(msgs, subs) // check for new and outdated subscriptions