fix fuzzy matching not working properly (#2321)
* fix fuzzy matching not working properly * delete continuousRangesmine
parent
e94f991ad4
commit
d0ec55737a
|
|
@ -96,3 +96,17 @@ func serviceAccountMatches(podSA, saName string) bool {
|
|||
}
|
||||
return podSA == saName
|
||||
}
|
||||
|
||||
// ContinuousRanges takes a sorted slice of integers and returns a slice of
|
||||
// sub-slices representing continuous ranges of integers.
|
||||
func ContinuousRanges(indexes []int) [][]int {
|
||||
var ranges [][]int
|
||||
for i, p := 1, 0; i <= len(indexes); i++ {
|
||||
if i == len(indexes) || indexes[i]-indexes[p] != i-p {
|
||||
ranges = append(ranges, []int{indexes[p], indexes[i-1] + 1})
|
||||
p = i
|
||||
}
|
||||
}
|
||||
|
||||
return ranges
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,3 +60,35 @@ func TestServiceAccountMatches(t *testing.T) {
|
|||
assert.Equal(t, u.expect, serviceAccountMatches(u.podTemplate.ServiceAccountName, u.saName))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContinuousRanges(t *testing.T) {
|
||||
tests := []struct {
|
||||
Indexes []int
|
||||
Ranges [][]int
|
||||
}{
|
||||
{
|
||||
Indexes: []int{0},
|
||||
Ranges: [][]int{{0, 1}},
|
||||
},
|
||||
{
|
||||
Indexes: []int{1},
|
||||
Ranges: [][]int{{1, 2}},
|
||||
},
|
||||
{
|
||||
Indexes: []int{0, 1, 2},
|
||||
Ranges: [][]int{{0, 3}},
|
||||
},
|
||||
{
|
||||
Indexes: []int{4, 5, 6},
|
||||
Ranges: [][]int{{4, 7}},
|
||||
},
|
||||
{
|
||||
Indexes: []int{0, 2, 4, 5, 6},
|
||||
Ranges: [][]int{{0, 1}, {2, 3}, {4, 7}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(t, tt.Ranges, ContinuousRanges(tt.Indexes))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
|
@ -69,28 +68,13 @@ func (d *Describe) filter(q string, lines []string) fuzzy.Matches {
|
|||
if dao.IsFuzzySelector(q) {
|
||||
return d.fuzzyFilter(strings.TrimSpace(q[2:]), lines)
|
||||
}
|
||||
return d.rxFilter(q, lines)
|
||||
return rxFilter(q, lines)
|
||||
}
|
||||
|
||||
func (*Describe) fuzzyFilter(q string, lines []string) fuzzy.Matches {
|
||||
return fuzzy.Find(q, lines)
|
||||
}
|
||||
|
||||
func (*Describe) rxFilter(q string, lines []string) fuzzy.Matches {
|
||||
rx, err := regexp.Compile(`(?i)` + q)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
matches := make(fuzzy.Matches, 0, len(lines))
|
||||
for i, l := range lines {
|
||||
if loc := rx.FindStringIndex(l); len(loc) == 2 {
|
||||
matches = append(matches, fuzzy.Match{Str: q, Index: i, MatchedIndexes: loc})
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
func (d *Describe) fireResourceChanged(lines []string, matches fuzzy.Matches) {
|
||||
for _, l := range d.listeners {
|
||||
l.ResourceChanged(lines, matches)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ package model
|
|||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/derailed/tview"
|
||||
runewidth "github.com/mattn/go-runewidth"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/sahilm/fuzzy"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
|
|
@ -37,3 +39,25 @@ func NewExpBackOff(ctx context.Context, start, max time.Duration) backoff.BackOf
|
|||
bf.InitialInterval, bf.MaxElapsedTime = start, max
|
||||
return backoff.WithContext(bf, ctx)
|
||||
}
|
||||
|
||||
func rxFilter(q string, lines []string) fuzzy.Matches {
|
||||
rx, err := regexp.Compile(`(?i)` + q)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
matches := make(fuzzy.Matches, 0, len(lines))
|
||||
for i, l := range lines {
|
||||
locs := rx.FindAllStringIndex(l, -1)
|
||||
for _, loc := range locs {
|
||||
indexes := make([]int, 0, loc[1]-loc[0])
|
||||
for v := loc[0]; v < loc[1]; v++ {
|
||||
indexes = append(indexes, v)
|
||||
}
|
||||
|
||||
matches = append(matches, fuzzy.Match{Str: q, Index: i, MatchedIndexes: indexes})
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/sahilm/fuzzy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestYAML_rxFilter(t *testing.T) {
|
||||
func Test_rxFilter(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
q string
|
||||
lines []string
|
||||
|
|
@ -32,7 +31,7 @@ func TestYAML_rxFilter(t *testing.T) {
|
|||
{
|
||||
Str: "foo",
|
||||
Index: 0,
|
||||
MatchedIndexes: []int{0, 3},
|
||||
MatchedIndexes: []int{0, 1, 2},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -43,26 +42,25 @@ func TestYAML_rxFilter(t *testing.T) {
|
|||
{
|
||||
Str: "foo",
|
||||
Index: 0,
|
||||
MatchedIndexes: []int{0, 3},
|
||||
MatchedIndexes: []int{0, 1, 2},
|
||||
},
|
||||
{
|
||||
Str: "foo",
|
||||
Index: 2,
|
||||
MatchedIndexes: []int{0, 3},
|
||||
MatchedIndexes: []int{0, 1, 2},
|
||||
},
|
||||
{
|
||||
Str: "foo",
|
||||
Index: 2,
|
||||
MatchedIndexes: []int{8, 11},
|
||||
MatchedIndexes: []int{8, 9, 10},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var y YAML
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, y.rxFilter(u.q, u.lines))
|
||||
assert.Equal(t, u.e, rxFilter(u.q, u.lines))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
|
|
@ -115,24 +114,9 @@ func (t *Text) filter(q string, lines []string) fuzzy.Matches {
|
|||
if dao.IsFuzzySelector(q) {
|
||||
return t.fuzzyFilter(strings.TrimSpace(q[2:]), lines)
|
||||
}
|
||||
return t.rxFilter(q, lines)
|
||||
return rxFilter(q, lines)
|
||||
}
|
||||
|
||||
func (*Text) fuzzyFilter(q string, lines []string) fuzzy.Matches {
|
||||
return fuzzy.Find(q, lines)
|
||||
}
|
||||
|
||||
func (*Text) rxFilter(q string, lines []string) fuzzy.Matches {
|
||||
rx, err := regexp.Compile(`(?i)` + q)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
matches := make(fuzzy.Matches, 0, len(lines))
|
||||
for i, l := range lines {
|
||||
if loc := rx.FindStringIndex(l); len(loc) == 2 {
|
||||
matches = append(matches, fuzzy.Match{Str: q, Index: i, MatchedIndexes: loc})
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ func (t *Tree) reconcile(ctx context.Context) error {
|
|||
|
||||
root.Sort()
|
||||
if t.query != "" {
|
||||
t.root = root.Filter(t.query, rxFilter)
|
||||
t.root = root.Filter(t.query, rxMatch)
|
||||
}
|
||||
if t.root == nil || t.root.Diff(root) {
|
||||
t.root = root
|
||||
|
|
@ -277,7 +277,7 @@ func (t *Tree) getMeta(ctx context.Context, gvr string) (ResourceMeta, error) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func rxFilter(q, path string) bool {
|
||||
func rxMatch(q, path string) bool {
|
||||
rx := regexp.MustCompile(`(?i)` + q)
|
||||
|
||||
tokens := strings.Split(path, "::")
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ package model
|
|||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
|
@ -93,28 +92,13 @@ func (v *Values) filter(q string, lines []string) fuzzy.Matches {
|
|||
if dao.IsFuzzySelector(q) {
|
||||
return v.fuzzyFilter(strings.TrimSpace(q[2:]), lines)
|
||||
}
|
||||
return v.rxFilter(q, lines)
|
||||
return rxFilter(q, lines)
|
||||
}
|
||||
|
||||
func (*Values) fuzzyFilter(q string, lines []string) fuzzy.Matches {
|
||||
return fuzzy.Find(q, lines)
|
||||
}
|
||||
|
||||
func (*Values) rxFilter(q string, lines []string) fuzzy.Matches {
|
||||
rx, err := regexp.Compile(`(?i)` + q)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
matches := make(fuzzy.Matches, 0, len(lines))
|
||||
for i, l := range lines {
|
||||
if loc := rx.FindStringIndex(l); len(loc) == 2 {
|
||||
matches = append(matches, fuzzy.Match{Str: q, Index: i, MatchedIndexes: loc})
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
func (v *Values) fireResourceChanged(lines []string, matches fuzzy.Matches) {
|
||||
for _, l := range v.listeners {
|
||||
l.ResourceChanged(lines, matches)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
|
@ -78,28 +77,13 @@ func (y *YAML) filter(q string, lines []string) fuzzy.Matches {
|
|||
if dao.IsFuzzySelector(q) {
|
||||
return y.fuzzyFilter(strings.TrimSpace(q[2:]), lines)
|
||||
}
|
||||
return y.rxFilter(q, lines)
|
||||
return rxFilter(q, lines)
|
||||
}
|
||||
|
||||
func (*YAML) fuzzyFilter(q string, lines []string) fuzzy.Matches {
|
||||
return fuzzy.Find(q, lines)
|
||||
}
|
||||
|
||||
func (*YAML) rxFilter(q string, lines []string) fuzzy.Matches {
|
||||
rx, err := regexp.Compile(`(?i)` + q)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
matches := make(fuzzy.Matches, 0, len(lines))
|
||||
for i, l := range lines {
|
||||
locs := rx.FindAllStringIndex(l, -1)
|
||||
for _, loc := range locs {
|
||||
matches = append(matches, fuzzy.Match{Str: q, Index: i, MatchedIndexes: loc})
|
||||
}
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
func (y *YAML) fireResourceChanged(lines []string, matches fuzzy.Matches) {
|
||||
for _, l := range y.listeners {
|
||||
l.ResourceChanged(lines, matches)
|
||||
|
|
|
|||
|
|
@ -102,19 +102,12 @@ func (d *Details) TextChanged(lines []string) {
|
|||
|
||||
// TextFiltered notifies when the filter changed.
|
||||
func (d *Details) TextFiltered(lines []string, matches fuzzy.Matches) {
|
||||
d.currentRegion, d.maxRegions = 0, 0
|
||||
|
||||
ll := make([]string, len(lines))
|
||||
copy(ll, lines)
|
||||
for _, m := range matches {
|
||||
loc, line := m.MatchedIndexes, ll[m.Index]
|
||||
ll[m.Index] = line[:loc[0]] + fmt.Sprintf(`<<<"search_%d">>>`, d.maxRegions) + line[loc[0]:loc[1]] + `<<<"">>>` + line[loc[1]:]
|
||||
d.maxRegions++
|
||||
}
|
||||
d.currentRegion, d.maxRegions = 0, len(matches)
|
||||
ll := linesWithRegions(lines, matches)
|
||||
|
||||
d.text.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(ll, "\n")))
|
||||
d.text.Highlight()
|
||||
if d.maxRegions > 0 {
|
||||
if len(matches) > 0 {
|
||||
d.text.Highlight("search_0")
|
||||
d.text.ScrollToHighlight()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,14 @@ import (
|
|||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sahilm/fuzzy"
|
||||
)
|
||||
|
||||
func clipboardWrite(text string) error {
|
||||
|
|
@ -240,3 +242,22 @@ func decorateCpuMemHeaderRows(app *App, data *render.TableData) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func matchTag(i int, s string) string {
|
||||
return `<<<"search_` + strconv.Itoa(i) + `">>>` + s + `<<<"">>>`
|
||||
}
|
||||
|
||||
func linesWithRegions(lines []string, matches fuzzy.Matches) []string {
|
||||
ll := make([]string, len(lines))
|
||||
copy(ll, lines)
|
||||
offsetForLine := make(map[int]int)
|
||||
for i, m := range matches {
|
||||
for _, loc := range dao.ContinuousRanges(m.MatchedIndexes) {
|
||||
start, end := loc[0]+offsetForLine[m.Index], loc[1]+offsetForLine[m.Index]
|
||||
regionStr := matchTag(i, ll[m.Index][start:end])
|
||||
ll[m.Index] = ll[m.Index][:start] + regionStr + ll[m.Index][end:]
|
||||
offsetForLine[m.Index] += len(regionStr) - (end - start)
|
||||
}
|
||||
}
|
||||
return ll
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/sahilm/fuzzy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
|
@ -266,3 +267,61 @@ func TestContainerID(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_linesWithRegions(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
lines []string
|
||||
matches fuzzy.Matches
|
||||
e []string
|
||||
}{
|
||||
"empty-lines": {
|
||||
e: []string{},
|
||||
},
|
||||
"no-match": {
|
||||
lines: []string{"bar"},
|
||||
e: []string{"bar"},
|
||||
},
|
||||
"single-match": {
|
||||
lines: []string{"foo", "bar", "baz"},
|
||||
matches: fuzzy.Matches{
|
||||
{Index: 1, MatchedIndexes: []int{0, 1, 2}},
|
||||
},
|
||||
e: []string{"foo", matchTag(0, "bar"), "baz"},
|
||||
},
|
||||
"single-character": {
|
||||
lines: []string{"foo", "bar", "baz"},
|
||||
matches: fuzzy.Matches{
|
||||
{Index: 1, MatchedIndexes: []int{1}},
|
||||
},
|
||||
e: []string{"foo", "b" + matchTag(0, "a") + "r", "baz"},
|
||||
},
|
||||
"multiple-matches": {
|
||||
lines: []string{"foo", "bar", "baz"},
|
||||
matches: fuzzy.Matches{
|
||||
{Index: 1, MatchedIndexes: []int{0, 1, 2}},
|
||||
{Index: 2, MatchedIndexes: []int{0, 1, 2}},
|
||||
},
|
||||
e: []string{"foo", matchTag(0, "bar"), matchTag(1, "baz")},
|
||||
},
|
||||
"multiple-matches-same-line": {
|
||||
lines: []string{"foosfoo baz", "dfbarfoos bar"},
|
||||
matches: fuzzy.Matches{
|
||||
{Index: 0, MatchedIndexes: []int{0, 1, 2}},
|
||||
{Index: 0, MatchedIndexes: []int{4, 5, 6}},
|
||||
{Index: 1, MatchedIndexes: []int{5, 6, 7}},
|
||||
},
|
||||
e: []string{
|
||||
matchTag(0, "foo") + "s" + matchTag(1, "foo") + " baz",
|
||||
"dfbar" + matchTag(2, "foo") + "s bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, u.e, linesWithRegions(u.lines, u.matches))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,36 +100,17 @@ func (v *LiveView) ResourceFailed(err error) {
|
|||
v.text.SetText(cowTalk(err.Error(), x+w))
|
||||
}
|
||||
|
||||
func (*LiveView) linesWithRegions(lines []string, matches fuzzy.Matches) []string {
|
||||
ll := make([]string, len(lines))
|
||||
copy(ll, lines)
|
||||
offsetForLine := make(map[int]int)
|
||||
for i, m := range matches {
|
||||
loc, line := m.MatchedIndexes, ll[m.Index]
|
||||
if len(loc) < 2 {
|
||||
continue
|
||||
}
|
||||
offset := offsetForLine[m.Index]
|
||||
|
||||
loc[0], loc[1] = loc[0]+offset, loc[1]+offset
|
||||
regionStr := `<<<"search_` + strconv.Itoa(i) + `">>>` + line[loc[0]:loc[1]] + `<<<"">>>`
|
||||
ll[m.Index] = line[:loc[0]] + regionStr + line[loc[1]:]
|
||||
offsetForLine[m.Index] += len(regionStr) - (loc[1] - loc[0])
|
||||
}
|
||||
return ll
|
||||
}
|
||||
|
||||
// ResourceChanged notifies when the filter changes.
|
||||
func (v *LiveView) ResourceChanged(lines []string, matches fuzzy.Matches) {
|
||||
v.app.QueueUpdateDraw(func() {
|
||||
v.text.SetTextAlign(tview.AlignLeft)
|
||||
v.maxRegions = len(matches)
|
||||
v.currentRegion, v.maxRegions = 0, len(matches)
|
||||
|
||||
if v.text.GetText(true) == "" {
|
||||
v.text.ScrollToBeginning()
|
||||
}
|
||||
|
||||
lines = v.linesWithRegions(lines, matches)
|
||||
lines = linesWithRegions(lines, matches)
|
||||
v.text.SetText(colorizeYAML(v.app.Styles.Views().Yaml, strings.Join(lines, "\n")))
|
||||
v.text.Highlight()
|
||||
if v.currentRegion < v.maxRegions {
|
||||
|
|
|
|||
|
|
@ -5,18 +5,12 @@ package view
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/sahilm/fuzzy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func matchTag(i int, s string) string {
|
||||
return `<<<"search_` + strconv.Itoa(i) + `">>>` + s + `<<<"">>>`
|
||||
}
|
||||
|
||||
func TestLiveViewSetText(t *testing.T) {
|
||||
s := `
|
||||
apiVersion: v1
|
||||
|
|
@ -30,54 +24,3 @@ apiVersion: v1
|
|||
|
||||
assert.Equal(t, s, sanitizeEsc(v.text.GetText(true)))
|
||||
}
|
||||
|
||||
func TestLiveView_linesWithRegions(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
lines []string
|
||||
matches fuzzy.Matches
|
||||
e []string
|
||||
}{
|
||||
"empty-lines": {
|
||||
e: []string{},
|
||||
},
|
||||
"no-match": {
|
||||
lines: []string{"bar"},
|
||||
e: []string{"bar"},
|
||||
},
|
||||
"single-match": {
|
||||
lines: []string{"foo", "bar", "baz"},
|
||||
matches: fuzzy.Matches{
|
||||
{Index: 1, MatchedIndexes: []int{0, 3}},
|
||||
},
|
||||
e: []string{"foo", matchTag(0, "bar"), "baz"},
|
||||
},
|
||||
"multiple-matches": {
|
||||
lines: []string{"foo", "bar", "baz"},
|
||||
matches: fuzzy.Matches{
|
||||
{Index: 1, MatchedIndexes: []int{0, 3}},
|
||||
{Index: 2, MatchedIndexes: []int{0, 3}},
|
||||
},
|
||||
e: []string{"foo", matchTag(0, "bar"), matchTag(1, "baz")},
|
||||
},
|
||||
"multiple-matches-same-line": {
|
||||
lines: []string{"foosfoo baz", "dfbarfoos bar"},
|
||||
matches: fuzzy.Matches{
|
||||
{Index: 0, MatchedIndexes: []int{0, 3}},
|
||||
{Index: 0, MatchedIndexes: []int{4, 7}},
|
||||
{Index: 1, MatchedIndexes: []int{5, 8}},
|
||||
},
|
||||
e: []string{
|
||||
matchTag(0, "foo") + "s" + matchTag(1, "foo") + " baz",
|
||||
"dfbar" + matchTag(2, "foo") + "s bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
var v LiveView
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, u.e, v.linesWithRegions(u.lines, u.matches))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue