diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 83b58fe..00bacf8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - go: ['1.17'] + go: ['1.19'] steps: - name: Set up Go ${{ matrix.go }} uses: actions/setup-go@v3 diff --git a/example/go.mod b/example/go.mod index 93d66de..b1bdca1 100644 --- a/example/go.mod +++ b/example/go.mod @@ -5,20 +5,21 @@ go 1.17 replace github.com/ktr0731/go-fuzzyfinder => ../ require ( - github.com/ktr0731/go-fuzzyfinder v0.5.1 - github.com/mattn/go-isatty v0.0.14 + github.com/ktr0731/go-fuzzyfinder v0.6.0 + github.com/mattn/go-isatty v0.0.16 github.com/spf13/pflag v1.0.5 ) require ( github.com/gdamore/encoding v1.0.0 // indirect github.com/gdamore/tcell/v2 v2.5.3 // indirect + github.com/ktr0731/go-ansisgr v0.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 // indirect + github.com/nsf/termbox-go v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 // indirect - golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect - golang.org/x/text v0.3.7 // indirect + github.com/rivo/uniseg v0.4.2 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/term v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect ) diff --git a/example/go.sum b/example/go.sum index 4e6e112..5f70409 100644 --- a/example/go.sum +++ b/example/go.sum @@ -6,29 +6,54 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/ktr0731/go-ansisgr v0.1.0 h1:fbuupput8739hQbEmZn1cEKjqQFwtCCZNznnF6ANo5w= +github.com/ktr0731/go-ansisgr v0.1.0/go.mod h1:G9lxwgBwH0iey0Dw5YQd7n6PmQTwTuTM/X5Sgm/UrzE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 h1:Rl8NelBe+n7SuLbJyw13ho7CGWUt2BjGGKIoreCWQ/c= -github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= +github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= +github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= +github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 h1:saXMvIOKvRFwbOMicHXr0B1uwoxq9dGmLe5ExMES6c4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/fuzz_test.go b/fuzz_test.go new file mode 100644 index 0000000..1fd04f4 --- /dev/null +++ b/fuzz_test.go @@ -0,0 +1,32 @@ +package fuzzyfinder_test + +import ( + "errors" + "testing" + + "github.com/gdamore/tcell/v2" + fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" +) + +func FuzzPreviewWindow(f *testing.F) { + slice := []string{"foo"} + + f.Add("Lorem ipsum dolor sit amet, consectetur adipiscing elit") + f.Add("Sed eget dui libero.\nVivamus tempus, magna nec mollis convallis, ipsum justo tincidunt ligula, ut varius est mi id nisl.\nMorbi commodo turpis risus, nec vehicula leo auctor sit amet.\nUt imperdiet suscipit massa ac vehicula.\nInterdum et malesuada fames ac ante ipsum primis in faucibus.\nPraesent ligula orci, facilisis pulvinar varius eget, iaculis in erat.\nProin pellentesque arcu sed nisl consectetur tristique.\nQuisque tempus blandit dignissim.\nPhasellus dignissim sollicitudin mauris, sed gravida arcu luctus tincidunt.\nNunc rhoncus sed eros vel molestie.\nAenean sodales tortor eu libero rutrum, et lobortis orci scelerisque.\nPraesent sollicitudin, nunc ut consequat commodo, risus velit consectetur nibh, quis pretium nunc elit et erat.") + f.Add("foo\x1b[31;1;44;0;90;105;38;5;12;48;5;226;38;2;10;20;30;48;2;200;100;50mbar") + + f.Fuzz(func(t *testing.T, s string) { + finder, term := fuzzyfinder.NewWithMockedTerminal() + events := []tcell.Event{key(input{tcell.KeyEsc, rune(tcell.KeyEsc), tcell.ModNone})} + term.SetEventsV2(events...) + + _, err := finder.Find( + slice, + func(int) string { return slice[0] }, + fuzzyfinder.WithPreviewWindow(func(i, width, height int) string { return s }), + ) + if !errors.Is(err, fuzzyfinder.ErrAbort) { + t.Fatalf("Find must return ErrAbort, but got '%s'", err) + } + }) +} diff --git a/fuzzing_test.go b/fuzzing_test.go index 49b83ee..9495ff7 100644 --- a/fuzzing_test.go +++ b/fuzzing_test.go @@ -1,3 +1,4 @@ +//go:build fuzz // +build fuzz package fuzzyfinder_test diff --git a/fuzzyfinder.go b/fuzzyfinder.go index 3fdb6da..2cc965a 100644 --- a/fuzzyfinder.go +++ b/fuzzyfinder.go @@ -8,7 +8,6 @@ import ( "flag" "fmt" "reflect" - "regexp" "sort" "strings" "sync" @@ -17,6 +16,7 @@ import ( "unicode/utf8" "github.com/gdamore/tcell/v2" + "github.com/ktr0731/go-ansisgr" "github.com/ktr0731/go-fuzzyfinder/matching" runewidth "github.com/mattn/go-runewidth" "github.com/pkg/errors" @@ -24,9 +24,8 @@ import ( var ( // ErrAbort is returned from Find* functions if there are no selections. - ErrAbort = errors.New("abort") - errEntered = errors.New("entered") - colorsRegex = regexp.MustCompile(`\[[0-9;]*m`) + ErrAbort = errors.New("abort") + errEntered = errors.New("entered") ) // Finds the minimum value among the arguments @@ -151,8 +150,7 @@ func (f *finder) _draw() { // prompt line var promptLinePad int - //nolint:staticcheck - for _, r := range []rune(f.opt.promptString) { + for _, r := range f.opt.promptString { style := tcell.StyleDefault. Foreground(tcell.ColorBlue). Background(tcell.ColorDefault) @@ -179,7 +177,7 @@ func (f *finder) _draw() { // Header line if len(f.opt.header) > 0 { w = 0 - for _, r := range []rune(runewidth.Truncate(f.opt.header, maxWidth-2, "..")) { + for _, r := range runewidth.Truncate(f.opt.header, maxWidth-2, "..") { style := tcell.StyleDefault. Foreground(tcell.ColorGreen). Background(tcell.ColorDefault) @@ -278,53 +276,6 @@ func (f *finder) _draw() { } } -func parseColor(rs *[]rune) (tcell.Color, bool) { - // only parses for 16 colors - // convert to string for easier parsing - str := string(*rs) - ansi := colorsRegex.FindStringSubmatch(str) - var bold bool - - if len(ansi) == 0 { - // no color is being passed, return defaults - return tcell.ColorDefault, false - } - - // ANSI color value is being passed - // find if bold is specified - if len(ansi[0]) > 4 && ansi[0][3:5] == ";1" { - bold = true - } - - // find the color value - color := ansi[0][1:3] - - // strip color codes (also strips trailing '[0m') - stripped := colorsRegex.ReplaceAllString(str, "") - *rs = []rune(stripped) - - switch color { - case "30": - return tcell.ColorBlack, bold - case "31": - return tcell.ColorRed, bold - case "32": - return tcell.ColorGreen, bold - case "33": - return tcell.ColorYellow, bold - case "34": - return tcell.ColorBlue, bold - case "35": - return tcell.ColorDarkMagenta, bold - case "36": - return tcell.ColorDarkCyan, bold - case "37": - return tcell.ColorWhite, bold - default: - return tcell.ColorDefault, bold - } -} - func (f *finder) _drawPreview() { if f.opt.previewFunc == nil { return @@ -338,11 +289,7 @@ func (f *finder) _drawPreview() { idx = f.state.matched[f.state.y].Idx } - sp := strings.Split(f.opt.previewFunc(idx, width, height), "\n") - prevLines := make([][]rune, 0, len(sp)) - for _, s := range sp { - prevLines = append(prevLines, []rune(s)) - } + iter := ansisgr.NewIterator(f.opt.previewFunc(idx, width, height)) // top line for i := width / 2; i < width; i++ { @@ -384,6 +331,8 @@ func (f *finder) _drawPreview() { const vline = '│' var wvline = runewidth.RuneWidth(vline) for h := 1; h < height-1; h++ { + // donePreviewLine indicates the preview string of the current line identified by h is already drawn. + var donePreviewLine bool w := width / 2 for i := width / 2; i < width; i++ { switch { @@ -410,20 +359,24 @@ func (f *finder) _drawPreview() { f.term.SetContent(w, h, ' ', nil, style) w++ default: // Preview text - if h-1 >= len(prevLines) { - w++ + if donePreviewLine { continue } - j := i - width/2 - 2 // Two spaces. - l := prevLines[h-1] - // parse colors here and strip color codes from runes - col, isBold := parseColor(&l) - if j >= len(l) { - w++ + + r, rstyle, ok := iter.Next() + if !ok || r == '\n' { + // Consumed all preview characters. + donePreviewLine = true continue } - rw := runewidth.RuneWidth(l[j]) + + rw := runewidth.RuneWidth(r) if w+rw > width-1-2 { + donePreviewLine = true + + // Discard the rest of the current line. + consumeIterator(iter, '\n') + style := tcell.StyleDefault. Foreground(tcell.ColorDefault). Background(tcell.ColorDefault) @@ -435,11 +388,39 @@ func (f *finder) _drawPreview() { continue } - style := tcell.StyleDefault. - Foreground(col). - Background(tcell.ColorDefault). - Bold(isBold) - f.term.SetContent(w, h, l[j], nil, style) + style := tcell.StyleDefault + if color, ok := rstyle.Foreground(); ok { + switch color.Mode() { + case ansisgr.Mode16: + style = style.Foreground(tcell.PaletteColor(color.Value() - 30)) + case ansisgr.Mode256: + style = style.Foreground(tcell.PaletteColor(color.Value())) + case ansisgr.ModeRGB: + r, g, b := color.RGB() + style = style.Foreground(tcell.NewRGBColor(int32(r), int32(g), int32(b))) + } + } + if color, valid := rstyle.Background(); valid { + switch color.Mode() { + case ansisgr.Mode16: + style = style.Background(tcell.PaletteColor(color.Value() - 40)) + case ansisgr.Mode256: + style = style.Background(tcell.PaletteColor(color.Value())) + case ansisgr.ModeRGB: + r, g, b := color.RGB() + style = style.Background(tcell.NewRGBColor(int32(r), int32(g), int32(b))) + } + } + + style = style. + Bold(rstyle.Bold()). + Dim(rstyle.Dim()). + Italic(rstyle.Italic()). + Underline(rstyle.Underline()). + Blink(rstyle.Blink()). + Reverse(rstyle.Reverse()). + StrikeThrough(rstyle.Strikethrough()) + f.term.SetContent(w, h, r, nil, style) w += rw } } @@ -824,3 +805,12 @@ func (f *finder) FindMulti(slice interface{}, itemFunc func(i int) string, opts func isInTesting() bool { return flag.Lookup("test.v") != nil } + +func consumeIterator(iter *ansisgr.Iterator, r rune) { + for { + r, _, ok := iter.Next() + if !ok || r == '\n' { + return + } + } +} diff --git a/fuzzyfinder_test.go b/fuzzyfinder_test.go index 2cdcda1..1b34fd6 100644 --- a/fuzzyfinder_test.go +++ b/fuzzyfinder_test.go @@ -400,6 +400,51 @@ func TestFind_enter(t *testing.T) { } } +func TestFind_WithPreviewWindow(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + previewString string + }{ + "normal": {previewString: "foo"}, + "multiline": {previewString: "foo\nbar"}, + "overflowed line": {previewString: strings.Repeat("foo", 1000)}, + "SGR": {previewString: "a\x1b[1mb\x1b[0;31mc\x1b[0;42md\x1b[0;38;5;139me\x1b[0;48;5;229mf\x1b[0;38;2;10;200;30mg\x1b[0;48;2;255;200;100mh"}, + "SGR with overflowed line": {previewString: "a\x1b[1mb\x1b[0;31mc\x1b[0;42md\x1b[0;38;5;139me\x1b[0;48;5;229mf\x1b[0;38;2;10;200;30mg\x1b[0;48;2;255;200;100mh\x1b[m" + strings.Repeat("foo", 1000)}, + } + + for name, c := range cases { + c := c + + t.Run(name, func(t *testing.T) { + t.Parallel() + + f, term := fuzzyfinder.NewWithMockedTerminal() + events := []tcell.Event{key(input{tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone})} + term.SetEventsV2(events...) + + assertWithGolden(t, func(t *testing.T) string { + _, err := f.Find( + tracks, + func(i int) string { + return tracks[i].Name + }, + fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { + return c.previewString + }), + ) + + if err != nil { + t.Fatalf("Find must not return an error, but got '%s'", err) + } + + res := term.GetResult() + return res + }) + }) + } +} + func TestFind_error(t *testing.T) { t.Parallel() diff --git a/go.mod b/go.mod index 940ce45..11d6817 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,14 @@ require ( github.com/gdamore/tcell/v2 v2.5.3 github.com/google/go-cmp v0.5.9 github.com/google/gofuzz v1.2.0 + github.com/ktr0731/go-ansisgr v0.1.0 github.com/mattn/go-runewidth v0.0.14 - github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 + github.com/nsf/termbox-go v1.1.1 github.com/pkg/errors v0.9.1 + github.com/rivo/uniseg v0.4.2 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/term v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect ) go 1.13 diff --git a/go.sum b/go.sum index 1287928..c2ebe83 100644 --- a/go.sum +++ b/go.sum @@ -6,24 +6,49 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/ktr0731/go-ansisgr v0.1.0 h1:fbuupput8739hQbEmZn1cEKjqQFwtCCZNznnF6ANo5w= +github.com/ktr0731/go-ansisgr v0.1.0/go.mod h1:G9lxwgBwH0iey0Dw5YQd7n6PmQTwTuTM/X5Sgm/UrzE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 h1:Rl8NelBe+n7SuLbJyw13ho7CGWUt2BjGGKIoreCWQ/c= -github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= +github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= +github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= +github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 h1:saXMvIOKvRFwbOMicHXr0B1uwoxq9dGmLe5ExMES6c4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/matching/matching.go b/matching/matching.go index fcd2b33..6a2f253 100644 --- a/matching/matching.go +++ b/matching/matching.go @@ -83,7 +83,7 @@ func match(input string, slice []string, opt opt) (res []Matched) { s = strings.ToLower(s) } LINE_MATCHING: - for _, r := range []rune(s) { //nolint:staticcheck + for _, r := range s { if r == in[idx] { idx++ if idx == len(in) { diff --git a/matching/matching_test.go b/matching/matching_test.go index 679f9dc..f2f5554 100644 --- a/matching/matching_test.go +++ b/matching/matching_test.go @@ -8,6 +8,8 @@ import ( ) func TestMatch(t *testing.T) { + t.Parallel() + cases := map[string]struct { idx int in string @@ -26,6 +28,8 @@ func TestMatch(t *testing.T) { for name, c := range cases { c := c t.Run(name, func(t *testing.T) { + t.Parallel() + var matched []matching.Matched if c.caseSensitive { matched = matching.FindAll(c.in, slice, matching.WithMode(matching.ModeCaseSensitive)) diff --git a/scoring/scoring_test.go b/scoring/scoring_test.go index 3b80f68..c772164 100644 --- a/scoring/scoring_test.go +++ b/scoring/scoring_test.go @@ -3,6 +3,8 @@ package scoring import "testing" func TestCalculate(t *testing.T) { + t.Parallel() + cases := map[string]struct { s1, s2 string willPanic bool @@ -25,6 +27,8 @@ func TestCalculate(t *testing.T) { } func Test_max(t *testing.T) { + t.Parallel() + if n := max(); n != 0 { t.Errorf("max must return 0 if no args, but got %d", n) } diff --git a/scoring/smith_waterman_test.go b/scoring/smith_waterman_test.go index 4a79398..a402363 100644 --- a/scoring/smith_waterman_test.go +++ b/scoring/smith_waterman_test.go @@ -7,6 +7,8 @@ import ( ) func Test_smithWaterman(t *testing.T) { + t.Parallel() + old := os.Getenv("DEBUG") os.Setenv("DEBUG", "true") defer os.Setenv("DEBUG", old) @@ -25,6 +27,8 @@ func Test_smithWaterman(t *testing.T) { c := c name := fmt.Sprintf("%s-%s", c.s1, c.s2) t.Run(name, func(t *testing.T) { + t.Parallel() + score, pos := smithWaterman([]rune(c.s1), []rune(c.s2)) if score != c.expectedScore { t.Errorf("expected 78, but got %d", score) diff --git a/testdata/fixtures/testfind_withpreviewwindow-multiline.golden b/testdata/fixtures/testfind_withpreviewwindow-multiline.golden new file mode 100644 index 0000000..858b5ef --- /dev/null +++ b/testdata/fixtures/testfind_withpreviewwindow-multiline.golden @@ -0,0 +1,11 @@ + ICHIDAIJI ┌────────────────────────────┐ + メーベル │ foo │ + glow │ bar │ + closing │ │ + ソラニン │ │ + adrenaline!!! │ │ + ヒトリノ夜 │ │ +> あの日自分が出て行ってや.. │ │ + 9/9 │ │ +> █ └────────────────────────────┘ + \ No newline at end of file diff --git a/testdata/fixtures/testfind_withpreviewwindow-normal.golden b/testdata/fixtures/testfind_withpreviewwindow-normal.golden new file mode 100644 index 0000000..e591406 --- /dev/null +++ b/testdata/fixtures/testfind_withpreviewwindow-normal.golden @@ -0,0 +1,11 @@ + ICHIDAIJI ┌────────────────────────────┐ + メーベル │ foo │ + glow │ │ + closing │ │ + ソラニン │ │ + adrenaline!!! │ │ + ヒトリノ夜 │ │ +> あの日自分が出て行ってや.. │ │ + 9/9 │ │ +> █ └────────────────────────────┘ + \ No newline at end of file diff --git a/testdata/fixtures/testfind_withpreviewwindow-overflowed_line.golden b/testdata/fixtures/testfind_withpreviewwindow-overflowed_line.golden new file mode 100644 index 0000000..2506f7c --- /dev/null +++ b/testdata/fixtures/testfind_withpreviewwindow-overflowed_line.golden @@ -0,0 +1,11 @@ + ICHIDAIJI ┌────────────────────────────┐ + メーベル │ foofoofoofoofoofoofoofoof..│ + glow │ │ + closing │ │ + ソラニン │ │ + adrenaline!!! │ │ + ヒトリノ夜 │ │ +> あの日自分が出て行ってや.. │ │ + 9/9 │ │ +> █ └────────────────────────────┘ + \ No newline at end of file diff --git a/testdata/fixtures/testfind_withpreviewwindow-sgr.golden b/testdata/fixtures/testfind_withpreviewwindow-sgr.golden new file mode 100644 index 0000000..82c5622 --- /dev/null +++ b/testdata/fixtures/testfind_withpreviewwindow-sgr.golden @@ -0,0 +1,11 @@ + ICHIDAIJI ┌────────────────────────────┐ + メーベル │ abcdefgh │ + glow │ │ + closing │ │ + ソラニン │ │ + adrenaline!!! │ │ + ヒトリノ夜 │ │ +> あの日自分が出て行ってや.. │ │ + 9/9 │ │ +> █ └────────────────────────────┘ + \ No newline at end of file diff --git a/testdata/fixtures/testfind_withpreviewwindow-sgr_with_overflowed_line.golden b/testdata/fixtures/testfind_withpreviewwindow-sgr_with_overflowed_line.golden new file mode 100644 index 0000000..de2d324 --- /dev/null +++ b/testdata/fixtures/testfind_withpreviewwindow-sgr_with_overflowed_line.golden @@ -0,0 +1,11 @@ + ICHIDAIJI ┌────────────────────────────┐ + メーベル │ abcdefghfoofoofoofoofoofo..│ + glow │ │ + closing │ │ + ソラニン │ │ + adrenaline!!! │ │ + ヒトリノ夜 │ │ +> あの日自分が出て行ってや.. │ │ + 9/9 │ │ +> █ └────────────────────────────┘ + \ No newline at end of file