package tea import ( "time" ) // Batch performs a bunch of commands concurrently with no ordering guarantees // about the results. Use a Batch to return several commands. // // Example: // // func (m model) Init() (Model, Cmd) { // return m, tea.Batch(someCommand, someOtherCommand) // } func Batch(cmds ...Cmd) Cmd { return compactCmds[BatchMsg](cmds) } // BatchMsg is a message used to perform a bunch of commands concurrently with // no ordering guarantees. You can send a BatchMsg with Batch. type BatchMsg []Cmd // Sequence runs the given commands one at a time, in order. Contrast this with // Batch, which runs commands concurrently. func Sequence(cmds ...Cmd) Cmd { return compactCmds[sequenceMsg](cmds) } // sequenceMsg is used internally to run the given commands in order. type sequenceMsg []Cmd // compactCmds ignores any nil commands in cmds, and returns the most direct // command possible. That is, considering the non-nil commands, if there are // none it returns nil, if there is exactly one it returns that command // directly, else it returns the non-nil commands as type T. func compactCmds[T ~[]Cmd](cmds []Cmd) Cmd { var validCmds []Cmd for _, c := range cmds { if c == nil { continue } validCmds = append(validCmds, c) } switch len(validCmds) { case 0: return nil case 1: return validCmds[0] default: return func() Msg { return T(validCmds) } } } // Every is a command that ticks in sync with the system clock. So, if you // wanted to tick with the system clock every second, minute or hour you // could use this. It's also handy for having different things tick in sync. // // Because we're ticking with the system clock the tick will likely not run for // the entire specified duration. For example, if we're ticking for one minute // and the clock is at 12:34:20 then the next tick will happen at 12:35:00, 40 // seconds later. // // To produce the command, pass a duration and a function which returns // a message containing the time at which the tick occurred. // // type TickMsg time.Time // // cmd := Every(time.Second, func(t time.Time) Msg { // return TickMsg(t) // }) // // Beginners' note: Every sends a single message and won't automatically // dispatch messages at an interval. To do that, you'll want to return another // Every command after receiving your tick message. For example: // // type TickMsg time.Time // // // Send a message every second. // func tickEvery() Cmd { // return Every(time.Second, func(t time.Time) Msg { // return TickMsg(t) // }) // } // // func (m model) Init() (Model, Cmd) { // // Start ticking. // return m, tickEvery() // } // // func (m model) Update(msg Msg) (Model, Cmd) { // switch msg.(type) { // case TickMsg: // // Return your Every command again to loop. // return m, tickEvery() // } // return m, nil // } // // Every is analogous to Tick in the Elm Architecture. func Every(duration time.Duration, fn func(time.Time) Msg) Cmd { n := time.Now() d := n.Truncate(duration).Add(duration).Sub(n) t := time.NewTimer(d) return func() Msg { ts := <-t.C t.Stop() for len(t.C) > 0 { <-t.C } return fn(ts) } } // Tick produces a command at an interval independent of the system clock at // the given duration. That is, the timer begins precisely when invoked, // and runs for its entire duration. // // To produce the command, pass a duration and a function which returns // a message containing the time at which the tick occurred. // // type TickMsg time.Time // // cmd := Tick(time.Second, func(t time.Time) Msg { // return TickMsg(t) // }) // // Beginners' note: Tick sends a single message and won't automatically // dispatch messages at an interval. To do that, you'll want to return another // Tick command after receiving your tick message. For example: // // type TickMsg time.Time // // func doTick() Cmd { // return Tick(time.Second, func(t time.Time) Msg { // return TickMsg(t) // }) // } // // func (m model) Init() (Model, Cmd) { // // Start ticking. // return m, doTick() // } // // func (m model) Update(msg Msg) (Model, Cmd) { // switch msg.(type) { // case TickMsg: // // Return your Tick command again to loop. // return m, doTick() // } // return m, nil // } func Tick(d time.Duration, fn func(time.Time) Msg) Cmd { t := time.NewTimer(d) return func() Msg { ts := <-t.C t.Stop() for len(t.C) > 0 { <-t.C } return fn(ts) } } type windowSizeMsg struct{} // RequestWindowSize is a command that queries the terminal for its current // size. It delivers the results to Update via a [WindowSizeMsg]. Keep in mind // that WindowSizeMsgs will automatically be delivered to Update when the // [Program] starts and when the window dimensions change so in many cases you // will not need to explicitly invoke this command. func RequestWindowSize() Msg { return windowSizeMsg{} }