Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 55 additions & 44 deletions internal/app/ui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ type State int
const (
StateInput State = iota
StateExecuting
StatePending
StatePendingClear
StatePendingQuit
)

// ReadyMsg signals the ui that execution is done and it should prompt.
Expand Down Expand Up @@ -63,7 +64,7 @@ type Model struct {
prevUserInput string
version string
highlighter func(string) string
escSeq int
pendingSeq int

keys KeyMap
styles Styles
Expand Down Expand Up @@ -180,54 +181,64 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m.handleInput()

case seqTimeoutMsg:
if msg.seq == m.escSeq {
if msg.seq == m.pendingSeq {
m.state = StateInput
}
return m, nil

case tea.KeyMsg:
if m.state == StatePending && !key.Matches(msg, m.keys.Clear) {
m.state = StateInput
}

if key.Matches(msg, m.keys.Quit) {
return m, func() tea.Msg {
return QuitRequestMsg{}
}
}
if key.Matches(msg, m.keys.Interrupt) {
if m.state == StateExecuting {
m.state = StateInput
cancelFn := m.cancel
return m, func() tea.Msg {
if err := cancelFn(context.Background()); err != nil {
return ExecCmdMsg{Cmd: PrintErrCmd(err, m.styles.ErrorOutput)}
}
return nil
}
}

m.input.Reset()
return m, nil
}
if key.Matches(msg, m.keys.Clear) {
if m.state == StateExecuting {
return m, nil
}
if m.state == StatePending {
m.state = StateInput
m.input.Reset()
return m, nil
}
m.state = StatePending
m.escSeq++
n := m.escSeq
return m, tea.Tick(500*time.Millisecond, func(t time.Time) tea.Msg {
return seqTimeoutMsg{seq: n}
})
}
if (m.state == StatePendingClear || m.state == StatePendingQuit) &&
!key.Matches(msg, m.keys.Clear) &&
!key.Matches(msg, m.keys.Interrupt) {
m.state = StateInput
}

if key.Matches(msg, m.keys.Quit) {
return m, func() tea.Msg {
return QuitRequestMsg{}
}
}
if key.Matches(msg, m.keys.Interrupt) {
if m.state == StateExecuting {
m.state = StateInput
cancelFn := m.cancel
return m, func() tea.Msg {
if err := cancelFn(context.Background()); err != nil {
return ExecCmdMsg{Cmd: PrintErrCmd(err, m.styles.ErrorOutput)}
}
return nil
}
}
if m.state == StatePendingQuit {
m.state = StateInput
return m, func() tea.Msg {
return QuitRequestMsg{}
}
}
m.state = StatePendingQuit
m.pendingSeq++
n := m.pendingSeq
return m, tea.Tick(500*time.Millisecond, func(t time.Time) tea.Msg {
return seqTimeoutMsg{seq: n}
})
}
if key.Matches(msg, m.keys.Clear) {
if m.state == StateExecuting {
return m, nil
}
if m.state == StatePendingClear {
m.state = StateInput
m.input.Reset()
return m, nil
}
m.state = StatePendingClear
m.pendingSeq++
n := m.pendingSeq
return m, tea.Tick(500*time.Millisecond, func(t time.Time) tea.Msg {
return seqTimeoutMsg{seq: n}
})
}
}

// Route to input only if in input state, avoiding capturing keystrokes while executing.
if m.state == StateInput {
var nextCmd tea.Cmd
Expand Down
Loading