mirror of
https://github.com/zyedidia/micro.git
synced 2026-02-04 22:20:20 +09:00
188 lines
4.1 KiB
Go
188 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/zyedidia/tcell"
|
|
)
|
|
|
|
var (
|
|
// What was the last search
|
|
lastSearch string
|
|
|
|
// Where should we start the search down from (or up from)
|
|
searchStart Loc
|
|
|
|
// Is there currently a search in progress
|
|
searching bool
|
|
|
|
// Stores the history for searching
|
|
searchHistory []string
|
|
)
|
|
|
|
// BeginSearch starts a search
|
|
func BeginSearch(searchStr string) {
|
|
searchHistory = append(searchHistory, "")
|
|
messenger.historyNum = len(searchHistory) - 1
|
|
searching = 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()
|
|
messenger.Reset()
|
|
if lastSearch != "" {
|
|
messenger.Message("^P Previous ^N Next")
|
|
}
|
|
}
|
|
|
|
// 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.KeyEscape:
|
|
// Exit the search mode
|
|
ExitSearch(v)
|
|
return
|
|
case tcell.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEnter:
|
|
// Done
|
|
EndSearch()
|
|
return
|
|
}
|
|
}
|
|
|
|
messenger.HandleEvent(event, searchHistory)
|
|
|
|
if messenger.cursorx < 0 {
|
|
// Done
|
|
EndSearch()
|
|
return
|
|
}
|
|
|
|
if messenger.response == "" {
|
|
v.Cursor.ResetSelection()
|
|
// We don't end the search though
|
|
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
|
|
func Search(searchStr string, v *View, down bool) {
|
|
if searchStr == "" {
|
|
return
|
|
}
|
|
r, err := regexp.Compile(searchStr)
|
|
if v.Buf.Settings["ignorecase"].(bool) {
|
|
r, err = regexp.Compile("(?i)" + searchStr)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var found bool
|
|
if down {
|
|
found = searchDown(r, v, searchStart, v.Buf.End())
|
|
if !found {
|
|
found = searchDown(r, v, v.Buf.Start(), searchStart)
|
|
}
|
|
} else {
|
|
found = searchUp(r, v, searchStart, v.Buf.Start())
|
|
if !found {
|
|
found = searchUp(r, v, v.Buf.End(), searchStart)
|
|
}
|
|
}
|
|
if found {
|
|
lastSearch = searchStr
|
|
} else {
|
|
v.Cursor.ResetSelection()
|
|
}
|
|
}
|