-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprompt.go
135 lines (110 loc) · 2.78 KB
/
prompt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package prompt
import (
"github.com/aschey/bubbleprompt/input"
"github.com/aschey/bubbleprompt/renderer"
"github.com/aschey/bubbleprompt/suggestion"
"github.com/aschey/bubbleprompt/suggestion/dropdown"
tea "github.com/charmbracelet/bubbletea"
)
// TODO: https://no-color.org/
type errMsg error
type modelState int
const (
completing modelState = iota
executing
)
type InputHandler[T any] interface {
Init() tea.Cmd
Update(msg tea.Msg) (InputHandler[T], tea.Cmd)
Execute(input string, prompt *Model[T]) (tea.Model, error)
Complete(prompt Model[T]) ([]suggestion.Suggestion[T], error)
}
type Model[T any] struct {
suggestionManager suggestion.Manager[T]
inputHandler InputHandler[T]
textInput input.Input[T]
renderer renderer.Renderer
executionManager *executionManager
modelState modelState
lastTypedCursorPosition int
typedRunes []rune
ready bool
size tea.WindowSizeMsg
sequenceNumber int
focus bool
err error
}
func New[T any](
inputHandler InputHandler[T],
textInput input.Input[T],
opts ...Option[T],
) Model[T] {
model := Model[T]{
suggestionManager: dropdown.New(textInput),
inputHandler: inputHandler,
textInput: textInput,
focus: true,
renderer: renderer.NewUnmanagedRenderer(),
}
for _, opt := range opts {
opt(&model)
}
return model
}
func (m *Model[T]) SuggestionManager() suggestion.Manager[T] {
return m.suggestionManager
}
func (m Model[T]) TextInput() input.Input[T] {
return m.textInput
}
func (m Model[T]) Renderer() renderer.Renderer {
return m.renderer
}
type rendererMsg struct {
renderer renderer.Renderer
retainHistory bool
}
func SetRenderer(r renderer.Renderer, retainHistory bool) tea.Cmd {
return func() tea.Msg {
return rendererMsg{
renderer: r,
retainHistory: retainHistory,
}
}
}
type focusMsg bool
func Focus() tea.Cmd {
return func() tea.Msg { return focusMsg(true) }
}
func Blur() tea.Cmd {
return func() tea.Msg { return focusMsg(false) }
}
var shutdown bool = false
type quitAttempted struct{}
func MsgFilter(_ tea.Model, msg tea.Msg) tea.Msg {
if _, ok := msg.(tea.QuitMsg); ok && !shutdown {
return quitAttempted{}
}
return msg
}
func (m Model[T]) getInitFocus() tea.Cmd {
if m.focus {
return Focus()
} else {
return Blur()
}
}
func (m Model[T]) Init() tea.Cmd {
return tea.Batch(m.suggestionManager.Init(), m.inputHandler.Init(), m.getInitFocus())
}
func (m Model[T]) View() string {
if !m.ready {
return "\n Initializing..."
}
view := m.renderer.View()
if shutdown {
// For the final view, add one more newline so the terminal prompt doesn't cut off the last line
view += "\n"
}
return view
}