import (
"regexp"
+ "strings"
"github.com/zyedidia/tcell"
)
lastSearch string
// Where should we start the search down from (or up from)
- searchStart int
+ searchStart Loc
// Is there currently a search in progress
searching bool
)
// BeginSearch starts a search
-func BeginSearch() {
+func BeginSearch(searchStr string) {
searchHistory = append(searchHistory, "")
messenger.historyNum = len(searchHistory) - 1
searching = true
- messenger.hasPrompt = true
+ messenger.response = searchStr
+ messenger.cursorx = Count(searchStr)
messenger.Message("Find: ")
+ messenger.hasPrompt = true
}
// EndSearch stops the current search
}
}
+// ExitSearch exits the search mode, reset active search phrase, and clear status bar
+func ExitSearch(v *View) {
+ lastSearch = ""
+ searching = false
+ messenger.hasPrompt = false
+ messenger.Clear()
+ messenger.Reset()
+ v.Cursor.ResetSelection()
+}
+
// HandleSearchEvent takes an event and a view and will do a real time match from the messenger's output
// to the current buffer. It searches down the buffer.
func HandleSearchEvent(event tcell.Event, v *View) {
switch e := event.(type) {
case *tcell.EventKey:
switch e.Key() {
- case tcell.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEscape, tcell.KeyEnter:
+ case tcell.KeyEscape:
+ // Exit the search mode
+ ExitSearch(v)
+ return
+ case tcell.KeyEnter:
+ // If the user has pressed Enter, they want this to be the lastSearch
+ lastSearch = messenger.response
+ EndSearch()
+ return
+ case tcell.KeyCtrlQ, tcell.KeyCtrlC:
// Done
EndSearch()
return
Search(messenger.response, v, true)
+ v.Relocate()
+
return
}
+func searchDown(r *regexp.Regexp, v *View, start, end Loc) bool {
+ for i := start.Y; i <= end.Y; i++ {
+ var l []byte
+ var charPos int
+ if i == start.Y {
+ runes := []rune(string(v.Buf.lines[i].data))
+ l = []byte(string(runes[start.X:]))
+ charPos = start.X
+
+ if strings.Contains(r.String(), "^") && start.X != 0 {
+ continue
+ }
+ } else {
+ l = v.Buf.lines[i].data
+ }
+
+ match := r.FindIndex(l)
+
+ if match != nil {
+ v.Cursor.SetSelectionStart(Loc{charPos + runePos(match[0], string(l)), i})
+ v.Cursor.SetSelectionEnd(Loc{charPos + runePos(match[1], string(l)), i})
+ v.Cursor.OrigSelection[0] = v.Cursor.CurSelection[0]
+ v.Cursor.OrigSelection[1] = v.Cursor.CurSelection[1]
+ v.Cursor.Loc = v.Cursor.CurSelection[1]
+
+ return true
+ }
+ }
+ return false
+}
+
+func searchUp(r *regexp.Regexp, v *View, start, end Loc) bool {
+ for i := start.Y; i >= end.Y; i-- {
+ var l []byte
+ if i == start.Y {
+ runes := []rune(string(v.Buf.lines[i].data))
+ l = []byte(string(runes[:start.X]))
+
+ if strings.Contains(r.String(), "$") && start.X != Count(string(l)) {
+ continue
+ }
+ } else {
+ l = v.Buf.lines[i].data
+ }
+
+ match := r.FindIndex(l)
+
+ if match != nil {
+ v.Cursor.SetSelectionStart(Loc{runePos(match[0], string(l)), i})
+ v.Cursor.SetSelectionEnd(Loc{runePos(match[1], string(l)), i})
+ v.Cursor.OrigSelection[0] = v.Cursor.CurSelection[0]
+ v.Cursor.OrigSelection[1] = v.Cursor.CurSelection[1]
+ v.Cursor.Loc = v.Cursor.CurSelection[1]
+
+ return true
+ }
+ }
+ return false
+}
+
// Search searches in the view for the given regex. The down bool
// specifies whether it should search down from the searchStart position
// or up from there
if searchStr == "" {
return
}
- var str string
- var charPos int
- text := v.Buf.String()
- if down {
- str = string([]rune(text)[searchStart:])
- charPos = searchStart
- } else {
- str = string([]rune(text)[:searchStart])
- }
r, err := regexp.Compile(searchStr)
if v.Buf.Settings["ignorecase"].(bool) {
r, err = regexp.Compile("(?i)" + searchStr)
if err != nil {
return
}
- matches := r.FindAllStringIndex(str, -1)
- var match []int
- if matches == nil {
- // Search the entire buffer now
- matches = r.FindAllStringIndex(text, -1)
- charPos = 0
- if matches == nil {
- v.Cursor.ResetSelection()
- return
- }
- if !down {
- match = matches[len(matches)-1]
- } else {
- match = matches[0]
+ var found bool
+ if down {
+ found = searchDown(r, v, searchStart, v.Buf.End())
+ if !found {
+ found = searchDown(r, v, v.Buf.Start(), searchStart)
}
- str = text
- }
-
- if !down {
- match = matches[len(matches)-1]
} else {
- match = matches[0]
- }
-
- if match[0] == match[1] {
- return
+ found = searchUp(r, v, searchStart, v.Buf.Start())
+ if !found {
+ found = searchUp(r, v, v.Buf.End(), searchStart)
+ }
}
-
- v.Cursor.CurSelection[0] = FromCharPos(charPos+runePos(match[0], str), v.Buf)
- v.Cursor.CurSelection[1] = FromCharPos(charPos+runePos(match[1], str), v.Buf)
- v.Cursor.Loc = v.Cursor.CurSelection[1]
- if v.Relocate() {
- v.matches = Match(v)
+ if !found {
+ v.Cursor.ResetSelection()
}
- lastSearch = searchStr
}