147 lines
3.4 KiB
Go
147 lines
3.4 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
|
|
|
|
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
|
|
}
|