@@ -127,6 +127,26 @@ func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
127127 return [2 ]Loc {}, false
128128}
129129
130+ func (b * Buffer ) findAll (r * regexp.Regexp , start , end Loc ) [][2 ]Loc {
131+ matches := [][2 ]Loc {}
132+ loc := start
133+ for {
134+ match , found := b .findDown (r , loc , end )
135+ if ! found {
136+ break
137+ }
138+ matches = append (matches , match )
139+ if match [0 ] != match [1 ] {
140+ loc = match [1 ]
141+ } else if match [1 ] != end {
142+ loc = match [1 ].Move (1 , b )
143+ } else {
144+ break
145+ }
146+ }
147+ return matches
148+ }
149+
130150// FindNext finds the next occurrence of a given string in the buffer
131151// It returns the start and end location of the match (if found) and
132152// a boolean indicating if it was found
@@ -170,53 +190,58 @@ func (b *Buffer) FindNext(s string, start, end, from Loc, down bool, useRegex bo
170190}
171191
172192// ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area
173- // and returns the number of replacements made and the number of runes
193+ // and returns the number of replacements made and the number of characters
174194// added or removed on the last line of the range
175195func (b * Buffer ) ReplaceRegex (start , end Loc , search * regexp.Regexp , replace []byte , captureGroups bool ) (int , int ) {
176196 if start .GreaterThan (end ) {
177197 start , end = end , start
178198 }
179199
180- netrunes := 0
181-
200+ charsEnd := util .CharacterCount (b .LineBytes (end .Y ))
182201 found := 0
183202 var deltas []Delta
203+
184204 for i := start .Y ; i <= end .Y ; i ++ {
185- l := b .lines [i ].data
186- charpos := 0
187-
188- if start .Y == end .Y && i == start .Y {
189- l = util .SliceStart (l , end .X )
190- l = util .SliceEnd (l , start .X )
191- charpos = start .X
192- } else if i == start .Y {
193- l = util .SliceEnd (l , start .X )
194- charpos = start .X
195- } else if i == end .Y {
196- l = util .SliceStart (l , end .X )
197- }
198- newText := search .ReplaceAllFunc (l , func (in []byte ) []byte {
199- var result []byte
200- if captureGroups {
201- for _ , submatches := range search .FindAllSubmatchIndex (in , - 1 ) {
202- result = search .Expand (result , replace , in , submatches )
205+ l := b .LineBytes (i )
206+ charCount := util .CharacterCount (l )
207+ if (i == start .Y && start .X > 0 ) || (i == end .Y && end .X < charCount ) {
208+ // This replacement code works in general, but it creates a separate
209+ // modification for each match. We only use it for the first and last
210+ // lines, which may use padded regexps
211+
212+ from := Loc {0 , i }.Clamp (start , end )
213+ to := Loc {charCount , i }.Clamp (start , end )
214+ matches := b .findAll (search , from , to )
215+ found += len (matches )
216+
217+ for j := len (matches ) - 1 ; j >= 0 ; j -- {
218+ // if we counted upwards, the different deltas would interfere
219+ match := matches [j ]
220+ var newText []byte
221+ if captureGroups {
222+ newText = search .ReplaceAll (b .Substr (match [0 ], match [1 ]), replace )
223+ } else {
224+ newText = replace
203225 }
204- } else {
205- result = replace
206- }
207- found ++
208- if i == end .Y {
209- netrunes += util .CharacterCount (result ) - util .CharacterCount (in )
226+ deltas = append (deltas , Delta {newText , match [0 ], match [1 ]})
210227 }
211- return result
212- })
213-
214- from := Loc {charpos , i }
215- to := Loc {charpos + util .CharacterCount (l ), i }
216-
217- deltas = append (deltas , Delta {newText , from , to })
228+ } else {
229+ newLine := search .ReplaceAllFunc (l , func (in []byte ) []byte {
230+ found ++
231+ var result []byte
232+ if captureGroups {
233+ match := search .FindSubmatchIndex (in )
234+ result = search .Expand (result , replace , in , match )
235+ } else {
236+ result = replace
237+ }
238+ return result
239+ })
240+ deltas = append (deltas , Delta {newLine , Loc {0 , i }, Loc {charCount , i }})
241+ }
218242 }
243+
219244 b .MultipleReplace (deltas )
220245
221- return found , netrunes
246+ return found , util . CharacterCount ( b . LineBytes ( end . Y )) - charsEnd
222247}
0 commit comments