Skip to content

Commit ffa0502

Browse files
authored
Merge pull request #848 from wolfmagnate/fix-nested-cmd-order
2 parents 21eecd5 + 9e0e8f0 commit ffa0502

File tree

3 files changed

+110
-64
lines changed

3 files changed

+110
-64
lines changed

examples/sequence/main.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package main
55
import (
66
"fmt"
77
"os"
8+
"time"
89

910
tea "github.com/charmbracelet/bubbletea"
1011
)
@@ -14,15 +15,39 @@ type model struct{}
1415
func (m model) Init() tea.Cmd {
1516
return tea.Sequence(
1617
tea.Batch(
17-
tea.Println("A"),
18-
tea.Println("B"),
19-
tea.Println("C"),
18+
tea.Sequence(
19+
SleepPrintln("1-1-1", 1000),
20+
SleepPrintln("1-1-2", 1000),
21+
),
22+
tea.Batch(
23+
SleepPrintln("1-2-1", 1500),
24+
SleepPrintln("1-2-2", 1250),
25+
),
26+
),
27+
tea.Println("2"),
28+
tea.Sequence(
29+
tea.Batch(
30+
SleepPrintln("3-1-1", 500),
31+
SleepPrintln("3-1-2", 1000),
32+
),
33+
tea.Sequence(
34+
SleepPrintln("3-2-1", 750),
35+
SleepPrintln("3-2-2", 500),
36+
),
2037
),
21-
tea.Println("Z"),
2238
tea.Quit,
2339
)
2440
}
2541

42+
// print string after stopping for a certain period of time
43+
func SleepPrintln(s string, milisecond int) tea.Cmd {
44+
printCmd := tea.Println(s)
45+
return func() tea.Msg {
46+
time.Sleep(time.Duration(milisecond) * time.Millisecond)
47+
return printCmd()
48+
}
49+
}
50+
2651
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
2752
switch msg.(type) {
2853
case tea.KeyMsg:

tea.go

Lines changed: 71 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -471,60 +471,12 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
471471
p.exec(msg.cmd, msg.fn)
472472

473473
case BatchMsg:
474-
for _, cmd := range msg {
475-
if cmd == nil {
476-
continue
477-
}
478-
select {
479-
case <-p.ctx.Done():
480-
return model, nil
481-
case cmds <- cmd:
482-
}
483-
}
474+
go p.execBatchMsg(msg)
484475
continue
485476

486477
case sequenceMsg:
487-
go func() {
488-
// Execute commands one at a time, in order.
489-
for _, cmd := range msg {
490-
if cmd == nil {
491-
continue
492-
}
493-
494-
msg := cmd()
495-
switch msg := msg.(type) {
496-
case BatchMsg:
497-
var wg sync.WaitGroup
498-
for _, cmd := range msg {
499-
if cmd == nil {
500-
continue
501-
}
502-
wg.Add(1)
503-
cmd := cmd
504-
go func() {
505-
defer wg.Done()
506-
p.Send(cmd())
507-
}()
508-
}
509-
wg.Wait()
510-
case sequenceMsg:
511-
for _, cmd := range msg {
512-
if cmd == nil {
513-
continue
514-
}
515-
p.Send(cmd())
516-
}
517-
default:
518-
p.Send(msg)
519-
}
520-
}
521-
}()
522-
523-
case setWindowTitleMsg:
524-
p.SetWindowTitle(string(msg))
525-
526-
case windowSizeMsg:
527-
go p.checkResize()
478+
go p.execSequenceMsg(msg)
479+
continue
528480
}
529481

530482
// Process internal messages for the renderer.
@@ -546,6 +498,74 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
546498
}
547499
}
548500

