Skip to content

Commit 95586f9

Browse files
committed
fix: Attempt to handle blink messages
1 parent f4afcf8 commit 95586f9

File tree

10 files changed

+85
-133
lines changed

10 files changed

+85
-133
lines changed

src/internal/function.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
tea "github.com/charmbracelet/bubbletea"
1717

1818
"github.com/yorukot/superfile/src/internal/ui/processbar"
19-
zoxideui "github.com/yorukot/superfile/src/internal/ui/zoxide"
2019
"github.com/yorukot/superfile/src/internal/utils"
2120

2221
"github.com/yorukot/superfile/src/internal/common"
@@ -363,16 +362,6 @@ func processCmdToTeaCmd(cmd processbar.Cmd) tea.Cmd {
363362
}
364363
}
365364

366-
func zoxideCmdToTeaCmd(cmd zoxideui.Cmd) tea.Cmd {
367-
if cmd == nil {
368-
return nil
369-
}
370-
return func() tea.Msg {
371-
updateMsg := cmd()
372-
return NewZoxideUpdateMsg(updateMsg)
373-
}
374-
}
375-
376365
func getCopyOrCutOperationName(cut bool) string {
377366
if cut {
378367
return "cut"

src/internal/key_function.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,7 @@ func (m *model) mainKey(msg string) tea.Cmd { //nolint: gocyclo,cyclop,funlen //
105105
case slices.Contains(common.Hotkeys.OpenSPFPrompt, msg):
106106
m.promptModal.Open(false)
107107
case slices.Contains(common.Hotkeys.OpenZoxide, msg):
108-
zCmd := m.zoxideModal.Open()
109-
return zoxideCmdToTeaCmd(zCmd)
108+
return m.zoxideModal.Open()
110109

111110
case slices.Contains(common.Hotkeys.OpenHelpMenu, msg):
112111
m.openHelpMenu()

src/internal/model.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/yorukot/superfile/src/internal/common"
1515
"github.com/yorukot/superfile/src/internal/ui/metadata"
1616
"github.com/yorukot/superfile/src/internal/ui/notify"
17-
zoxideui "github.com/yorukot/superfile/src/internal/ui/zoxide"
1817
"github.com/yorukot/superfile/src/internal/utils"
1918

2019
"github.com/barasher/go-exiftool"
@@ -24,6 +23,7 @@ import (
2423
"github.com/charmbracelet/x/ansi"
2524

2625
variable "github.com/yorukot/superfile/src/config"
26+
zoxideui "github.com/yorukot/superfile/src/internal/ui/zoxide"
2727
stringfunction "github.com/yorukot/superfile/src/pkg/string_function"
2828
)
2929

@@ -86,6 +86,14 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
8686
m.handleMouseMsg(msg)
8787
case tea.KeyMsg:
8888
inputCmd = m.handleKeyInput(msg)
89+
90+
// Has to handle zoxide messages separately as they could be generated via
91+
// zoxide update commands, or batched commands from textinput
92+
// Cannot do it like processbar messages
93+
case zoxideui.UpdateMsg:
94+
slog.Debug("Got ModelUpdate message", "id", msg.GetReqID())
95+
gotModelUpdateMsg = true
96+
updateCmd = msg.Apply(&m.zoxideModal)
8997
case ModelUpdateMessage:
9098
// TODO: Some of these updates messages should trigger filePanel state update
9199
// For example a success message for delete operation
@@ -412,6 +420,7 @@ func (m *model) handleKeyInput(msg tea.KeyMsg) tea.Cmd {
412420
func (m *model) updateFilePanelsState(msg tea.Msg) tea.Cmd {
413421
focusPanel := &m.fileModel.filePanels[m.filePanelFocusIndex]
414422
var cmd tea.Cmd
423+
var action common.ModelAction
415424
switch {
416425
case m.firstTextInput:
417426
m.firstTextInput = false
@@ -422,20 +431,13 @@ func (m *model) updateFilePanelsState(msg tea.Msg) tea.Cmd {
422431
case m.typingModal.open:
423432
m.typingModal.textInput, cmd = m.typingModal.textInput.Update(msg)
424433
case m.promptModal.IsOpen():
425-
// *cmd is a non-name, and cannot be used on left of :=
426-
var action common.ModelAction
427-
// Taking returned cmd is necessary for blinking
428434
// TODO : Separate this to a utility
429435
cwdLocation := m.fileModel.filePanels[m.filePanelFocusIndex].location
430436
action, cmd = m.promptModal.HandleUpdate(msg, cwdLocation)
431437
m.applyPromptModalAction(action)
432438
case m.zoxideModal.IsOpen():
433-
var action common.ModelAction
434-
var zCmd zoxideui.Cmd
435-
action, zCmd = m.zoxideModal.HandleUpdate(msg)
439+
action, cmd = m.zoxideModal.HandleUpdate(msg)
436440
m.applyZoxideModalAction(action)
437-
// Wrap zoxide cmd to convert to tea.Cmd
438-
return zoxideCmdToTeaCmd(zCmd)
439441
}
440442

441443
// TODO : This is like duct taping a bigger problem

src/internal/model_msg.go

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/yorukot/superfile/src/internal/ui/metadata"
99
"github.com/yorukot/superfile/src/internal/ui/notify"
1010
"github.com/yorukot/superfile/src/internal/ui/processbar"
11-
zoxideui "github.com/yorukot/superfile/src/internal/ui/zoxide"
1211
)
1312

1413
type ModelUpdateMessage interface {
@@ -169,26 +168,6 @@ func (msg NotifyModalUpdateMsg) ApplyToModel(m *model) tea.Cmd {
169168
return nil
170169
}
171170

172-
type ZoxideUpdateMsg struct {
173-
BaseMessage
174-
175-
zMsg zoxideui.UpdateMsg
176-
}
177-
178-
func NewZoxideUpdateMsg(zMsg zoxideui.UpdateMsg) ZoxideUpdateMsg {
179-
return ZoxideUpdateMsg{
180-
zMsg: zMsg,
181-
BaseMessage: BaseMessage{
182-
reqID: zMsg.GetReqID(),
183-
},
184-
}
185-
}
186-
187-
func (msg ZoxideUpdateMsg) ApplyToModel(m *model) tea.Cmd {
188-
cmd := msg.zMsg.Apply(&m.zoxideModal)
189-
return zoxideCmdToTeaCmd(cmd)
190-
}
191-
192171
type FilePreviewUpdateMsg struct {
193172
BaseMessage
194173

src/internal/ui/zoxide/model.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package zoxide
22

33
import (
44
"log/slog"
5+
"reflect"
56
"slices"
67
"strings"
78

@@ -30,13 +31,14 @@ func GenerateModel(zClient *zoxidelib.Client, maxHeight int, width int) Model {
3031
return m
3132
}
3233

33-
func (m *Model) HandleUpdate(msg tea.Msg) (common.ModelAction, Cmd) {
34+
func (m *Model) HandleUpdate(msg tea.Msg) (common.ModelAction, tea.Cmd) {
3435
slog.Debug("zoxide.Model HandleUpdate()", "msg", msg,
36+
"msgType", reflect.TypeOf(msg),
3537
"textInput", m.textInput.Value(),
3638
"cursorBlink", m.textInput.Cursor.Blink)
3739
var action common.ModelAction
3840
action = common.NoAction{}
39-
var cmd Cmd
41+
var cmd tea.Cmd
4042
if !m.IsOpen() {
4143
slog.Error("HandleUpdate called on closed zoxide")
4244
return action, cmd
@@ -78,7 +80,7 @@ func (m *Model) HandleUpdate(msg tea.Msg) (common.ModelAction, Cmd) {
7880
// Non keypress updates like Cursor Blink
7981
// Only update text input if zoxide is available
8082
if m.zClient != nil {
81-
m.textInput, _ = m.textInput.Update(msg)
83+
m.textInput, cmd = m.textInput.Update(msg)
8284
}
8385
}
8486
return action, cmd
@@ -97,14 +99,14 @@ func (m *Model) handleConfirm() common.ModelAction {
9799
return common.NoAction{}
98100
}
99101

100-
func (m *Model) handleNormalKeyInput(msg tea.KeyMsg) Cmd {
102+
func (m *Model) handleNormalKeyInput(msg tea.KeyMsg) tea.Cmd {
101103
m.textInput, _ = m.textInput.Update(msg)
102104

103105
// Return async query command
104106
return m.GetQueryCmd(m.textInput.Value())
105107
}
106108

107-
func (m *Model) GetQueryCmd(query string) Cmd {
109+
func (m *Model) GetQueryCmd(query string) tea.Cmd {
108110
if m.zClient == nil || !common.Config.ZoxideSupport {
109111
return nil
110112
}
@@ -114,7 +116,7 @@ func (m *Model) GetQueryCmd(query string) Cmd {
114116

115117
slog.Debug("Submitting zoxide query request", "query", query, "id", reqID)
116118

117-
return func() UpdateMsg {
119+
return func() tea.Msg {
118120
queryFields := strings.Fields(query)
119121
results, err := m.zClient.QueryAll(queryFields...)
120122
if err != nil {
@@ -124,3 +126,22 @@ func (m *Model) GetQueryCmd(query string) Cmd {
124126
return NewUpdateMsg(query, results, reqID)
125127
}
126128
}
129+
130+
// Apply updates the zoxide modal with query results
131+
func (msg UpdateMsg) Apply(m *Model) tea.Cmd {
132+
// Ignore stale results - only apply if query matches current input
133+
currentQuery := m.textInput.Value()
134+
if msg.query != currentQuery {
135+
slog.Debug("Ignoring stale zoxide query result",
136+
"msgQuery", msg.query,
137+
"currentQuery", currentQuery,
138+
"id", msg.reqID)
139+
return nil
140+
}
141+
142+
m.results = msg.results
143+
m.cursor = 0
144+
m.renderIndex = 0
145+
146+
return nil
147+
}

src/internal/ui/zoxide/model_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,48 @@ func TestJKKeyHandling(t *testing.T) {
8080
assert.Equal(t, 1, m.cursor, "down arrow should navigate down")
8181
assert.Empty(t, m.textInput.Value(), "down arrow should not add to textInput")
8282
}
83+
84+
func TestApplyWithMatchingQuery(t *testing.T) {
85+
m := setupTestModel()
86+
m.textInput.SetValue("test")
87+
m.cursor = 5
88+
m.renderIndex = 2
89+
90+
results := []zoxidelib.Result{
91+
{Path: "/test/path1", Score: 100},
92+
{Path: "/test/path2", Score: 90},
93+
{Path: "/test/path3", Score: 80},
94+
}
95+
msg := NewUpdateMsg("test", results, 1)
96+
97+
cmd := msg.Apply(&m)
98+
assert.Nil(t, cmd)
99+
assert.Len(t, m.results, 3, "results should be updated to 3 items")
100+
assert.Equal(t, 0, m.cursor, "cursor should be reset to 0")
101+
assert.Equal(t, 0, m.renderIndex, "renderIndex should be reset to 0")
102+
assert.Equal(t, results, m.results, "results should match the update message")
103+
}
104+
105+
func TestApplyWithStaleQuery(t *testing.T) {
106+
m := setupTestModel()
107+
m.textInput.SetValue("new")
108+
m.cursor = 1
109+
m.renderIndex = 1
110+
originalResults := []zoxidelib.Result{
111+
{Path: "/original/path", Score: 50},
112+
}
113+
m.results = originalResults
114+
115+
staleResults := []zoxidelib.Result{
116+
{Path: "/test/path1", Score: 100},
117+
{Path: "/test/path2", Score: 90},
118+
{Path: "/test/path3", Score: 80},
119+
}
120+
msg := NewUpdateMsg("old", staleResults, 1)
121+
122+
cmd := msg.Apply(&m)
123+
assert.Nil(t, cmd)
124+
assert.Equal(t, originalResults, m.results, "results should remain unchanged")
125+
assert.Equal(t, 1, m.cursor, "cursor should remain unchanged")
126+
assert.Equal(t, 1, m.renderIndex, "renderIndex should remain unchanged")
127+
}

src/internal/ui/zoxide/type.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ type Model struct {
2929
reqCnt int
3030
}
3131

32-
// Cmd is a function that returns an UpdateMsg
33-
type Cmd func() UpdateMsg
34-
3532
// UpdateMsg represents an async query result
3633
type UpdateMsg struct {
3734
query string

src/internal/ui/zoxide/update.go

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/internal/ui/zoxide/update_test.go

Lines changed: 0 additions & 53 deletions
This file was deleted.

src/internal/ui/zoxide/utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
zoxidelib "github.com/lazysegtree/go-zoxide"
99
)
1010

11-
func (m *Model) Open() Cmd {
11+
func (m *Model) Open() tea.Cmd {
1212
m.open = true
1313
m.justOpened = true
1414
m.textInput.SetValue("")

0 commit comments

Comments
 (0)