Skip to content

Commit

Permalink
match beginning and end of line correctly for FindNext
Browse files Browse the repository at this point in the history
  • Loading branch information
matthias314 committed Feb 3, 2025
1 parent f49487d commit 574b48f
Showing 1 changed file with 69 additions and 42 deletions.
111 changes: 69 additions & 42 deletions internal/buffer/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,55 @@ package buffer

import (
"regexp"
"unicode/utf8"

"github.com/zyedidia/micro/v2/internal/util"
)

const (
padStart = 1 << iota
padEnd
)

// We want "^" and "$" to match only the beginning/end of a line, not the
// beginning/end of the search region if it is in the middle of a line.
// In that case we use padded regexps to require a rune before or after
// the match. (This also affects other empty-string patters like "\\b".)
// The function padRegexp creates these padded regexps.
func padRegexp(r *regexp.Regexp) [4]*regexp.Regexp {
rPadStart := regexp.MustCompile(".(?:" + r.String() + ")")
rPadEnd := regexp.MustCompile("(?:" + r.String() + ").")
rPadBoth := regexp.MustCompile(".(?:" + r.String() + ").")
return [4]*regexp.Regexp{r, rPadStart, rPadEnd, rPadBoth}
}

func findLineParams(b *Buffer, start, end Loc, i int) ([]byte, int, int) {
l := b.LineBytes(i)
charpos := 0
padMode := 0

if i == end.Y {
nchars := util.CharacterCount(l)
end.X = util.Clamp(end.X, 0, nchars)
if end.X < nchars {
l = util.SliceStart(l, end.X+1)
padMode |= padEnd
}
}

if i == start.Y {
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
if start.X > 0 {
charpos = start.X - 1
l = util.SliceEnd(l, charpos)
padMode |= padStart
}
}

return l, charpos, padMode
}

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 {
Expand All @@ -21,31 +66,22 @@ func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
start, end = end, start
}

for i := start.Y; i <= end.Y; i++ {
l := b.LineBytes(i)
charpos := 0
rPadded := padRegexp(r)

if i == start.Y && start.Y == end.Y {
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
end.X = util.Clamp(end.X, 0, nchars)
l = util.SliceStart(l, end.X)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == start.Y {
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == end.Y {
nchars := util.CharacterCount(l)
end.X = util.Clamp(end.X, 0, nchars)
l = util.SliceStart(l, end.X)
}
for i := start.Y; i <= end.Y; i++ {
l, charpos, padMode := findLineParams(b, start, end, i)

match := r.FindIndex(l)
match := rPadded[padMode].FindIndex(l)

if match != nil {
if padMode&padStart != 0 {
_, size := utf8.DecodeRune(l[match[0]:])
match[0] += size
}
if padMode&padEnd != 0 {
_, size := utf8.DecodeLastRune(l[:match[1]])
match[1] -= size
}
start := Loc{charpos + util.RunePos(l, match[0]), i}
end := Loc{charpos + util.RunePos(l, match[1]), i}
return [2]Loc{start, end}, true
Expand All @@ -69,33 +105,24 @@ func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
start, end = end, start
}

for i := end.Y; i >= start.Y; i-- {
l := b.LineBytes(i)
charpos := 0
rPadded := padRegexp(r)

if i == start.Y && start.Y == end.Y {
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
end.X = util.Clamp(end.X, 0, nchars)
l = util.SliceStart(l, end.X)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == start.Y {
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == end.Y {
nchars := util.CharacterCount(l)
end.X = util.Clamp(end.X, 0, nchars)
l = util.SliceStart(l, end.X)
}
for i := end.Y; i >= start.Y; i-- {
l, charpos, padMode := findLineParams(b, start, end, i)

allMatches := r.FindAllIndex(l, -1)
allMatches := rPadded[padMode].FindAllIndex(l, -1)

if allMatches != nil {
match := allMatches[len(allMatches)-1]
if padMode&padStart != 0 {
_, size := utf8.DecodeRune(l[match[0]:])
match[0] += size
}
start := Loc{charpos + util.RunePos(l, match[0]), i}
if padMode&padEnd != 0 {
_, size := utf8.DecodeLastRune(l[:match[1]])
match[1] -= size
}
end := Loc{charpos + util.RunePos(l, match[1]), i}
return [2]Loc{start, end}, true
}
Expand Down

0 comments on commit 574b48f

Please sign in to comment.