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
68 // If the user has pressed Enter, they want this to be the lastSearch
69 lastSearch = messenger.response
72 case tcell.KeyCtrlQ, tcell.KeyCtrlC:
79 messenger.HandleEvent(event, searchHistory)
81 if messenger.cursorx < 0 {
87 if messenger.response == "" {
88 v.Cursor.ResetSelection()
89 // We don't end the search though
93 Search(messenger.response, v, true)
100 func searchDown(r *regexp.Regexp, v *View, start, end Loc) bool {
101 if start.Y >= v.Buf.NumLines {
102 start.Y = v.Buf.NumLines - 1
107 for i := start.Y; i <= end.Y; i++ {
111 runes := []rune(string(v.Buf.lines[i].data))
112 if start.X >= len(runes) {
113 start.X = len(runes) - 1
118 l = []byte(string(runes[start.X:]))
121 if strings.Contains(r.String(), "^") && start.X != 0 {
125 l = v.Buf.lines[i].data
128 match := r.FindIndex(l)
131 v.Cursor.SetSelectionStart(Loc{charPos + runePos(match[0], string(l)), i})
132 v.Cursor.SetSelectionEnd(Loc{charPos + runePos(match[1], string(l)), i})
133 v.Cursor.OrigSelection[0] = v.Cursor.CurSelection[0]
134 v.Cursor.OrigSelection[1] = v.Cursor.CurSelection[1]
135 v.Cursor.Loc = v.Cursor.CurSelection[1]
143 func searchUp(r *regexp.Regexp, v *View, start, end Loc) bool {
144 if start.Y >= v.Buf.NumLines {
145 start.Y = v.Buf.NumLines - 1
150 for i := start.Y; i >= end.Y; i-- {
153 runes := []rune(string(v.Buf.lines[i].data))
154 if start.X >= len(runes) {
155 start.X = len(runes) - 1
160 l = []byte(string(runes[:start.X]))
162 if strings.Contains(r.String(), "$") && start.X != Count(string(l)) {
166 l = v.Buf.lines[i].data
169 match := r.FindIndex(l)
172 v.Cursor.SetSelectionStart(Loc{runePos(match[0], string(l)), i})
173 v.Cursor.SetSelectionEnd(Loc{runePos(match[1], string(l)), i})
174 v.Cursor.OrigSelection[0] = v.Cursor.CurSelection[0]
175 v.Cursor.OrigSelection[1] = v.Cursor.CurSelection[1]
176 v.Cursor.Loc = v.Cursor.CurSelection[1]
184 // Search searches in the view for the given regex. The down bool
185 // specifies whether it should search down from the searchStart position
187 func Search(searchStr string, v *View, down bool) {
191 r, err := regexp.Compile(searchStr)
192 if v.Buf.Settings["ignorecase"].(bool) {
193 r, err = regexp.Compile("(?i)" + searchStr)
201 found = searchDown(r, v, searchStart, v.Buf.End())
203 found = searchDown(r, v, v.Buf.Start(), searchStart)
206 found = searchUp(r, v, searchStart, v.Buf.Start())
208 found = searchUp(r, v, v.Buf.End(), searchStart)
212 v.Cursor.ResetSelection()