Skip to content

Commit 884d09e

Browse files
committed
added more search methods, removed ReplaceRegex
1 parent 4fb2f2d commit 884d09e

File tree

4 files changed

+186
-73
lines changed

4 files changed

+186
-73
lines changed

cmd/micro/initlua.go

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ func luaImportMicroBuffer() *lua.LTable {
141141
ulua.L.SetField(pkg, "ByteOffset", luar.New(ulua.L, buffer.ByteOffset))
142142
ulua.L.SetField(pkg, "Log", luar.New(ulua.L, buffer.WriteLog))
143143
ulua.L.SetField(pkg, "LogBuf", luar.New(ulua.L, buffer.GetLogBuf))
144+
ulua.L.SetField(pkg, "NewRegexpGroup", luar.New(ulua.L, buffer.NewRegexpGroup))
144145

145146
return pkg
146147
}

internal/action/command.go

+18-23
Original file line numberDiff line numberDiff line change
@@ -931,21 +931,11 @@ func (h *BufPane) ReplaceCmd(args []string) {
931931

932932
if noRegex {
933933
search = regexp.QuoteMeta(search)
934+
replaceStr = strings.ReplaceAll(replaceStr, "$", "$$")
934935
}
935936

936-
replace := []byte(replaceStr)
937-
938-
var regex buffer.RegexpGroup
939-
var err error
940937
if h.Buf.Settings["ignorecase"].(bool) {
941-
regex, err = buffer.NewRegexpGroup("(?i)" + search)
942-
} else {
943-
regex, err = buffer.NewRegexpGroup(search)
944-
}
945-
if err != nil {
946-
// There was an error with the user's regex
947-
InfoBar.Error(err)
948-
return
938+
search = "(?i)" + search
949939
}
950940

951941
nreplaced := 0
@@ -959,21 +949,28 @@ func (h *BufPane) ReplaceCmd(args []string) {
959949
searchLoc = start // otherwise me might start at the end
960950
}
961951
if all {
962-
nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace, !noRegex)
952+
var err error
953+
nreplaced, _, err = h.Buf.ReplaceAll(search, start, end, replaceStr)
954+
if err != nil {
955+
InfoBar.Error(err)
956+
return
957+
}
963958
} else {
959+
rgrp, err := buffer.NewRegexpGroup(search)
960+
if err != nil {
961+
InfoBar.Error(err)
962+
return
963+
}
964+
964965
inRange := func(l buffer.Loc) bool {
965966
return l.GreaterEqual(start) && l.LessEqual(end)
966967
}
967968

968969
lastMatchEnd := buffer.Loc{-1, -1}
969970
var doReplacement func()
970971
doReplacement = func() {
971-
locs, found, err := h.Buf.FindNext(search, start, end, searchLoc, true, true)
972-
if err != nil {
973-
InfoBar.Error(err)
974-
return
975-
}
976-
if !found || !inRange(locs[0]) || !inRange(locs[1]) {
972+
locs := h.Buf.FindDown(rgrp, searchLoc, end)
973+
if locs == nil || !inRange(locs[0]) || !inRange(locs[1]) {
977974
h.Cursor.ResetSelection()
978975
h.Buf.RelocateCursors()
979976

@@ -1001,12 +998,10 @@ func (h *BufPane) ReplaceCmd(args []string) {
1001998

1002999
InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) {
10031000
if !canceled && yes {
1004-
_, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace, !noRegex)
1001+
_, searchLoc, _ = h.Buf.ReplaceAll(search, locs[0], locs[1], replaceStr)
10051002

1006-
searchLoc = locs[0]
1007-
searchLoc.X += nrunes + locs[0].Diff(locs[1], h.Buf)
10081003
if end.Y == locs[1].Y {
1009-
end = end.Move(nrunes, h.Buf)
1004+
end = buffer.Loc{end.X + searchLoc.X - locs[1].X, end.Y}
10101005
}
10111006
h.Cursor.Loc = searchLoc
10121007
nreplaced++

internal/buffer/search.go

+165-50
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ const (
2121

2222
// NewRegexpGroup creates a RegexpGroup from a string
2323
func 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

3535
func 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
}

runtime/help/plugins.md

+2
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ The packages and their contents are listed below (in Go type signatures):
339339
- `Log(s string)`: writes a string to the log buffer.
340340
- `LogBuf() *Buffer`: returns the log buffer.
341341

342+
- `NewRegexpGroup`: creates a `RegexpGroup`, which is used for searching
343+
342344
Relevant links:
343345
[Message](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/buffer#Message)
344346
[Loc](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/buffer#Loc)

0 commit comments

Comments
 (0)