micro/internal/buffer/search.go

199 lines
4.8 KiB
Go
Raw Normal View History

2019-01-03 23:27:43 +03:00
package buffer
import (
"regexp"
2020-05-04 17:16:15 +03:00
"github.com/zyedidia/micro/v2/internal/util"
2019-01-03 23:27:43 +03:00
)
func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
lastcn := util.CharacterCount(b.LineBytes(b.LinesNum() - 1))
if start.Y > b.LinesNum()-1 {
start.X = lastcn - 1
}
if end.Y > b.LinesNum()-1 {
end.X = lastcn
}
2019-01-03 23:27:43 +03:00
start.Y = util.Clamp(start.Y, 0, b.LinesNum()-1)
2019-01-16 06:45:28 +03:00
end.Y = util.Clamp(end.Y, 0, b.LinesNum()-1)
if start.GreaterThan(end) {
start, end = end, start
}
2019-01-03 23:27:43 +03:00
for i := start.Y; i <= end.Y; i++ {
l := b.LineBytes(i)
charpos := 0
2019-01-16 06:45:28 +03:00
if i == start.Y && start.Y == end.Y {
2020-05-20 23:47:08 +03:00
nchars := util.CharacterCount(l)
2019-06-15 21:44:03 +03:00
start.X = util.Clamp(start.X, 0, nchars)
end.X = util.Clamp(end.X, 0, nchars)
2019-01-16 06:45:28 +03:00
l = util.SliceStart(l, end.X)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == start.Y {
2020-05-20 23:47:08 +03:00
nchars := util.CharacterCount(l)
2019-06-15 21:44:03 +03:00
start.X = util.Clamp(start.X, 0, nchars)
2019-01-03 23:27:43 +03:00
l = util.SliceEnd(l, start.X)
charpos = start.X
2019-01-16 06:45:28 +03:00
} else if i == end.Y {
2020-05-20 23:47:08 +03:00
nchars := util.CharacterCount(l)
2019-06-15 21:44:03 +03:00
end.X = util.Clamp(end.X, 0, nchars)
2019-01-16 06:45:28 +03:00
l = util.SliceStart(l, end.X)
2019-01-03 23:27:43 +03:00
}
match := r.FindIndex(l)
if match != nil {
start := Loc{charpos + util.RunePos(l, match[0]), i}
end := Loc{charpos + util.RunePos(l, match[1]), i}
return [2]Loc{start, end}, true
}
}
return [2]Loc{}, false
}
func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
lastcn := util.CharacterCount(b.LineBytes(b.LinesNum() - 1))
if start.Y > b.LinesNum()-1 {
start.X = lastcn - 1
}
if end.Y > b.LinesNum()-1 {
end.X = lastcn
}
2019-01-03 23:27:43 +03:00
start.Y = util.Clamp(start.Y, 0, b.LinesNum()-1)
2019-01-16 06:45:28 +03:00
end.Y = util.Clamp(end.Y, 0, b.LinesNum()-1)
2019-01-03 23:27:43 +03:00
2019-01-16 06:45:28 +03:00
if start.GreaterThan(end) {
start, end = end, start
}
for i := end.Y; i >= start.Y; i-- {
2019-01-03 23:27:43 +03:00
l := b.LineBytes(i)
2019-01-16 06:45:28 +03:00
charpos := 0
2019-01-03 23:27:43 +03:00
2019-01-16 06:45:28 +03:00
if i == start.Y && start.Y == end.Y {
2020-05-20 23:47:08 +03:00
nchars := util.CharacterCount(l)
2019-06-15 21:44:03 +03:00
start.X = util.Clamp(start.X, 0, nchars)
end.X = util.Clamp(end.X, 0, nchars)
2019-01-16 06:45:28 +03:00
l = util.SliceStart(l, end.X)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == start.Y {
2020-05-20 23:47:08 +03:00
nchars := util.CharacterCount(l)
2019-06-15 21:44:03 +03:00
start.X = util.Clamp(start.X, 0, nchars)
2019-01-16 06:45:28 +03:00
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == end.Y {
2020-05-20 23:47:08 +03:00
nchars := util.CharacterCount(l)
2019-06-15 21:44:03 +03:00
end.X = util.Clamp(end.X, 0, nchars)
2019-01-16 06:45:28 +03:00
l = util.SliceStart(l, end.X)
2019-01-03 23:27:43 +03:00
}
2021-08-22 00:58:30 +03:00
allMatches := r.FindAllIndex(l, -1)
2019-01-03 23:27:43 +03:00
2021-08-22 00:58:30 +03:00
if allMatches != nil {
match := allMatches[len(allMatches)-1]
2019-01-16 06:45:28 +03:00
start := Loc{charpos + util.RunePos(l, match[0]), i}
end := Loc{charpos + util.RunePos(l, match[1]), i}
2019-01-03 23:27:43 +03:00
return [2]Loc{start, end}, true
}
}
return [2]Loc{}, false
}
// FindNext finds the next occurrence of a given string in the buffer
// It returns the start and end location of the match (if found) and
// a boolean indicating if it was found
// May also return an error if the search regex is invalid
2019-01-16 06:45:28 +03:00
func (b *Buffer) FindNext(s string, start, end, from Loc, down bool, useRegex bool) ([2]Loc, bool, error) {
2019-01-03 23:27:43 +03:00
if s == "" {
return [2]Loc{}, false, nil
}
var r *regexp.Regexp
var err error
2019-01-16 06:45:28 +03:00
if !useRegex {
s = regexp.QuoteMeta(s)
}
2019-01-03 23:27:43 +03:00
if b.Settings["ignorecase"].(bool) {
r, err = regexp.Compile("(?i)" + s)
} else {
r, err = regexp.Compile(s)
}
if err != nil {
return [2]Loc{}, false, err
}
2019-12-30 06:02:14 +03:00
var found bool
2019-01-03 23:27:43 +03:00
var l [2]Loc
if down {
2019-01-16 06:45:28 +03:00
l, found = b.findDown(r, from, end)
2019-01-03 23:27:43 +03:00
if !found {
l, found = b.findDown(r, start, end)
2019-01-03 23:27:43 +03:00
}
} else {
2019-01-16 06:45:28 +03:00
l, found = b.findUp(r, from, start)
2019-01-03 23:27:43 +03:00
if !found {
l, found = b.findUp(r, end, start)
2019-01-03 23:27:43 +03:00
}
}
return l, found, nil
}
2019-01-16 06:45:28 +03:00
// ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area
// and returns the number of replacements made and the number of runes
// added or removed on the last line of the range
func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte, captureGroups bool) (int, int) {
2019-01-16 06:45:28 +03:00
if start.GreaterThan(end) {
start, end = end, start
}
netrunes := 0
2019-01-16 06:45:28 +03:00
found := 0
var deltas []Delta
for i := start.Y; i <= end.Y; i++ {
l := b.lines[i].data
charpos := 0
if start.Y == end.Y && i == start.Y {
l = util.SliceStart(l, end.X)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == start.Y {
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == end.Y {
l = util.SliceStart(l, end.X)
}
newText := search.ReplaceAllFunc(l, func(in []byte) []byte {
var result []byte
if captureGroups {
for _, submatches := range search.FindAllSubmatchIndex(in, -1) {
result = search.Expand(result, replace, in, submatches)
}
} else {
result = replace
}
2019-01-16 06:45:28 +03:00
found++
if i == end.Y {
netrunes += util.CharacterCount(result) - util.CharacterCount(in)
}
return result
2019-01-16 06:45:28 +03:00
})
from := Loc{charpos, i}
2020-05-20 23:47:08 +03:00
to := Loc{charpos + util.CharacterCount(l), i}
2019-01-16 06:45:28 +03:00
deltas = append(deltas, Delta{newText, from, to})
}
b.MultipleReplace(deltas)
return found, netrunes
2019-01-16 06:45:28 +03:00
}