Skip to content

Commit ab98feb

Browse files
committed
match beginning and end of line correctly for FindNext
1 parent f49487d commit ab98feb

1 file changed

Lines changed: 69 additions & 42 deletions

File tree

internal/buffer/search.go

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,55 @@ package buffer
22

33
import (
44
"regexp"
5+
"unicode/utf8"
56

67
"github.com/zyedidia/micro/v2/internal/util"
78
)
89

10+
const (
11+
padStart = 1 << iota
12+
padEnd
13+
)
14+
15+
// We want "^" and "$" to match only the beginning/end of a line, not the
16+
// beginning/end of the search region if it is in the middle of a line.
17+
// In that case we use padded regexps to require a rune before or after
18+
// the match. (This also affects other empty-string patters like "\\b".)
19+
// The function padRegexp creates these padded regexps.
20+
func padRegexp(r *regexp.Regexp) [4]*regexp.Regexp {
21+
rPadStart := regexp.MustCompile(".(?:" + r.String() + ")")
22+
rPadEnd := regexp.MustCompile("(?:" + r.String() + ").")
23+
rPadBoth := regexp.MustCompile(".(?:" + r.String() + ").")
24+
return [4]*regexp.Regexp{r, rPadStart, rPadEnd, rPadBoth}
25+
}
26+
27+
func findLineParams(b *Buffer, start, end Loc, i int) ([]byte, int, int) {
28+
l := b.LineBytes(i)
29+
charpos := 0
30+
padMode := 0
31+
32+
if i == end.Y {
33+
nchars := util.CharacterCount(l)
34+
end.X = util.Clamp(end.X, 0, nchars)
35+
if end.X < nchars {
36+
l = util.SliceStart(l, end.X+1)
37+
padMode |= padEnd
38+
}
39+
}
40+
41+
if i == start.Y {
42+
nchars := util.CharacterCount(l)
43+
start.X = util.Clamp(start.X, 0, nchars)
44+
if start.X > 0 {
45+
charpos = start.X - 1
46+
l = util.SliceEnd(l, charpos)
47+
padMode |= padStart
48+
}
49+
}
50+
51+
return l, charpos, padMode
52+
}
53+
954
func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
1055
lastcn := util.CharacterCount(b.LineBytes(b.LinesNum() - 1))
1156
if start.Y > b.LinesNum()-1 {
@@ -21,32 +66,23 @@ func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
2166
start, end = end, start
2267
}
2368

24-
for i := start.Y; i <= end.Y; i++ {
25-
l := b.LineBytes(i)
26-
charpos := 0
69+
rPadded := padRegexp(r)
2770

28-
if i == start.Y && start.Y == end.Y {
29-
nchars := util.CharacterCount(l)
30-
start.X = util.Clamp(start.X, 0, nchars)
31-
end.X = util.Clamp(end.X, 0, nchars)
32-
l = util.SliceStart(l, end.X)
33-
l = util.SliceEnd(l, start.X)
34-
charpos = start.X
35-
} else if i == start.Y {
36-
nchars := util.CharacterCount(l)
37-
start.X = util.Clamp(start.X, 0, nchars)
38-
l = util.SliceEnd(l, start.X)
39-
charpos = start.X
40-
} else if i == end.Y {
41-
nchars := util.CharacterCount(l)
42-
end.X = util.Clamp(end.X, 0, nchars)
43-
l = util.SliceStart(l, end.X)
44-
}
71+
for i := start.Y; i <= end.Y; i++ {
72+
l, charpos, padMode := findLineParams(b, start, end, i)
4573

46-
match := r.FindIndex(l)
74+
match := rPadded[padMode].FindIndex(l)
4775

4876
if match != nil {
77+
if padMode&padStart != 0 {
78+
_, size := utf8.DecodeRune(l[match[0]:])
79+
match[0] += size
80+
}
4981
start := Loc{charpos + util.RunePos(l, match[0]), i}
82+
if padMode&padEnd != 0 {
83+
_, size := utf8.DecodeLastRune(l[:match[1]])
84+
match[1] -= size
85+
}
5086
end := Loc{charpos + util.RunePos(l, match[1]), i}
5187
return [2]Loc{start, end}, true
5288
}
@@ -69,33 +105,24 @@ func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
69105
start, end = end, start
70106
}
71107

72-
for i := end.Y; i >= start.Y; i-- {
73-
l := b.LineBytes(i)
74-
charpos := 0
108+
rPadded := padRegexp(r)
75109

76-
if i == start.Y && start.Y == end.Y {
77-
nchars := util.CharacterCount(l)
78-
start.X = util.Clamp(start.X, 0, nchars)
79-
end.X = util.Clamp(end.X, 0, nchars)
80-
l = util.SliceStart(l, end.X)
81-
l = util.SliceEnd(l, start.X)
82-
charpos = start.X
83-
} else if i == start.Y {
84-
nchars := util.CharacterCount(l)
85-
start.X = util.Clamp(start.X, 0, nchars)
86-
l = util.SliceEnd(l, start.X)
87-
charpos = start.X
88-
} else if i == end.Y {
89-
nchars := util.CharacterCount(l)
90-
end.X = util.Clamp(end.X, 0, nchars)
91-
l = util.SliceStart(l, end.X)
92-
}
110+
for i := end.Y; i >= start.Y; i-- {
111+
l, charpos, padMode := findLineParams(b, start, end, i)
93112

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

96115
if allMatches != nil {
97116
match := allMatches[len(allMatches)-1]
117+
if padMode&padStart != 0 {
118+
_, size := utf8.DecodeRune(l[match[0]:])
119+
match[0] += size
120+
}
98121
start := Loc{charpos + util.RunePos(l, match[0]), i}
122+
if padMode&padEnd != 0 {
123+
_, size := utf8.DecodeLastRune(l[:match[1]])
124+
match[1] -= size
125+
}
99126
end := Loc{charpos + util.RunePos(l, match[1]), i}
100127
return [2]Loc{start, end}, true
101128
}

0 commit comments

Comments
 (0)