501+
func (p *Program) execSequenceMsg(msg sequenceMsg) {
502+
if !p.startupOptions.has(withoutCatchPanics) {
503+
defer func() {
504+
if r := recover(); r != nil {
505+
p.recoverFromGoPanic(r)
506+
}
507+
}()
508+
}
509+
510+
// Execute commands one at a time, in order.
511+
for _, cmd := range msg {
512+
if cmd == nil {
513+
continue
514+
}
515+
msg := cmd()
516+
switch msg := msg.(type) {
517+
case BatchMsg:
518+
p.execBatchMsg(msg)
519+
case sequenceMsg:
520+
p.execSequenceMsg(msg)
521+
default:
522+
p.Send(msg)
523+
}
524+
}
525+
}
526+
527+
func (p *Program) execBatchMsg(msg BatchMsg) {
528+
if !p.startupOptions.has(withoutCatchPanics) {
529+
defer func() {
530+
if r := recover(); r != nil {
531+
p.recoverFromGoPanic(r)
532+
}
533+
}()
534+
}
535+
536+
// Execute commands one at a time.
537+
var wg sync.WaitGroup
538+
for _, cmd := range msg {
539+
if cmd == nil {
540+
continue
541+
}
542+
wg.Add(1)
543+
go func() {
544+
defer wg.Done()
545+
546+
if !p.startupOptions.has(withoutCatchPanics) {
547+
defer func() {
548+
if r := recover(); r != nil {
549+
p.recoverFromGoPanic(r)
550+
}
551+
}()
552+
}
553+
554+
msg := cmd()
555+
switch msg := msg.(type) {
556+
case BatchMsg:
557+
p.execBatchMsg(msg)
558+
case sequenceMsg:
559+
p.execSequenceMsg(msg)
560+
default:
561+
p.Send(msg)
562+
}
563+
}()
564+
}
565+
566+
wg.Wait() // wait for all commands from batch msg to finish
567+
}
568+
549569
// Run initializes the program and runs its event loops, blocking until it gets
550570
// terminated by either [Program.Quit], [Program.Kill], or its signal handler.
551571
// Returns the final model.

tea_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ type incrementMsg struct{}
1818

1919
type panicMsg struct{}
2020

21+
func panicCmd() Msg {
22+
panic("testing goroutine panic behavior")
23+
}
24+
2125
type testModel struct {
2226
executed atomic.Value
2327
counter atomic.Value
@@ -434,14 +438,14 @@ func TestTeaNestedSequenceMsg(t *testing.T) {
434438

435439
m := &testModel{}
436440
p := NewProgram(m, WithInput(&in), WithOutput(&buf))
437-
go p.Send(sequenceMsg{inc, Sequence(inc, inc), Quit})
441+
go p.Send(sequenceMsg{inc, Sequence(inc, inc, Batch(inc, inc)), Quit})
438442

439443
if _, err := p.Run(); err != nil {
440444
t.Fatal(err)
441445
}
442446

443-
if m.counter.Load() != 3 {
444-
t.Fatalf("counter should be 3, got %d", m.counter.Load())
447+
if m.counter.Load() != 5 {
448+
t.Fatalf("counter should be 5, got %d", m.counter.Load())
445449
}
446450
}
447451

@@ -502,19 +506,16 @@ func TestTeaGoroutinePanic(t *testing.T) {
502506
var buf bytes.Buffer
503507
var in bytes.Buffer
504508

505-
panicCmd := func() Msg {
506-
panic("testing goroutine panic behavior")
507-
}
508-
509509
m := &testModel{}
510510
p := NewProgram(m, WithInput(&in), WithOutput(&buf))
511511
go func() {
512512
for {
513513
time.Sleep(time.Millisecond)
514514
if m.executed.Load() != nil {
515515
batch := make(BatchMsg, 10)
516-
for i := range batch {
517-
batch[i] = panicCmd
516+
for i := 0; i < len(batch); i += 2 {
517+
batch[i] = Sequence(panicCmd)
518+
batch[i+1] = Batch(panicCmd)
518519
}
519520
p.Send(batch)
520521
return

0 commit comments

Comments
 (0)