]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/search.go
Set OrigSelection during search
[micro.git] / cmd / micro / search.go
index 98e80758bad8b3016d1b6d86e22fbe303a6c7ca6..8317fd0a9109a62672a676ea608458ecf6f4e339 100644 (file)
@@ -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,34 @@ 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.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEnter:
                        // Done
                        EndSearch()
                        return
                }
        }
 
-       messenger.HandleEvent(event)
+       messenger.HandleEvent(event, searchHistory)
 
        if messenger.cursorx < 0 {
                // Done
@@ -64,9 +87,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,56 +159,29 @@ 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 settings["ignorecase"].(bool) {
+       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] = charPos + runePos(match[0], str)
-       v.Cursor.CurSelection[1] = charPos + runePos(match[1], str)
-       v.Cursor.X, v.Cursor.Y = FromCharPos(charPos+match[1]-1, v.Buf)
-       if v.Relocate() {
-               v.matches = Match(v)
+       if found {
+               lastSearch = searchStr
+       } else {
+               v.Cursor.ResetSelection()
        }
-       lastSearch = searchStr
 }