X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Fsearch.go;h=139bcb4178e1be32a3a8c480b41f59c99ac47327;hb=7bc2d870cd0abbc90b8fa791e54626d970a3746c;hp=4253a316026dc3e0c7338a1411c6ece71b6ebc48;hpb=aeff0f8170f6bde5c45b8cb6c7e9bf0dc81ae742;p=micro.git diff --git a/cmd/micro/search.go b/cmd/micro/search.go index 4253a316..139bcb41 100644 --- a/cmd/micro/search.go +++ b/cmd/micro/search.go @@ -2,6 +2,7 @@ package main import ( "regexp" + "strings" "github.com/zyedidia/tcell" ) @@ -11,21 +12,29 @@ var ( 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 + + // Stores the history for searching + searchHistory []string ) // 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 func EndSearch() { + searchHistory[len(searchHistory)-1] = messenger.response searching = false messenger.hasPrompt = false messenger.Clear() @@ -35,20 +44,39 @@ func EndSearch() { } } +// 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 } } - messenger.HandleEvent(event) + messenger.HandleEvent(event, searchHistory) if messenger.cursorx < 0 { // Done @@ -64,9 +92,71 @@ func HandleSearchEvent(event tcell.Event, v *View) { 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 @@ -74,48 +164,27 @@ func Search(searchStr string, v *View, down bool) { if searchStr == "" { return } - var str string - var charPos int - text := v.Buf.String() - if down { - str = text[searchStart:] - charPos = searchStart - } else { - str = 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) } - } - - if !down { - match = matches[len(matches)-1] } else { - match = matches[0] + found = searchUp(r, v, searchStart, v.Buf.Start()) + if !found { + found = searchUp(r, v, v.Buf.End(), searchStart) + } } - - v.Cursor.curSelection[0] = charPos + match[0] - v.Cursor.curSelection[1] = charPos + match[1] - v.Cursor.x, v.Cursor.y = FromCharPos(charPos+match[1]-1, v.Buf) - if v.Relocate() { - v.matches = Match(v) + if !found { + v.Cursor.ResetSelection() } - lastSearch = searchStr }