Skip to content

Commit 45d7d25

Browse files
authored
Use continuation instead of message to signal subcomponent results (#604)
Previously, `av sync` was using message passing to signal subcomponent results. By sending out a special message to the bubbletea message bus, the parent component can tell if a subcomponent finishes its process. For example, sequencerui had three messages, Conflict, Abort, and Done, to indicate its processing result. While this works, it becomes complicated to handle those messages. As we add more subcomponents, we need to handle the messages in a central message handling function. Instead of using message passing for signaling the results, use continuation passing style to handle the results. This style is already employed in the official API such as ExecProcess (https://pkg.go.dev/github.com/charmbracelet/bubbletea#ExecProcess). The benefit of the continuation passing style in `av` is that we can write the processing flow from top to bottom. The passed continuation indicates what will be done after one step. This reduces the number of messages that we need to handle in the message handler. Also, because `av` is doing processing step-by-step, it turned out that we just can forward most of the messages to the last subcomponent. This greatly simplifies the message handling code as well as the rendering code. Overall, this reduces the complexity of the implementation structure.
1 parent 57e8ea4 commit 45d7d25

File tree

8 files changed

+290
-330
lines changed

8 files changed

+290
-330
lines changed

cmd/av/commit_common.go

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,40 @@ type postCommitRestackViewModel struct {
2727
repo *git.Repo
2828
db meta.DB
2929

30-
restackModel *sequencerui.RestackModel
30+
state *sequencerui.RestackState
31+
restackModel tea.Model
3132

3233
quitWithConflict bool
3334
err error
3435
}
3536

3637
func (vm *postCommitRestackViewModel) Init() tea.Cmd {
37-
state, err := vm.createState()
38+
var err error
39+
vm.state, err = vm.createState()
3840
if err != nil {
3941
return uiutils.ErrCmd(err)
4042
}
41-
vm.restackModel = sequencerui.NewRestackModel(vm.repo, vm.db)
42-
vm.restackModel.State = state
43+
vm.restackModel = sequencerui.NewRestackModel(vm.repo, vm.db, vm.state, sequencerui.RestackStateOptions{
44+
OnConflict: func() tea.Cmd {
45+
if err := vm.writeState(vm.state); err != nil {
46+
return uiutils.ErrCmd(err)
47+
}
48+
vm.quitWithConflict = true
49+
return tea.Quit
50+
},
51+
OnAbort: func() tea.Cmd {
52+
if err := vm.writeState(nil); err != nil {
53+
return uiutils.ErrCmd(err)
54+
}
55+
return tea.Quit
56+
},
57+
OnDone: func() tea.Cmd {
58+
if err := vm.writeState(nil); err != nil {
59+
return uiutils.ErrCmd(err)
60+
}
61+
return tea.Quit
62+
},
63+
})
4364
return vm.restackModel.Init()
4465
}
4566

@@ -49,17 +70,6 @@ func (vm *postCommitRestackViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
4970
var cmd tea.Cmd
5071
vm.restackModel, cmd = vm.restackModel.Update(msg)
5172
return vm, cmd
52-
case *sequencerui.RestackConflict:
53-
if err := vm.writeState(vm.restackModel.State); err != nil {
54-
return vm, uiutils.ErrCmd(err)
55-
}
56-
vm.quitWithConflict = true
57-
return vm, tea.Quit
58-
case *sequencerui.RestackAbort, *sequencerui.RestackDone:
59-
if err := vm.writeState(nil); err != nil {
60-
return vm, uiutils.ErrCmd(err)
61-
}
62-
return vm, tea.Quit
6373
case tea.KeyMsg:
6474
switch msg.String() {
6575
case "ctrl+c":

cmd/av/reparent.go

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,40 @@ type reparentViewModel struct {
5959
repo *git.Repo
6060
db meta.DB
6161

62-
restackModel *sequencerui.RestackModel
62+
state *sequencerui.RestackState
63+
restackModel tea.Model
6364

6465
quitWithConflict bool
6566
err error
6667
}
6768

6869
func (vm *reparentViewModel) Init() tea.Cmd {
69-
state, err := vm.createState()
70+
var err error
71+
vm.state, err = vm.createState()
7072
if err != nil {
7173
return uiutils.ErrCmd(err)
7274
}
73-
vm.restackModel = sequencerui.NewRestackModel(vm.repo, vm.db)
74-
vm.restackModel.State = state
75+
vm.restackModel = sequencerui.NewRestackModel(vm.repo, vm.db, vm.state, sequencerui.RestackStateOptions{
76+
OnConflict: func() tea.Cmd {
77+
if err := vm.writeState(vm.state); err != nil {
78+
return uiutils.ErrCmd(err)
79+
}
80+
vm.quitWithConflict = true
81+
return tea.Quit
82+
},
83+
OnAbort: func() tea.Cmd {
84+
if err := vm.writeState(nil); err != nil {
85+
return uiutils.ErrCmd(err)
86+
}
87+
return tea.Quit
88+
},
89+
OnDone: func() tea.Cmd {
90+
if err := vm.writeState(nil); err != nil {
91+
return uiutils.ErrCmd(err)
92+
}
93+
return tea.Quit
94+
},
95+
})
7596
return vm.restackModel.Init()
7697
}
7798

@@ -81,17 +102,6 @@ func (vm *reparentViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
81102
var cmd tea.Cmd
82103
vm.restackModel, cmd = vm.restackModel.Update(msg)
83104
return vm, cmd
84-
case *sequencerui.RestackConflict:
85-
if err := vm.writeState(vm.restackModel.State); err != nil {
86-
return vm, uiutils.ErrCmd(err)
87-
}
88-
vm.quitWithConflict = true
89-
return vm, tea.Quit
90-
case *sequencerui.RestackAbort, *sequencerui.RestackDone:
91-
if err := vm.writeState(nil); err != nil {
92-
return vm, uiutils.ErrCmd(err)
93-
}
94-
return vm, tea.Quit
95105
case tea.KeyMsg:
96106
switch msg.String() {
97107
case "ctrl+c":

cmd/av/restack.go

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,35 +50,56 @@ type restackViewModel struct {
5050
repo *git.Repo
5151
db meta.DB
5252

53-
restackModel *sequencerui.RestackModel
53+
state *sequencerui.RestackState
54+
restackModel tea.Model
5455

5556
quitWithConflict bool
5657
err error
5758
}
5859

5960
func (vm *restackViewModel) Init() tea.Cmd {
60-
state, err := vm.readState()
61+
var err error
62+
vm.state, err = vm.readState()
6163
if err != nil {
6264
return uiutils.ErrCmd(err)
6365
}
64-
if state == nil {
66+
if vm.state == nil {
6567
if restackFlags.Abort || restackFlags.Continue || restackFlags.Skip {
6668
return uiutils.ErrCmd(errors.New("no restack in progress"))
6769
}
68-
state, err = vm.createState()
70+
vm.state, err = vm.createState()
6971
if err != nil {
7072
return uiutils.ErrCmd(err)
7173
}
7274
}
73-
if state == nil {
75+
if vm.state == nil {
7476
return uiutils.ErrCmd(nothingToRestackError)
7577
}
76-
vm.restackModel = sequencerui.NewRestackModel(vm.repo, vm.db)
77-
vm.restackModel.State = state
78-
vm.restackModel.Abort = restackFlags.Abort
79-
vm.restackModel.Continue = restackFlags.Continue
80-
vm.restackModel.Skip = restackFlags.Skip
81-
vm.restackModel.DryRun = restackFlags.DryRun
78+
vm.restackModel = sequencerui.NewRestackModel(vm.repo, vm.db, vm.state, sequencerui.RestackStateOptions{
79+
Abort: restackFlags.Abort,
80+
Continue: restackFlags.Continue,
81+
Skip: restackFlags.Skip,
82+
DryRun: restackFlags.DryRun,
83+
OnConflict: func() tea.Cmd {
84+
if err := vm.writeState(vm.state); err != nil {
85+
return uiutils.ErrCmd(err)
86+
}
87+
vm.quitWithConflict = true
88+
return tea.Quit
89+
},
90+
OnAbort: func() tea.Cmd {
91+
if err := vm.writeState(nil); err != nil {
92+
return uiutils.ErrCmd(err)
93+
}
94+
return tea.Quit
95+
},
96+
OnDone: func() tea.Cmd {
97+
if err := vm.writeState(nil); err != nil {
98+
return uiutils.ErrCmd(err)
99+
}
100+
return tea.Quit
101+
},
102+
})
82103
return vm.restackModel.Init()
83104
}
84105

@@ -88,17 +109,6 @@ func (vm *restackViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
88109
var cmd tea.Cmd
89110
vm.restackModel, cmd = vm.restackModel.Update(msg)
90111
return vm, cmd
91-
case *sequencerui.RestackConflict:
92-
if err := vm.writeState(vm.restackModel.State); err != nil {
93-
return vm, uiutils.ErrCmd(err)
94-
}
95-
vm.quitWithConflict = true
96-
return vm, tea.Quit
97-
case *sequencerui.RestackAbort, *sequencerui.RestackDone:
98-
if err := vm.writeState(nil); err != nil {
99-
return vm, uiutils.ErrCmd(err)
100-
}
101-
return vm, tea.Quit
102112
case tea.KeyMsg:
103113
switch msg.String() {
104114
case "ctrl+c":

0 commit comments

Comments
 (0)