Skip to content

Commit 3e16d5b

Browse files
committed
match beginning and end of line correctly
1 parent f49487d commit 3e16d5b

1 file changed

Lines changed: 64 additions & 42 deletions

File tree

internal/buffer/search.go

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,50 @@ import (
66
"github.com/zyedidia/micro/v2/internal/util"
77
)
88

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

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

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-
}
70+
for i := start.Y; i <= end.Y; i++ {
71+
l, charpos, padMode := findLineParams(b, start, end, i)
4572

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

4875
if match != nil {
4976
start := Loc{charpos + util.RunePos(l, match[0]), i}
77+
if padMode&padStart != 0 {
78+
start = start.Move(1, b)
79+
}
5080
end := Loc{charpos + util.RunePos(l, match[1]), i}
81+
if padMode&padEnd != 0 {
82+
end = end.Move(-1, b)
83+
}
5184
return [2]Loc{start, end}, true
5285
}
5386
}
@@ -69,34 +102,23 @@ func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
69102
start, end = end, start
70103
}
71104

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

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-
}
107+
for i := end.Y; i >= start.Y; i-- {
108+
l, charpos, padMode := findLineParams(b, start, end, i)
93109

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

96112
if allMatches != nil {
97113
match := allMatches[len(allMatches)-1]
98114
start := Loc{charpos + util.RunePos(l, match[0]), i}
115+
if padMode&padStart != 0 {
116+
start = start.Move(1, b)
117+
}
99118
end := Loc{charpos + util.RunePos(l, match[1]), i}
119+
if padMode&padEnd != 0 {
120+
end = end.Move(-1, b)
121+
}
100122
return [2]Loc{start, end}, true
101123
}
102124
}

0 commit comments

Comments
 (0)