Gim/internal/action/search.go
Hayden Hargreaves 501d15e410
Some checks failed
Run Test Suite / test (push) Failing after 29s
WIP: working on search execution
2026-04-09 15:17:07 -07:00

179 lines
3.9 KiB
Go

package action
import (
"strings"
"git.gophernest.net/azpect/TextEditor/internal/core"
tea "github.com/charmbracelet/bubbletea"
)
type EnterSearchMode struct {
Forward bool
}
func (a EnterSearchMode) Execute(m Model) tea.Cmd {
search := m.SearchState()
search.Forword = a.Forward
// BUG: Not sure if this is safe?
m.SetCommandOutput(nil)
m.SetSearchState(search)
m.SetMode(core.SearchMode)
return nil
}
type ExitSearchMode struct{}
func (a ExitSearchMode) Execute(m Model) tea.Cmd {
// Reset state
search := m.SearchState()
if strings.TrimSpace(search.Query) != "" {
search.History = append(search.History, search.Query)
}
search.Cursor = 0
search.Query = ""
search.HistoryCursor = 0
// TODO: Maybe we want to keep Query until we enter it again next, for N and n?
m.SetSearchState(search)
m.SetMode(core.NormalMode)
return nil
}
type InsertSearchChar struct {
Char string
}
// InsertSearchChar.Execute: Inserts a character at the search cursor position.
func (a InsertSearchChar) Execute(m Model) tea.Cmd {
search := m.SearchState()
cur := search.Cursor
query := search.Query
search.Query = query[:cur] + a.Char + query[cur:]
search.Cursor++
m.SetSearchState(search)
return nil
}
// SearchBackspace implements Action - deletes character before cursor in search mode.
type SearchBackspace struct{}
// SearchBackspace.Execute: Deletes the character before the search cursor (Backspace key).
func (a SearchBackspace) Execute(m Model) tea.Cmd {
search := m.SearchState()
cur := search.Cursor
query := search.Query
if cur > 0 {
search.Query = query[:cur-1] + query[cur:]
search.Cursor--
m.SetSearchState(search)
}
return nil
}
// SearchDelete implements Action - deletes character at cursor in search mode.
type SearchDelete struct{}
// SearchDelete.Execute: Deletes the character at the command cursor (Delete key).
func (a SearchDelete) Execute(m Model) tea.Cmd {
search := m.SearchState()
cur := search.Cursor
query := search.Query
if cur < len(query)-1 {
search.Query = query[:cur+1] + query[cur+2:]
} else if cur == len(query)-1 {
// last text char, delete it
search.Query = query[:cur] + query[cur+1:]
} else if cur == len(query) && cur > 0 {
// if at end, we do backspace op
search.Query = query[:cur-1] + query[cur:]
search.Cursor = max(0, search.Cursor-1)
}
m.SetSearchState(search)
return nil
}
// SearchDeletePreviousWord implements Action - deletes word before cursor in search mode.
type SearchDeletePreviousWord struct{}
// SearchDeletePreviousWord.Execute: Deletes the word before the search cursor (Ctrl+W).
func (a SearchDeletePreviousWord) Execute(m Model) tea.Cmd {
search := m.SearchState()
cur := search.Cursor
cmd := search.Query
if cur > 0 {
newCur := cur
// If we are on punctuation, we should just skip them all and quit
if isPunctuation(cmd[newCur-1]) {
for newCur > 0 && isPunctuation(cmd[newCur-1]) {
newCur--
}
search.Query = cmd[:newCur] + cmd[cur:]
search.Cursor = newCur
m.SetSearchState(search)
return nil
}
// Skip whitespace immediately before the cursor
for newCur > 0 && (cmd[newCur-1] == ' ' || cmd[newCur-1] == '\t') {
newCur--
}
// Skip the word characters before the cursor
for newCur > 0 && isWordChar(cmd[newCur-1]) {
newCur--
}
// Delete everything from newCur up to cur in one operation
search.Query = cmd[:newCur] + cmd[cur:]
search.Cursor = newCur
m.SetSearchState(search)
}
return nil
}
type SearchExecute struct{}
func (a SearchExecute) Execute(m Model) tea.Cmd {
search := m.SearchState()
// Exit normally
defer func() {
act := ExitSearchMode{}
act.Execute(m)
}()
if !search.Forword {
m.SetCommandOutput(&core.CommandOutput{
Lines: []string{"reverse search not implemented yet."},
Inline: true,
IsError: true,
})
return nil
}
// Do the search
win := m.ActiveWindow()
buf := m.ActiveBuffer()
x, y := win.Cursor.Col, win.Cursor.Line
return nil
}