-
Notifications
You must be signed in to change notification settings - Fork 5
/
matcher.go
123 lines (101 loc) · 2.12 KB
/
matcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package main
import (
"fmt"
"regexp"
"sort"
"github.com/abhinav/tmux-fastcopy/internal/fastcopy"
)
type matcher []*regexpMatcher
func (rms matcher) Match(s string) []fastcopy.Match {
var ms []match
for _, m := range rms {
ms = m.AppendMatches(s, ms)
}
ms = rms.removeOverlaps(ms)
rs := make([]fastcopy.Match, len(ms))
for i, m := range ms {
rs[i] = fastcopy.Match{
Matcher: m.Matcher,
Range: m.Sel,
}
}
return rs
}
func (rms matcher) removeOverlaps(ms []match) []match {
if len(ms) < 2 {
return ms
}
// Sort in ascending order by:
// - Starts earliest
// - Runs longest
sort.Slice(ms, func(i, j int) bool {
l, r := ms[i].Full, ms[j].Full
if l.Start < r.Start {
return true
}
if l.Start > r.Start {
return false
}
return l.Len() > r.Len()
})
out := ms[:1]
for _, m := range ms[1:] {
if m.Full.Start < out[len(out)-1].Full.End {
continue
}
out = append(out, m)
}
return out
}
type regexpMatcher struct {
name string
regex *regexp.Regexp
subexp int
}
// compileRegexpMatcher builds a regexpMatcher with the provided name and
// regular expression.
func compileRegexpMatcher(name, s string) (*regexpMatcher, error) {
if len(s) == 0 {
return ®expMatcher{name: name}, nil
}
re, err := regexp.Compile(s)
if err != nil {
return nil, err
}
n := 0
if re.NumSubexp() > 0 {
n++
}
return ®expMatcher{
regex: re,
subexp: n,
name: name,
}, nil
}
func (rm *regexpMatcher) Name() string {
return rm.name
}
func (rm *regexpMatcher) String() string {
return fmt.Sprintf("%v:%v", rm.name, rm.regex)
}
type match struct {
// Name of the matcher that found this match.
Matcher string
// Full matched area.
Full fastcopy.Range
// Selected portion that will be copied.
Sel fastcopy.Range
}
func (rm *regexpMatcher) AppendMatches(s string, ms []match) []match {
if rm.regex == nil {
return ms
}
for _, m := range rm.regex.FindAllStringSubmatchIndex(s, -1) {
ms = append(ms, match{
Matcher: rm.Name(),
Full: fastcopy.Range{Start: m[0], End: m[1]},
Sel: fastcopy.Range{Start: m[2*rm.subexp], End: m[2*rm.subexp+1]},
})
}
return ms
}