···
41
41
}
42
42
43
43
type candidateMatch struct {
44
44
-
query *SubstringQuery
44
44
+
caseSensitive bool
45
45
+
fileName bool
45
46
46
47
substrBytes []byte
47
48
substrLowered []byte
···
49
50
caseMask [][]byte
50
51
caseBits [][]byte
51
52
52
52
-
file uint32
53
53
-
offset uint32
53
53
+
file uint32
54
54
+
offset uint32
55
55
+
matchSz uint32
54
56
}
55
57
56
58
func (m *candidateMatch) String() string {
···
58
60
}
59
61
60
62
func (m *candidateMatch) caseMatches(fileCaseBits []byte) bool {
61
61
-
if !m.query.CaseSensitive {
63
63
+
if !m.caseSensitive {
62
64
return true
63
65
}
64
66
patLen := len(m.substrBytes)
···
83
85
}
84
86
85
87
func (m *candidateMatch) matchContent(content []byte) bool {
86
86
-
return bytes.Compare(content[m.offset:m.offset+uint32(len(m.substrLowered))], m.substrLowered) == 0
88
88
+
return bytes.Compare(content[m.offset:m.offset+uint32(m.matchSz)], m.substrLowered) == 0
87
89
}
88
90
89
91
func (m *candidateMatch) line(newlines []uint32, fileSize uint32) (lineNum, lineStart, lineEnd int) {
···
141
143
&candidateMatch{
142
144
caseMask: caseMasks,
143
145
caseBits: caseBits,
144
144
-
query: s.query,
146
146
+
caseSensitive: s.query.CaseSensitive,
147
147
+
fileName: s.query.FileName,
145
148
substrBytes: patBytes,
146
149
substrLowered: lowerPatBytes,
150
150
+
matchSz: uint32(len(lowerPatBytes)),
147
151
file: uint32(s.fileIdx),
148
152
offset: p1 - fileStart - s.leftPad,
149
153
})
···
3
3
import (
4
4
"fmt"
5
5
"log"
6
6
+
"regexp"
6
7
)
7
8
8
9
var _ = log.Println
···
10
11
// An expression tree coupled with matches
11
12
type matchTree interface {
12
13
// returns whether this matches, and if we are sure.
13
13
-
matches(known map[matchTree]bool, docID uint32) (match bool, sure bool)
14
14
+
matches(known map[matchTree]bool) (match bool, sure bool)
15
15
+
16
16
+
// clears any per-document state of the matchTree, and prepares for
17
17
+
// evaluating the given doc
18
18
+
prepare(nextDoc uint32)
14
19
String() string
15
20
}
16
21
···
26
31
child matchTree
27
32
}
28
33
34
34
+
type regexpMatchTree struct {
35
35
+
query *RegexpQuery
36
36
+
regexp *regexp.Regexp
37
37
+
child matchTree
38
38
+
39
39
+
// mutable
40
40
+
reEvaluated bool
41
41
+
found []*candidateMatch
42
42
+
}
43
43
+
29
44
type substrMatchTree struct {
30
30
-
query *SubstringQuery
45
45
+
query *SubstringQuery
46
46
+
47
47
+
cands []*candidateMatch
48
48
+
coversContent bool
49
49
+
50
50
+
// mutable
31
51
current []*candidateMatch
32
52
caseEvaluated bool
33
53
contEvaluated bool
34
34
-
cands []*candidateMatch
35
35
-
coversContent bool
36
54
}
37
55
38
56
type branchQueryMatchTree struct {
39
57
fileMasks []uint32
40
58
mask uint32
59
59
+
60
60
+
// mutable
61
61
+
docID uint32
62
62
+
}
63
63
+
64
64
+
// prepare
65
65
+
func (t *andMatchTree) prepare(doc uint32) {
66
66
+
for _, c := range t.children {
67
67
+
c.prepare(doc)
68
68
+
}
69
69
+
}
70
70
+
71
71
+
func (t *regexpMatchTree) prepare(doc uint32) {
72
72
+
t.found = t.found[:0]
73
73
+
t.reEvaluated = false
74
74
+
t.child.prepare(doc)
75
75
+
}
76
76
+
77
77
+
func (t *orMatchTree) prepare(doc uint32) {
78
78
+
for _, c := range t.children {
79
79
+
c.prepare(doc)
80
80
+
}
81
81
+
}
82
82
+
83
83
+
func (t *notMatchTree) prepare(doc uint32) {
84
84
+
t.child.prepare(doc)
41
85
}
42
86
87
87
+
func (t *substrMatchTree) prepare(nextDoc uint32) {
88
88
+
for len(t.cands) > 0 && t.cands[0].file < nextDoc {
89
89
+
t.cands = t.cands[1:]
90
90
+
}
91
91
+
92
92
+
i := 0
93
93
+
for ; i < len(t.cands) && t.cands[i].file == nextDoc; i++ {
94
94
+
}
95
95
+
t.current = t.cands[:i]
96
96
+
t.cands = t.cands[i:]
97
97
+
t.contEvaluated = false
98
98
+
t.caseEvaluated = false
99
99
+
}
100
100
+
101
101
+
func (t *branchQueryMatchTree) prepare(doc uint32) {
102
102
+
t.docID = doc
103
103
+
}
104
104
+
105
105
+
// String.
43
106
func (t *andMatchTree) String() string {
44
107
return fmt.Sprintf("and%v", t.children)
45
108
}
46
109
110
110
+
func (t *regexpMatchTree) String() string {
111
111
+
return fmt.Sprintf("re(%s,%s)", t.regexp, t.child)
112
112
+
}
113
113
+
47
114
func (t *orMatchTree) String() string {
48
48
-
return fmt.Sprintf("and%v", t.children)
115
115
+
return fmt.Sprintf("or%v", t.children)
49
116
}
50
117
51
118
func (t *notMatchTree) String() string {
···
70
137
for _, ch := range s.children {
71
138
collectPositiveSubstrings(ch, f)
72
139
}
140
140
+
case *regexpMatchTree:
141
141
+
collectPositiveSubstrings(s.child, f)
73
142
case *notMatchTree:
74
143
case *substrMatchTree:
75
144
f(s)
76
145
}
77
146
}
78
147
148
148
+
func collectRegexps(t matchTree, f func(*regexpMatchTree)) {
149
149
+
switch s := t.(type) {
150
150
+
case *andMatchTree:
151
151
+
for _, ch := range s.children {
152
152
+
collectRegexps(ch, f)
153
153
+
}
154
154
+
case *orMatchTree:
155
155
+
for _, ch := range s.children {
156
156
+
collectRegexps(ch, f)
157
157
+
}
158
158
+
case *regexpMatchTree:
159
159
+
f(s)
160
160
+
}
161
161
+
}
162
162
+
79
163
func visitMatches(t matchTree, known map[matchTree]bool, f func(matchTree)) {
80
164
switch s := t.(type) {
81
165
case *andMatchTree:
···
106
190
})
107
191
}
108
192
193
193
+
func visitRegexMatches(t matchTree, known map[matchTree]bool, f func(*regexpMatchTree)) {
194
194
+
visitMatches(t, known, func(mt matchTree) {
195
195
+
st, ok := mt.(*regexpMatchTree)
196
196
+
if ok {
197
197
+
f(st)
198
198
+
}
199
199
+
})
200
200
+
}
201
201
+
109
202
func (p *contentProvider) evalContentMatches(s *substrMatchTree) {
110
203
if !s.coversContent {
111
204
pruned := s.current[:0]
···
119
212
s.contEvaluated = true
120
213
}
121
214
215
215
+
func (p *contentProvider) evalRegexpMatches(s *regexpMatchTree) {
216
216
+
idxs := s.regexp.FindAllIndex(p.data(false), -1)
217
217
+
for _, idx := range idxs {
218
218
+
s.found = append(s.found, &candidateMatch{
219
219
+
offset: uint32(idx[0]),
220
220
+
matchSz: uint32(idx[1] - idx[0]),
221
221
+
})
222
222
+
}
223
223
+
s.reEvaluated = true
224
224
+
}
225
225
+
122
226
func (p *contentProvider) evalCaseMatches(s *substrMatchTree) {
123
227
if s.query.CaseSensitive {
124
228
pruned := s.current[:0]
···
132
236
s.caseEvaluated = true
133
237
}
134
238
135
135
-
func (t *andMatchTree) matches(known map[matchTree]bool, docID uint32) (bool, bool) {
239
239
+
func (t *andMatchTree) matches(known map[matchTree]bool) (bool, bool) {
136
240
sure := true
137
241
138
242
for _, ch := range t.children {
139
139
-
v, ok := evalMatchTree(known, ch, docID)
243
243
+
v, ok := evalMatchTree(known, ch)
140
244
if ok && !v {
141
245
return false, true
142
246
}
···
147
251
return true, sure
148
252
}
149
253
150
150
-
func (t *orMatchTree) matches(known map[matchTree]bool, docID uint32) (bool, bool) {
254
254
+
func (t *orMatchTree) matches(known map[matchTree]bool) (bool, bool) {
151
255
sure := true
152
256
for _, ch := range t.children {
153
153
-
v, ok := evalMatchTree(known, ch, docID)
257
257
+
v, ok := evalMatchTree(known, ch)
154
258
if ok {
155
259
if v {
156
260
return true, true
···
162
266
return false, sure
163
267
}
164
268
165
165
-
func (t *branchQueryMatchTree) matches(known map[matchTree]bool, docID uint32) (bool, bool) {
166
166
-
return t.fileMasks[docID]&t.mask != 0, true
269
269
+
func (t *branchQueryMatchTree) matches(known map[matchTree]bool) (bool, bool) {
270
270
+
return t.fileMasks[t.docID]&t.mask != 0, true
167
271
}
168
272
169
169
-
func evalMatchTree(known map[matchTree]bool, mt matchTree, docID uint32) (bool, bool) {
273
273
+
func (t *regexpMatchTree) matches(known map[matchTree]bool) (bool, bool) {
274
274
+
v, ok := evalMatchTree(known, t.child)
275
275
+
if ok && !v {
276
276
+
return false, true
277
277
+
}
278
278
+
279
279
+
if !t.reEvaluated {
280
280
+
return false, false
281
281
+
}
282
282
+
283
283
+
return len(t.found) > 0, true
284
284
+
}
285
285
+
286
286
+
func evalMatchTree(known map[matchTree]bool, mt matchTree) (bool, bool) {
170
287
if v, ok := known[mt]; ok {
171
288
return v, true
172
289
}
173
290
174
174
-
v, ok := mt.matches(known, docID)
291
291
+
v, ok := mt.matches(known)
175
292
if ok {
176
293
known[mt] = v
177
294
}
···
179
296
return v, ok
180
297
}
181
298
182
182
-
func (t *notMatchTree) matches(known map[matchTree]bool, docID uint32) (bool, bool) {
183
183
-
v, ok := evalMatchTree(known, t.child, docID)
299
299
+
func (t *notMatchTree) matches(known map[matchTree]bool) (bool, bool) {
300
300
+
v, ok := evalMatchTree(known, t.child)
184
301
return !v, ok
185
302
}
186
303
187
187
-
func (t *substrMatchTree) matches(known map[matchTree]bool, docID uint32) (bool, bool) {
304
304
+
func (t *substrMatchTree) matches(known map[matchTree]bool) (bool, bool) {
188
305
if len(t.current) == 0 {
189
306
return false, true
190
307
}
···
195
312
196
313
func (d *indexData) newMatchTree(q Query, sq map[*SubstringQuery]*substrMatchTree) (matchTree, error) {
197
314
switch s := q.(type) {
315
315
+
case *RegexpQuery:
316
316
+
subQ := regexpToQuery(s.Regexp)
317
317
+
subMT, err := d.newMatchTree(subQ, sq)
318
318
+
if err != nil {
319
319
+
return nil, err
320
320
+
}
321
321
+
322
322
+
return ®expMatchTree{
323
323
+
regexp: regexp.MustCompile(s.Regexp.String()),
324
324
+
child: subMT,
325
325
+
}, nil
198
326
case *AndQuery:
199
327
var r []matchTree
200
328
for _, ch := range s.Children {
···
260
388
collectPositiveSubstrings(mt, func(sq *substrMatchTree) {
261
389
positiveAtoms = append(positiveAtoms, sq)
262
390
})
391
391
+
392
392
+
var regexpAtoms []*regexpMatchTree
393
393
+
collectRegexps(mt, func(re *regexpMatchTree) {
394
394
+
regexpAtoms = append(regexpAtoms, re)
395
395
+
})
396
396
+
263
397
for _, st := range atoms {
264
398
if st.query.FileName {
265
399
fileAtoms = append(fileAtoms, st)
···
281
415
}
282
416
283
417
res.Stats.FilesConsidered++
284
284
-
for _, st := range atoms {
285
285
-
for len(st.cands) > 0 && st.cands[0].file < nextDoc {
286
286
-
st.cands = st.cands[1:]
287
287
-
}
288
288
-
289
289
-
i := 0
290
290
-
for ; i < len(st.cands) && st.cands[i].file == nextDoc; i++ {
291
291
-
}
292
292
-
st.current = st.cands[:i]
293
293
-
st.cands = st.cands[i:]
294
294
-
st.contEvaluated = false
295
295
-
st.caseEvaluated = false
296
296
-
}
418
418
+
mt.prepare(nextDoc)
297
419
298
420
var fileStart uint32
299
421
if nextDoc > 0 {
···
308
430
}
309
431
310
432
known := make(map[matchTree]bool)
311
311
-
if v, ok := evalMatchTree(known, mt, nextDoc); ok && !v {
433
433
+
if v, ok := evalMatchTree(known, mt); ok && !v {
312
434
continue nextFileMatch
313
435
}
314
436
···
318
440
cp.evalCaseMatches(st)
319
441
cp.evalContentMatches(st)
320
442
}
321
321
-
if v, ok := evalMatchTree(known, mt, nextDoc); ok && !v {
443
443
+
if v, ok := evalMatchTree(known, mt); ok && !v {
322
444
continue nextFileMatch
323
445
}
324
446
}
···
327
449
cp.evalCaseMatches(st)
328
450
}
329
451
330
330
-
if v, ok := evalMatchTree(known, mt, nextDoc); ok && !v {
452
452
+
if v, ok := evalMatchTree(known, mt); ok && !v {
331
453
continue nextFileMatch
332
454
}
333
455
···
336
458
cp.evalContentMatches(st)
337
459
}
338
460
339
339
-
if v, ok := evalMatchTree(known, mt, nextDoc); !ok {
461
461
+
if len(regexpAtoms) > 0 {
462
462
+
if v, ok := evalMatchTree(known, mt); ok && !v {
463
463
+
continue nextFileMatch
464
464
+
}
465
465
+
466
466
+
for _, re := range regexpAtoms {
467
467
+
cp.evalRegexpMatches(re)
468
468
+
}
469
469
+
}
470
470
+
471
471
+
if v, ok := evalMatchTree(known, mt); !ok {
340
472
panic("did not decide")
341
473
} else if !v {
342
474
continue nextFileMatch
···
354
486
visitSubtreeMatches(mt, known, func(s *substrMatchTree) {
355
487
for _, c := range s.current {
356
488
fileMatch.Matches = append(fileMatch.Matches, cp.fillMatch(c))
357
357
-
if !c.query.FileName {
489
489
+
if !c.fileName {
358
490
foundContentMatch = true
359
491
}
492
492
+
}
493
493
+
})
494
494
+
495
495
+
visitRegexMatches(mt, known, func(re *regexpMatchTree) {
496
496
+
for _, c := range re.found {
497
497
+
foundContentMatch = true
498
498
+
fileMatch.Matches = append(fileMatch.Matches, cp.fillMatch(c))
360
499
}
361
500
})
362
501
···
458
458
Pattern: "helpers.go",
459
459
FileName: true,
460
460
})
461
461
+
clearScores(sres)
461
462
462
463
if err != nil {
463
464
t.Fatalf("Search: %v", err)
···
578
579
t.Errorf("got %#v, want no FilesLoaded", sres.Stats)
579
580
}
580
581
}
582
582
+
583
583
+
func TestRegexp(t *testing.T) {
584
584
+
b := NewIndexBuilder()
585
585
+
586
586
+
content := []byte("needle the bla")
587
587
+
// ----------------01234567890123
588
588
+
b.AddFile("f1", content)
589
589
+
590
590
+
searcher := searcherForTest(t, b)
591
591
+
sres, err := searcher.Search(
592
592
+
&RegexpQuery{
593
593
+
mustParseRE("dle.*bla"),
594
594
+
})
595
595
+
596
596
+
if err != nil {
597
597
+
t.Fatalf("Search: %v", err)
598
598
+
}
599
599
+
clearScores(sres)
600
600
+
if len(sres.Files) != 1 || len(sres.Files[0].Matches) != 1 {
601
601
+
t.Fatalf("got %v, want 1 match in 1 file", sres.Files)
602
602
+
}
603
603
+
604
604
+
got := sres.Files[0].Matches[0]
605
605
+
want := Match{
606
606
+
LineOff: 3,
607
607
+
Offset: 3,
608
608
+
MatchLength: 11,
609
609
+
Line: content,
610
610
+
FileName: false,
611
611
+
LineNum: 1,
612
612
+
LineStart: 0,
613
613
+
LineEnd: 14,
614
614
+
}
615
615
+
616
616
+
if !reflect.DeepEqual(got, want) {
617
617
+
t.Errorf("got %#v, want %#v", got, want)
618
618
+
}
619
619
+
}
620
620
+
621
621
+
func TestRegexpOrder(t *testing.T) {
622
622
+
b := NewIndexBuilder()
623
623
+
624
624
+
content := []byte("bla the needle")
625
625
+
// ----------------01234567890123
626
626
+
b.AddFile("f1", content)
627
627
+
628
628
+
searcher := searcherForTest(t, b)
629
629
+
sres, err := searcher.Search(
630
630
+
&RegexpQuery{
631
631
+
mustParseRE("dle.*bla"),
632
632
+
})
633
633
+
634
634
+
if err != nil {
635
635
+
t.Fatalf("Search: %v", err)
636
636
+
}
637
637
+
clearScores(sres)
638
638
+
if len(sres.Files) != 0 {
639
639
+
t.Fatalf("got %v, want 0 matches", sres.Files)
640
640
+
}
641
641
+
}
···
18
18
"bytes"
19
19
"fmt"
20
20
"log"
21
21
+
"regexp/syntax"
21
22
)
22
23
23
24
var _ = log.Printf
···
63
64
var casePrefix = []byte("case:")
64
65
var filePrefix = []byte("file:")
65
66
var branchPrefix = []byte("branch:")
67
67
+
var regexPrefix = []byte("regex:")
66
68
67
69
type setCase string
68
70
···
131
133
return string(arg), n, ok, err
132
134
}
133
135
136
136
+
func tryConsumeRegexp(in []byte) (string, int, bool, error) {
137
137
+
arg, n, ok, err := consumeKeyword(in, regexPrefix)
138
138
+
return string(arg), n, ok, err
139
139
+
}
140
140
+
134
141
func Parse(qStr string) (Query, error) {
135
142
b := []byte(qStr)
136
143
···
180
187
} else if ok {
181
188
add(&BranchQuery{
182
189
Name: fn,
190
190
+
})
191
191
+
b = b[n:]
192
192
+
continue
193
193
+
}
194
194
+
195
195
+
if arg, n, ok, err := tryConsumeRegexp(b); err != nil {
196
196
+
return nil, err
197
197
+
} else if ok {
198
198
+
r, err := syntax.Parse(arg, 0)
199
199
+
if err != nil {
200
200
+
return nil, err
201
201
+
}
202
202
+
203
203
+
add(&RegexpQuery{
204
204
+
Regexp: r,
183
205
})
184
206
b = b[n:]
185
207
continue
···
16
16
17
17
import (
18
18
"reflect"
19
19
+
"regexp/syntax"
19
20
"testing"
20
21
)
22
22
+
23
23
+
func mustParseRE(s string) *syntax.Regexp {
24
24
+
r, err := syntax.Parse(s, 0)
25
25
+
if err != nil {
26
26
+
panic(err)
27
27
+
}
28
28
+
29
29
+
return r
30
30
+
}
21
31
22
32
func TestParseQuery(t *testing.T) {
23
33
type testcase struct {
···
44
54
&SubstringQuery{Pattern: "helpers.go", FileName: true},
45
55
&SubstringQuery{Pattern: "byte"},
46
56
}}, false},
57
57
+
58
58
+
{"regex:abc[p-q]", &RegexpQuery{mustParseRE("abc[p-q]")}, false},
47
59
48
60
// case
49
61
{"abc case:yes", &SubstringQuery{Pattern: "abc", CaseSensitive: true}, false},
···
18
18
"fmt"
19
19
"log"
20
20
"reflect"
21
21
+
"regexp/syntax"
21
22
"strings"
22
23
)
23
24
···
26
27
// Query is a representation for a possibly hierarchical search query.
27
28
type Query interface {
28
29
String() string
30
30
+
}
31
31
+
32
32
+
// RegexpQuery is a query looking for regular expressions matches.
33
33
+
type RegexpQuery struct {
34
34
+
Regexp *syntax.Regexp
35
35
+
}
36
36
+
37
37
+
func (q *RegexpQuery) String() string {
38
38
+
return fmt.Sprintf("regex:%q", q.Regexp.String())
29
39
}
30
40
31
41
// SubstringQuery is the most basic query: a query for a substring.
···
1
1
+
package zoekt
2
2
+
3
3
+
import (
4
4
+
"regexp/syntax"
5
5
+
)
6
6
+
7
7
+
// regexpToQuery tries to distill a substring search query that
8
8
+
// matches a superset of the regexp.
9
9
+
func regexpToQuery(r *syntax.Regexp) Query {
10
10
+
// TODO - we could perhaps transform Begin/EndText in '\n'?
11
11
+
// TODO - we could perhaps transform CharClass in (OrQuery )
12
12
+
// if there are just a few runes, and part of a OpConcat?
13
13
+
switch r.Op {
14
14
+
case syntax.OpLiteral:
15
15
+
s := string(r.Rune)
16
16
+
if len(s) >= ngramSize {
17
17
+
return &SubstringQuery{Pattern: s}
18
18
+
}
19
19
+
case syntax.OpCapture:
20
20
+
return regexpToQuery(r.Sub[0])
21
21
+
22
22
+
case syntax.OpPlus:
23
23
+
return regexpToQuery(r.Sub[0])
24
24
+
25
25
+
case syntax.OpRepeat:
26
26
+
if r.Min >= 1 {
27
27
+
return regexpToQuery(r.Sub[0])
28
28
+
}
29
29
+
30
30
+
case syntax.OpConcat, syntax.OpAlternate:
31
31
+
var qs []Query
32
32
+
for _, sr := range r.Sub {
33
33
+
if sq := regexpToQuery(sr); sq != nil {
34
34
+
qs = append(qs, sq)
35
35
+
}
36
36
+
}
37
37
+
if r.Op == syntax.OpConcat {
38
38
+
return &AndQuery{qs}
39
39
+
}
40
40
+
return &OrQuery{qs}
41
41
+
}
42
42
+
return nil
43
43
+
}
···
1
1
+
package zoekt
2
2
+
3
3
+
import (
4
4
+
"log"
5
5
+
"reflect"
6
6
+
"regexp/syntax"
7
7
+
"strings"
8
8
+
"testing"
9
9
+
)
10
10
+
11
11
+
var opnames = map[syntax.Op]string{
12
12
+
syntax.OpNoMatch: "OpNoMatch",
13
13
+
syntax.OpEmptyMatch: "OpEmptyMatch",
14
14
+
syntax.OpLiteral: "OpLiteral",
15
15
+
syntax.OpCharClass: "OpCharClass",
16
16
+
syntax.OpAnyCharNotNL: "OpAnyCharNotNL",
17
17
+
syntax.OpAnyChar: "OpAnyChar",
18
18
+
syntax.OpBeginLine: "OpBeginLine",
19
19
+
syntax.OpEndLine: "OpEndLine",
20
20
+
syntax.OpBeginText: "OpBeginText",
21
21
+
syntax.OpEndText: "OpEndText",
22
22
+
syntax.OpWordBoundary: "OpWordBoundary",
23
23
+
syntax.OpNoWordBoundary: "OpNoWordBoundary",
24
24
+
syntax.OpCapture: "OpCapture",
25
25
+
syntax.OpStar: "OpStar",
26
26
+
syntax.OpPlus: "OpPlus",
27
27
+
syntax.OpQuest: "OpQuest",
28
28
+
syntax.OpRepeat: "OpRepeat",
29
29
+
syntax.OpConcat: "OpConcat",
30
30
+
syntax.OpAlternate: "OpAlternate",
31
31
+
}
32
32
+
33
33
+
func printRegexp(r *syntax.Regexp, lvl int) {
34
34
+
log.Printf("%s%s ch: %d", strings.Repeat(" ", lvl), opnames[r.Op], len(r.Sub))
35
35
+
for _, s := range r.Sub {
36
36
+
printRegexp(s, lvl+1)
37
37
+
}
38
38
+
}
39
39
+
40
40
+
func TestRegexpParse(t *testing.T) {
41
41
+
type testcase struct {
42
42
+
in string
43
43
+
want Query
44
44
+
}
45
45
+
46
46
+
cases := []testcase{
47
47
+
{"(foo|bar)baz.*bla", &AndQuery{[]Query{
48
48
+
&OrQuery{[]Query{
49
49
+
&SubstringQuery{Pattern: "foo"},
50
50
+
&SubstringQuery{Pattern: "bar"},
51
51
+
}},
52
52
+
&SubstringQuery{Pattern: "baz"},
53
53
+
&SubstringQuery{Pattern: "bla"},
54
54
+
}}},
55
55
+
56
56
+
{"^[a-z](People)+barrabas$",
57
57
+
&AndQuery{[]Query{
58
58
+
&SubstringQuery{Pattern: "People"},
59
59
+
&SubstringQuery{Pattern: "barrabas"},
60
60
+
}}},
61
61
+
}
62
62
+
63
63
+
for _, c := range cases {
64
64
+
r, err := syntax.Parse(c.in, 0)
65
65
+
if err != nil {
66
66
+
t.Errorf("Parse(%q): %v", c.in, err)
67
67
+
continue
68
68
+
}
69
69
+
70
70
+
got := regexpToQuery(r)
71
71
+
if !reflect.DeepEqual(c.want, got) {
72
72
+
t.Errorf("regexpToQuery(%q): got %v, want %v", c.in, got, c.want)
73
73
+
}
74
74
+
}
75
75
+
}
···
64
64
}
65
65
66
66
func (p *contentProvider) caseMatches(m *candidateMatch) bool {
67
67
-
return m.caseMatches(p.caseBits(m.query.FileName))
67
67
+
return m.caseMatches(p.caseBits(m.fileName))
68
68
}
69
69
70
70
func (p *contentProvider) matchContent(m *candidateMatch) bool {
71
71
-
return m.matchContent(p.data(m.query.FileName))
71
71
+
return m.matchContent(p.data(m.fileName))
72
72
}
73
73
74
74
func (p *contentProvider) fillMatch(m *candidateMatch) Match {
75
75
-
if m.query.FileName {
75
75
+
if m.fileName {
76
76
return Match{
77
77
Offset: m.offset,
78
78
Line: p.data(true),
79
79
LineOff: int(m.offset),
80
80
-
MatchLength: len(m.substrBytes),
80
80
+
MatchLength: int(m.matchSz),
81
81
FileName: true,
82
82
}
83
83
}
···
89
89
LineEnd: end,
90
90
LineNum: num,
91
91
LineOff: int(m.offset) - start,
92
92
-
MatchLength: len(m.substrBytes),
92
92
+
MatchLength: int(m.matchSz),
93
93
}
94
94
finalMatch.Line = toOriginal(p.data(false), p.caseBits(false), start, end)
95
95
finalMatch.Score = matchScore(&finalMatch)