@@ -7,6 +7,7 @@ import (
77 "runtime"
88 "slices"
99 "strings"
10+ "time"
1011 "unicode"
1112
1213 "github.com/charmbracelet/bubbles/v2/key"
@@ -30,6 +31,8 @@ type FileCompletionItem struct {
3031 Path string // The file path
3132}
3233
34+type CompletionDebounceMsg struct{}
35+
3336type editorCmp struct {
3437 width int
3538 height int
@@ -46,6 +49,9 @@ type editorCmp struct {
4649 currentQuery string
4750 completionsStartIndex int
4851 isCompletionsOpen bool
52+
53+ // Debouncing for completions
54+ debounceTimer *time.Timer
4955}
5056
5157var DeleteKeyMaps = DeleteAttachmentKeyMaps{
@@ -141,6 +147,22 @@ func (m *editorCmp) send() tea.Cmd {
141147 )
142148}
143149
150+// debouncedCompletionFilter creates a debounced command for filtering completions
151+func (m *editorCmp) debouncedCompletionFilter() tea.Cmd {
152+ // Cancel existing timer if any
153+ if m.debounceTimer != nil {
154+ m.debounceTimer.Stop()
155+ }
156+
157+ // Create new timer for debouncing
158+ m.debounceTimer = time.NewTimer(150 * time.Millisecond)
159+
160+ return func() tea.Msg {
161+ <-m.debounceTimer.C
162+ return CompletionDebounceMsg{}
163+ }
164+}
165+
144166func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
145167 var cmd tea.Cmd
146168 var cmds []tea.Cmd
@@ -160,6 +182,18 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
160182 m.isCompletionsOpen = false
161183 m.currentQuery = ""
162184 m.completionsStartIndex = 0
185+ // Cancel any pending debounce timer
186+ if m.debounceTimer != nil {
187+ m.debounceTimer.Stop()
188+ m.debounceTimer = nil
189+ }
190+ case CompletionDebounceMsg:
191+ // Handle debounced completion filtering
192+ if m.isCompletionsOpen {
193+ return m, util.CmdHandler(completions.FilterCompletionsMsg{
194+ Query: m.currentQuery,
195+ })
196+ }
163197 case completions.SelectCompletionMsg:
164198 if !m.isCompletionsOpen {
165199 return m, nil
@@ -196,9 +230,8 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
196230 case msg.String() == "backspace" && m.isCompletionsOpen:
197231 if len(m.currentQuery) > 0 {
198232 m.currentQuery = m.currentQuery[:len(m.currentQuery)-1]
199− cmds = append(cmds, util.CmdHandler(completions.FilterCompletionsMsg{
200− Query: m.currentQuery,
201− }))
233+ // Use debounced filtering instead of immediate filtering
234+ cmds = append(cmds, m.debouncedCompletionFilter())
202235 } else {
203236 m.isCompletionsOpen = false
204237 m.currentQuery = ""
@@ -208,9 +241,8 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
208241 default:
209242 if m.isCompletionsOpen {
210243 m.currentQuery += msg.String()
211− cmds = append(cmds, util.CmdHandler(completions.FilterCompletionsMsg{
212− Query: m.currentQuery,
213− }))
244+ // Use debounced filtering instead of immediate filtering
245+ cmds = append(cmds, m.debouncedCompletionFilter())
214246 }
215247 }
216248 if key.Matches(msg, DeleteKeyMaps.AttachmentDeleteMode) {
@@ -258,6 +290,7 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
258290 }
259291 }
260292 }
293+
261294 m.textarea, cmd = m.textarea.Update(msg)
262295 cmds = append(cmds, cmd)
263296 return m, tea.Batch(cmds...)
@@ -355,6 +388,11 @@ func (m *editorCmp) startCompletions() tea.Msg {
355388// Blur implements Container.
356389func (c *editorCmp) Blur() tea.Cmd {
357390 c.textarea.Blur()
391+ // Clean up debounce timer when losing focus
392+ if c.debounceTimer != nil {
393+ c.debounceTimer.Stop()
394+ c.debounceTimer = nil
395+ }
358396 return nil
359397}
360398