@@ -21,15 +21,15 @@ const (
2121
2222// NewRegexpGroup creates a RegexpGroup from a string
2323func NewRegexpGroup (s string ) (RegexpGroup , error ) {
24- var r RegexpGroup
24+ var rgrp RegexpGroup
2525 var err error
26- r [0 ], err = regexp .Compile (s )
26+ rgrp [0 ], err = regexp .Compile (s )
2727 if err == nil {
28- r [padStart ] = regexp .MustCompile (".(?:" + s + ")" )
29- r [padEnd ] = regexp .MustCompile ("(?:" + s + ")." )
30- r [padStart | padEnd ] = regexp .MustCompile (".(?:" + s + ")." )
28+ rgrp [padStart ] = regexp .MustCompile (".(?:" + s + ")" )
29+ rgrp [padEnd ] = regexp .MustCompile ("(?:" + s + ")." )
30+ rgrp [padStart | padEnd ] = regexp .MustCompile (".(?:" + s + ")." )
3131 }
32- return r , err
32+ return rgrp , err
3333}
3434
3535func findLineParams (b * Buffer , start , end Loc , i int ) ([]byte , int , int ) {
@@ -59,7 +59,9 @@ func findLineParams(b *Buffer, start, end Loc, i int) ([]byte, int, int) {
5959 return l , charpos , padMode
6060}
6161
62- func (b * Buffer ) findDown (r RegexpGroup , start , end Loc ) ([2 ]Loc , bool ) {
62+ type bytesFind func (* regexp.Regexp , []byte ) []int
63+
64+ func (b * Buffer ) findDownFunc (rgrp RegexpGroup , start , end Loc , find bytesFind ) []Loc {
6365 lastcn := util .CharacterCount (b .LineBytes (b .LinesNum () - 1 ))
6466 if start .Y > b .LinesNum ()- 1 {
6567 start .X = lastcn - 1
@@ -77,7 +79,7 @@ func (b *Buffer) findDown(r RegexpGroup, start, end Loc) ([2]Loc, bool) {
7779 for i := start .Y ; i <= end .Y ; i ++ {
7880 l , charpos , padMode := findLineParams (b , start , end , i )
7981
80- match := r [padMode ]. FindIndex ( l )
82+ match := find ( rgrp [padMode ], l )
8183
8284 if match != nil {
8385 if padMode & padStart != 0 {
@@ -88,15 +90,31 @@ func (b *Buffer) findDown(r RegexpGroup, start, end Loc) ([2]Loc, bool) {
8890 _ , size := utf8 .DecodeLastRune (l [:match [1 ]])
8991 match [1 ] -= size
9092 }
91- start := Loc { charpos + util .RunePos ( l , match [ 0 ]), i }
92- end := Loc {charpos + util .RunePos (l , match [ 1 ] ), i }
93- return [ 2 ] Loc { start , end }, true
93+ return util .SliceMap ( match , func ( pos int ) Loc {
94+ return Loc {charpos + util .RunePos (l , pos ), i }
95+ })
9496 }
9597 }
96- return [2 ]Loc {}, false
98+ return nil
99+ }
100+
101+ type bufferFind func (* Buffer , RegexpGroup , Loc , Loc ) []Loc
102+
103+ // FindDown returns a slice containing the start and end positions
104+ // of the first match of `rgrp` between `start` and `end`, or nil
105+ // if no match exists.
106+ func (b * Buffer ) FindDown (rgrp RegexpGroup , start , end Loc ) []Loc {
107+ return b .findDownFunc (rgrp , start , end , (* regexp .Regexp ).FindIndex )
108+ }
109+
110+ // FindDownSubmatch returns a slice containing the start and end positions
111+ // of the first match of `rgrp` between `start` and `end` plus those
112+ // of all submatches (capturing groups), or nil if no match exists.
113+ func (b * Buffer ) FindDownSubmatch (rgrp RegexpGroup , start , end Loc ) []Loc {
114+ return b .findDownFunc (rgrp , start , end , (* regexp .Regexp ).FindSubmatchIndex )
97115}
98116
99- func (b * Buffer ) findUp ( r RegexpGroup , start , end Loc ) ([ 2 ]Loc , bool ) {
117+ func (b * Buffer ) findUpFunc ( rgrp RegexpGroup , start , end Loc , find bytesFind ) [ ]Loc {
100118 lastcn := util .CharacterCount (b .LineBytes (b .LinesNum () - 1 ))
101119 if start .Y > b .LinesNum ()- 1 {
102120 start .X = lastcn - 1
@@ -111,33 +129,63 @@ func (b *Buffer) findUp(r RegexpGroup, start, end Loc) ([2]Loc, bool) {
111129 start , end = end , start
112130 }
113131
114- var match [ 2 ]Loc
132+ var locs [ ]Loc
115133 for i := end .Y ; i >= start .Y ; i -- {
116134 charCount := util .CharacterCount (b .LineBytes (i ))
117135 from := Loc {0 , i }.Clamp (start , end )
118136 to := Loc {charCount , i }.Clamp (start , end )
119137
120- n := b .findAllFunc (r , from , to , func (from , to Loc ) {
121- match = [2 ]Loc {from , to }
138+ b .findAllFuncFunc (rgrp , from , to , func (b * Buffer , rgrp RegexpGroup , start , end Loc ) []Loc {
139+ return b .findDownFunc (rgrp , start , end , find )
140+ }, func (match []Loc ) {
141+ locs = match
122142 })
123143
124- if n != 0 {
125- return match , true
144+ if locs != nil {
145+ return locs
126146 }
127147 }
128- return match , false
148+ return nil
129149}
130150
131- func (b * Buffer ) findAllFunc (r RegexpGroup , start , end Loc , f func (from , to Loc )) int {
151+ // FindUp returns a slice containing the start and end positions
152+ // of the last match of `rgrp` between `start` and `end`, or nil
153+ // if no match exists.
154+ func (b * Buffer ) FindUp (rgrp RegexpGroup , start , end Loc ) []Loc {
155+ return b .findUpFunc (rgrp , start , end , func (re * regexp.Regexp , l []byte ) []int {
156+ allMatches := re .FindAllIndex (l , - 1 )
157+ if allMatches != nil {
158+ return allMatches [len (allMatches )- 1 ]
159+ } else {
160+ return nil
161+ }
162+ })
163+ }
164+
165+ // FindUpSubmatch returns a slice containing the start and end positions
166+ // of the last match of `rgrp` between `start` and `end` plus those
167+ // of all submatches (capturing groups), or nil if no match exists.
168+ func (b * Buffer ) FindUpSubmatch (rgrp RegexpGroup , start , end Loc ) []Loc {
169+ return b .findUpFunc (rgrp , start , end , func (re * regexp.Regexp , l []byte ) []int {
170+ allMatches := re .FindAllSubmatchIndex (l , - 1 )
171+ if allMatches != nil {
172+ return allMatches [len (allMatches )- 1 ]
173+ } else {
174+ return nil
175+ }
176+ })
177+ }
178+
179+ func (b * Buffer ) findAllFuncFunc (r RegexpGroup , start , end Loc , find bufferFind , f func ([]Loc )) int {
180+ n := 0
132181 loc := start
133- nfound := 0
134182 for {
135- match , found := b . findDown ( r , loc , end )
136- if ! found {
183+ match := find ( b , r , loc , end )
184+ if match == nil {
137185 break
138186 }
139- nfound ++
140- f (match [ 0 ], match [ 1 ] )
187+ n ++
188+ f (match )
141189 if match [0 ] != match [1 ] {
142190 loc = match [1 ]
143191 } else if match [1 ] != end {
@@ -146,7 +194,58 @@ func (b *Buffer) findAllFunc(r RegexpGroup, start, end Loc, f func(from, to Loc)
146194 break
147195 }
148196 }
149- return nfound
197+ return n
198+ }
199+
200+ // FindAllFunc calls the function `f` once for each match between `start`
201+ // and `end` of the regexp given by `s`. The argument of `f` is the slice
202+ // containing the start and end positions of the match. FindAllFunc returns
203+ // the number of matches plus any error that occured when compiling the regexp.
204+ func (b * Buffer ) FindAllFunc (s string , start , end Loc , f func ([]Loc )) (int , error ) {
205+ rgrp , err := NewRegexpGroup (s )
206+ if err == nil {
207+ return b .findAllFuncFunc (rgrp , start , end , (* Buffer ).FindDown , f ), nil
208+ } else {
209+ return - 1 , err
210+ }
211+ }
212+
213+ // FindAll returns a slice containing the start and end positions of all
214+ // matches between `start` and `end` of the regexp given by `s`, plus any
215+ // error that occured when compiling the regexp. If no match is found, the
216+ // slice returned is nil.
217+ func (b * Buffer ) FindAll (s string , start , end Loc ) ([][]Loc , error ) {
218+ var matches [][]Loc
219+ _ , err := b .FindAllFunc (s , start , end , func (match []Loc ) {
220+ matches = append (matches , match )
221+ })
222+ return matches , err
223+ }
224+
225+ // FindAllSubmatchFunc calls the function `f` once for each match between
226+ // `start` and `end` of the regexp given by `s`. The argument of `f` is the
227+ // slice containing the start and end positions of the match and all submatches
228+ // (capturing groups). FindAllSubmatch Func returns the number of matches plus
229+ // any error that occured when compiling the regexp.
230+ func (b * Buffer ) FindAllSubmatchFunc (s string , start , end Loc , f func ([]Loc )) (int , error ) {
231+ rgrp , err := NewRegexpGroup (s )
232+ if err == nil {
233+ return b .findAllFuncFunc (rgrp , start , end , (* Buffer ).FindDownSubmatch , f ), nil
234+ } else {
235+ return - 1 , err
236+ }
237+ }
238+
239+ // FindAllSubmatch returns a slice containing the start and end positions of
240+ // all matches and all submatches (capturing groups) between `start` and `end`
241+ // of the regexp given by `s`, plus any error that occured when compiling
242+ // the regexp. If no match is found, the slice returned is nil.
243+ func (b * Buffer ) FindAllSubmatch (s string , start , end Loc ) ([][]Loc , error ) {
244+ var matches [][]Loc
245+ _ , err := b .FindAllSubmatchFunc (s , start , end , func (match []Loc ) {
246+ matches = append (matches , match )
247+ })
248+ return matches , err
150249}
151250
152251// FindNext finds the next occurrence of a given string in the buffer
@@ -166,49 +265,65 @@ func (b *Buffer) FindNext(s string, start, end, from Loc, down bool, useRegex bo
166265 s = "(?i)" + s
167266 }
168267
169- r , err := NewRegexpGroup (s )
268+ rgrp , err := NewRegexpGroup (s )
170269 if err != nil {
171270 return [2 ]Loc {}, false , err
172271 }
173272
174- var found bool
175- var l [2 ]Loc
273+ var match []Loc
176274 if down {
177- l , found = b .findDown ( r , from , end )
178- if ! found {
179- l , found = b .findDown ( r , start , end )
275+ match = b .FindDown ( rgrp , from , end )
276+ if match == nil {
277+ match = b .FindDown ( rgrp , start , end )
180278 }
181279 } else {
182- l , found = b .findUp ( r , from , start )
183- if ! found {
184- l , found = b .findUp ( r , end , start )
280+ match = b .FindUp ( rgrp , from , start )
281+ if match == nil {
282+ match = b .FindUp ( rgrp , end , start )
185283 }
186284 }
187- return l , found , nil
285+ if match != nil {
286+ return [2 ]Loc {match [0 ], match [1 ]}, true , nil
287+ } else {
288+ return [2 ]Loc {}, false , nil
289+ }
188290}
189291
190- // ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area
191- // and returns the number of replacements made and the number of characters
192- // added or removed on the last line of the range
193- func (b * Buffer ) ReplaceRegex (start , end Loc , search RegexpGroup , replace []byte , captureGroups bool ) (int , int ) {
292+ // ReplaceAll replaces all matches of 's' with 'template' in the given area
293+ // and returns the number of replacements made, the new end position and any
294+ // error that occured during regexp compilation
295+ func (b * Buffer ) ReplaceAll (s string , start , end Loc , template string ) (int , Loc , error ) {
296+ rgrp , err := NewRegexpGroup (s )
297+ if err != nil {
298+ return - 1 , Loc {- 1 , - 1 }, err
299+ }
300+
194301 if start .GreaterThan (end ) {
195302 start , end = end , start
196303 }
197304
305+ templateBytes := []byte (template )
198306 charsEnd := util .CharacterCount (b .LineBytes (end .Y ))
199307
200308 var deltas []Delta
201- nfound := b .findAllFunc (search , start , end , func (from , to Loc ) {
202- var newText []byte
203- if captureGroups {
204- newText = search [0 ].ReplaceAll (b .Substr (from , to ), replace )
205- } else {
206- newText = replace
207- }
208- deltas = append (deltas , Delta {newText , from , to })
209- })
309+ var replace []byte
310+
311+ f := func (match []Loc ) {
312+ deltas = append (deltas , Delta {replace , match [0 ], match [1 ]})
313+ }
314+
315+ n := b .findAllFuncFunc (rgrp , start , end , func (b * Buffer , r RegexpGroup , start , end Loc ) []Loc {
316+ return b .findDownFunc (r , start , end , func (re * regexp.Regexp , l []byte ) []int {
317+ match := re .FindSubmatchIndex (l )
318+ if match == nil {
319+ return nil
320+ }
321+ replace = re .Expand (nil , templateBytes , l , match )
322+ return match [:2 ] // this way match[2:] is not transformed to Loc's
323+ })
324+ }, f )
210325 b .MultipleReplace (deltas )
211326
212327 deltaX := util .CharacterCount (b .LineBytes (end .Y )) - charsEnd
213- return nfound , deltaX
328+ return n , Loc { end . X + deltaX , end . Y }, nil
214329}
0 commit comments