@@ -2,10 +2,55 @@ package buffer
22
33import (
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+
954func (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