7 "github.com/zyedidia/tcell"
11 // What was the last search
14 // Where should we start the search down from (or up from)
17 // Is there currently a search in progress
20 // Stores the history for searching
21 searchHistory []string
24 // BeginSearch starts a search
25 func BeginSearch(searchStr string) {
26 searchHistory = append(searchHistory, "")
27 messenger.historyNum = len(searchHistory) - 1
29 messenger.response = searchStr
30 messenger.cursorx = Count(searchStr)
31 messenger.Message("Find: ")
32 messenger.hasPrompt = true
35 // EndSearch stops the current search
37 searchHistory[len(searchHistory)-1] = messenger.response
39 messenger.hasPrompt = false
43 messenger.Message("^P Previous ^N Next")
47 // ExitSearch exits the search mode, reset active search phrase, and clear status bar
48 func ExitSearch(v *View) {
51 messenger.hasPrompt = false
54 v.Cursor.ResetSelection()
57 // HandleSearchEvent takes an event and a view and will do a real time match from the messenger's output
58 // to the current buffer. It searches down the buffer.
59 func HandleSearchEvent(event tcell.Event, v *View) {
60 switch e := event.(type) {
64 // Exit the search mode
67 case tcell.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEnter:
74 messenger.HandleEvent(event, searchHistory)
76 if messenger.cursorx < 0 {
82 if messenger.response == "" {
83 v.Cursor.ResetSelection()
84 // We don't end the search though
88 Search(messenger.response, v, true)
95 func searchDown(r *regexp.Regexp, v *View, start, end Loc) bool {
96 for i := start.Y; i <= end.Y; i++ {
100 runes := []rune(string(v.Buf.lines[i].data))
101 l = []byte(string(runes[start.X:]))
104 if strings.Contains(r.String(), "^") && start.X != 0 {
108 l = v.Buf.lines[i].data
111 match := r.FindIndex(l)
114 v.Cursor.SetSelectionStart(Loc{charPos + runePos(match[0], string(l)), i})
115 v.Cursor.SetSelectionEnd(Loc{charPos + runePos(match[1], string(l)), i})
116 v.Cursor.OrigSelection[0] = v.Cursor.CurSelection[0]
117 v.Cursor.OrigSelection[1] = v.Cursor.CurSelection[1]
118 v.Cursor.Loc = v.Cursor.CurSelection[1]
126 func searchUp(r *regexp.Regexp, v *View, start, end Loc) bool {
127 for i := start.Y; i >= end.Y; i-- {
130 runes := []rune(string(v.Buf.lines[i].data))
131 l = []byte(string(runes[:start.X]))
133 if strings.Contains(r.String(), "$") && start.X != Count(string(l)) {
137 l = v.Buf.lines[i].data
140 match := r.FindIndex(l)
143 v.Cursor.SetSelectionStart(Loc{runePos(match[0], string(l)), i})
144 v.Cursor.SetSelectionEnd(Loc{runePos(match[1], string(l)), i})
145 v.Cursor.OrigSelection[0] = v.Cursor.CurSelection[0]
146 v.Cursor.OrigSelection[1] = v.Cursor.CurSelection[1]
147 v.Cursor.Loc = v.Cursor.CurSelection[1]
155 // Search searches in the view for the given regex. The down bool
156 // specifies whether it should search down from the searchStart position
158 func Search(searchStr string, v *View, down bool) {
162 r, err := regexp.Compile(searchStr)
163 if v.Buf.Settings["ignorecase"].(bool) {
164 r, err = regexp.Compile("(?i)" + searchStr)
172 found = searchDown(r, v, searchStart, v.Buf.End())
174 found = searchDown(r, v, v.Buf.Start(), searchStart)
177 found = searchUp(r, v, searchStart, v.Buf.Start())
179 found = searchUp(r, v, v.Buf.End(), searchStart)
183 lastSearch = searchStr
185 v.Cursor.ResetSelection()