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 }