From a5be038c9956f4d2020b1a10965f5cf791a81bd2 Mon Sep 17 00:00:00 2001 From: Mohamed Messaad Date: Fri, 17 Mar 2023 16:26:10 +0100 Subject: [PATCH] fix: multiple matches in a single line. (#1876) --- internal/model/yaml.go | 4 +- internal/model/yaml_test.go | 65 +++++++++++++++++++++++++++++++++ internal/view/live_view.go | 30 +++++++++------ internal/view/live_view_test.go | 64 ++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 internal/model/yaml_test.go create mode 100644 internal/view/live_view_test.go diff --git a/internal/model/yaml.go b/internal/model/yaml.go index df4f7b51..e919b26f 100644 --- a/internal/model/yaml.go +++ b/internal/model/yaml.go @@ -84,11 +84,11 @@ func (*YAML) rxFilter(q string, lines []string) fuzzy.Matches { } matches := make(fuzzy.Matches, 0, len(lines)) for i, l := range lines { - if loc := rx.FindStringIndex(l); len(loc) == 2 { + locs := rx.FindAllStringIndex(l, -1) + for _, loc := range locs { matches = append(matches, fuzzy.Match{Str: q, Index: i, MatchedIndexes: loc}) } } - return matches } diff --git a/internal/model/yaml_test.go b/internal/model/yaml_test.go new file mode 100644 index 00000000..9b595134 --- /dev/null +++ b/internal/model/yaml_test.go @@ -0,0 +1,65 @@ +package model + +import ( + "testing" + + "github.com/sahilm/fuzzy" + "github.com/stretchr/testify/assert" +) + +func TestYAML_rxFilter(t *testing.T) { + uu := map[string]struct { + q string + lines []string + e fuzzy.Matches + }{ + "empty-lines": { + q: "foo", + e: fuzzy.Matches{}, + }, + "no-match": { + q: "foo", + lines: []string{"bar"}, + e: fuzzy.Matches{}, + }, + "single-match": { + q: "foo", + lines: []string{"foo", "bar", "baz"}, + e: fuzzy.Matches{ + { + Str: "foo", + Index: 0, + MatchedIndexes: []int{0, 3}, + }, + }, + }, + "multiple-matches": { + q: "foo", + lines: []string{"foo", "bar", "foo bar foo", "baz"}, + e: fuzzy.Matches{ + { + Str: "foo", + Index: 0, + MatchedIndexes: []int{0, 3}, + }, + { + Str: "foo", + Index: 2, + MatchedIndexes: []int{0, 3}, + }, + { + Str: "foo", + Index: 2, + MatchedIndexes: []int{8, 11}, + }, + }, + }, + } + 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)) + }) + } +} diff --git a/internal/view/live_view.go b/internal/view/live_view.go index 35d1730b..aeefd9cb 100644 --- a/internal/view/live_view.go +++ b/internal/view/live_view.go @@ -92,6 +92,21 @@ 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] + 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() { @@ -101,23 +116,14 @@ func (v *LiveView) ResourceChanged(lines []string, matches fuzzy.Matches) { v.text.SetTextAlign(tview.AlignLeft) v.maxRegions = len(matches) - var ll []string - if len(matches) == 0 { - ll = lines - } else { - ll = make([]string, len(lines)) - copy(ll, lines) - for i, m := range matches { - loc, line := m.MatchedIndexes, ll[m.Index] - ll[m.Index] = line[:loc[0]] + `<<<"search_` + strconv.Itoa(i) + `">>>` + line[loc[0]:loc[1]] + `<<<"">>>` + line[loc[1]:] - } - } if v.text.GetText(true) == "" { v.text.ScrollToBeginning() } - v.text.SetText(colorizeYAML(v.app.Styles.Views().Yaml, strings.Join(ll, "\n"))) + lines = v.linesWithRegions(lines, matches) + + v.text.SetText(colorizeYAML(v.app.Styles.Views().Yaml, strings.Join(lines, "\n"))) v.text.Highlight() if v.currentRegion < v.maxRegions { v.text.Highlight("search_" + strconv.Itoa(v.currentRegion)) diff --git a/internal/view/live_view_test.go b/internal/view/live_view_test.go new file mode 100644 index 00000000..d3b801cc --- /dev/null +++ b/internal/view/live_view_test.go @@ -0,0 +1,64 @@ +package view + +import ( + "strconv" + "testing" + + "github.com/sahilm/fuzzy" + "github.com/stretchr/testify/assert" +) + +func matchTag(i int, s string) string { + return `<<<"search_` + strconv.Itoa(i) + `">>>` + s + `<<<"">>>` +} + +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)) + }) + } +}