@@ -122,6 +122,26 @@ func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
122122 return [2 ]Loc {}, false
123123}
124124
125+ func (b * Buffer ) findAll (r * regexp.Regexp , start , end Loc ) [][2 ]Loc {
126+ matches := [][2 ]Loc {}
127+ loc := start
128+ for {
129+ match , found := b .findDown (r , loc , end )
130+ if ! found {
131+ break
132+ }
133+ matches = append (matches , match )
134+ if match [0 ] != match [1 ] {
135+ loc = match [1 ]
136+ } else if match [1 ] != end {
137+ loc = match [1 ].Move (1 , b )
138+ } else {
139+ break
140+ }
141+ }
142+ return matches
143+ }
144+
125145// FindNext finds the next occurrence of a given string in the buffer
126146// It returns the start and end location of the match (if found) and
127147// a boolean indicating if it was found
@@ -165,53 +185,73 @@ func (b *Buffer) FindNext(s string, start, end, from Loc, down bool, useRegex bo
165185}
166186
167187// ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area
168- // and returns the number of replacements made and the number of runes
188+ // and returns the number of replacements made and the number of characters
169189// added or removed on the last line of the range
170190func (b * Buffer ) ReplaceRegex (start , end Loc , search * regexp.Regexp , replace []byte , captureGroups bool ) (int , int ) {
171191 if start .GreaterThan (end ) {
172192 start , end = end , start
173193 }
174194
175- netrunes := 0
176-
195+ charsEnd := util .CharacterCount (b .LineBytes (end .Y ))
177196 found := 0
178197 var deltas []Delta
179- for i := start .Y ; i <= end .Y ; i ++ {
180- l := b .lines [i ].data
181- charpos := 0
182-
183- if start .Y == end .Y && i == start .Y {
184- l = util .SliceStart (l , end .X )
185- l = util .SliceEnd (l , start .X )
186- charpos = start .X
187- } else if i == start .Y {
188- l = util .SliceEnd (l , start .X )
189- charpos = start .X
190- } else if i == end .Y {
191- l = util .SliceStart (l , end .X )
192- }
193- newText := search .ReplaceAllFunc (l , func (in []byte ) []byte {
194- var result []byte
198+
199+ // This replacement function works in general, but it creates a separate
200+ // modification for each match. We only use it for the first and last lines,
201+ // which may use padded regexps
202+ replaceFirstLast := func (start , end Loc ) []Delta {
203+ matches := b .findAll (search , start , end )
204+ for j := len (matches ) - 1 ; j >= 0 ; j -- {
205+ // if we counted upwards, the different deltas would interfere
206+ match := matches [j ]
207+ var newText []byte
195208 if captureGroups {
196- for _ , submatches := range search .FindAllSubmatchIndex (in , - 1 ) {
197- result = search .Expand (result , replace , in , submatches )
198- }
209+ newText = search .ReplaceAll (b .Substr (match [0 ], match [1 ]), replace )
199210 } else {
200- result = replace
211+ newText = replace
201212 }
202- found ++
203- if i == end .Y {
204- netrunes += util .CharacterCount (result ) - util .CharacterCount (in )
205- }
206- return result
207- })
213+ deltas = append (deltas , Delta {newText , match [0 ], match [1 ]})
214+ }
215+ found += len (matches )
216+ return deltas
217+ }
218+
219+ replaceMiddle := func (in []byte ) []byte {
220+ found ++
221+ var result []byte
222+ if captureGroups {
223+ match := search .FindSubmatchIndex (in )
224+ result = search .Expand (result , replace , in , match )
225+ } else {
226+ result = replace
227+ }
228+ return result
229+ }
208230
209- from := Loc {charpos , i }
210- to := Loc {charpos + util .CharacterCount (l ), i }
231+ // first line (if different from last line)
232+ if start .Y < end .Y {
233+ n := util .CharacterCount (b .LineBytes (start .Y ))
234+ startEnd := Loc {n , start .Y }
235+ deltas = replaceFirstLast (start , startEnd )
236+ }
211237
212- deltas = append (deltas , Delta {newText , from , to })
238+ // middle lines
239+ for i := start .Y + 1 ; i < end .Y ; i ++ {
240+ l := b .LineBytes (i )
241+ n := util .CharacterCount (l )
242+ newLine := search .ReplaceAllFunc (l , replaceMiddle )
243+ deltas = append (deltas , Delta {newLine , Loc {0 , i }, Loc {n , i }})
213244 }
245+
246+ // last line
247+ endStart := Loc {0 , end .Y }
248+ if start .Y == end .Y {
249+ endStart = start
250+ }
251+ deltas = replaceFirstLast (endStart , end )
252+
214253 b .MultipleReplace (deltas )
215254
216- return found , netrunes
255+ deltaEndX := util .CharacterCount (b .LineBytes (end .Y )) - charsEnd
256+ return found , deltaEndX
217257}
0 commit comments