mirror of https://github.com/golang/go.git
internal/lsp/source: parse symbol queries when using fastfuzzy
Hook up the fastfuzzy symbol matcher to our fzf-style query parsing, for consistency with the (slow) fuzzy matcher. In the past I had wanted to implement this natively inside the SymbolMatcher, but it is much simpler to keep using combinators. In the common case we'll just be using fuzzy matching. For golang/go#50016 Change-Id: I1c62c8c8e9d29da570cb1e4034c2b10782529081 Reviewed-on: https://go-review.googlesource.com/c/tools/+/376362 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Peter Weinberger <pjw@google.com>
This commit is contained in:
parent
3737ecd836
commit
21ca3b3a93
|
|
@ -7,6 +7,7 @@ package misc
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
. "golang.org/x/tools/internal/lsp/regtest"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/testenv"
|
||||
|
|
@ -20,7 +21,7 @@ func TestWorkspaceSymbolMissingMetadata(t *testing.T) {
|
|||
-- go.mod --
|
||||
module mod.com
|
||||
|
||||
go 1.12
|
||||
go 1.17
|
||||
-- a.go --
|
||||
package p
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ func TestWorkspaceSymbolSorting(t *testing.T) {
|
|||
-- go.mod --
|
||||
module mod.com
|
||||
|
||||
go 1.12
|
||||
go 1.17
|
||||
-- a/a.go --
|
||||
package a
|
||||
|
||||
|
|
@ -82,15 +83,49 @@ const (
|
|||
"Fooey", // shorter than Fooest, Foobar
|
||||
"Fooest",
|
||||
}
|
||||
syms := env.WorkspaceSymbol("Foo")
|
||||
if len(syms) != len(want) {
|
||||
t.Errorf("got %d symbols, want %d", len(syms), len(want))
|
||||
}
|
||||
|
||||
for i := range syms {
|
||||
if syms[i].Name != want[i] {
|
||||
t.Errorf("syms[%d] = %q, want %q", i, syms[i].Name, want[i])
|
||||
}
|
||||
}
|
||||
got := env.WorkspaceSymbol("Foo")
|
||||
compareSymbols(t, got, want)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceSymbolSpecialPatterns(t *testing.T) {
|
||||
const files = `
|
||||
-- go.mod --
|
||||
module mod.com
|
||||
|
||||
go 1.17
|
||||
-- a/a.go --
|
||||
package a
|
||||
|
||||
const (
|
||||
AxxBxxCxx
|
||||
ABC
|
||||
)
|
||||
`
|
||||
|
||||
var symbolMatcher = string(source.SymbolFastFuzzy)
|
||||
WithOptions(
|
||||
EditorConfig{
|
||||
SymbolMatcher: &symbolMatcher,
|
||||
},
|
||||
).Run(t, files, func(t *testing.T, env *Env) {
|
||||
compareSymbols(t, env.WorkspaceSymbol("ABC"), []string{"ABC", "AxxBxxCxx"})
|
||||
compareSymbols(t, env.WorkspaceSymbol("'ABC"), []string{"ABC"})
|
||||
compareSymbols(t, env.WorkspaceSymbol("^mod.com"), []string{"mod.com/a.ABC", "mod.com/a.AxxBxxCxx"})
|
||||
compareSymbols(t, env.WorkspaceSymbol("^mod.com Axx"), []string{"mod.com/a.AxxBxxCxx"})
|
||||
compareSymbols(t, env.WorkspaceSymbol("C$"), []string{"ABC"})
|
||||
})
|
||||
}
|
||||
|
||||
func compareSymbols(t *testing.T, got []protocol.SymbolInformation, want []string) {
|
||||
t.Helper()
|
||||
if len(got) != len(want) {
|
||||
t.Errorf("got %d symbols, want %d", len(got), len(want))
|
||||
}
|
||||
|
||||
for i := range got {
|
||||
if got[i].Name != want[i] {
|
||||
t.Errorf("got[%d] = %q, want %q", i, got[i].Name, want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,11 +49,6 @@ const (
|
|||
//
|
||||
// Currently this matcher only accepts case-insensitive fuzzy patterns.
|
||||
//
|
||||
// TODO(rfindley):
|
||||
// - implement smart-casing
|
||||
// - implement space-separated groups
|
||||
// - implement ', ^, and $ modifiers
|
||||
//
|
||||
// An empty pattern matches no input.
|
||||
func NewSymbolMatcher(pattern string) *SymbolMatcher {
|
||||
m := &SymbolMatcher{}
|
||||
|
|
|
|||
|
|
@ -177,9 +177,11 @@ func newSymbolCollector(matcher SymbolMatcher, style SymbolStyle, query string)
|
|||
func buildMatcher(matcher SymbolMatcher, query string) matcherFunc {
|
||||
switch matcher {
|
||||
case SymbolFuzzy:
|
||||
return parseQuery(query)
|
||||
return parseQuery(query, newFuzzyMatcher)
|
||||
case SymbolFastFuzzy:
|
||||
return fuzzy.NewSymbolMatcher(query).Match
|
||||
return parseQuery(query, func(query string) matcherFunc {
|
||||
return fuzzy.NewSymbolMatcher(query).Match
|
||||
})
|
||||
case SymbolCaseSensitive:
|
||||
return matchExact(query)
|
||||
case SymbolCaseInsensitive:
|
||||
|
|
@ -195,6 +197,18 @@ func buildMatcher(matcher SymbolMatcher, query string) matcherFunc {
|
|||
panic(fmt.Errorf("unknown symbol matcher: %v", matcher))
|
||||
}
|
||||
|
||||
func newFuzzyMatcher(query string) matcherFunc {
|
||||
fm := fuzzy.NewMatcher(query)
|
||||
return func(chunks []string) (int, float64) {
|
||||
score := float64(fm.ScoreChunks(chunks))
|
||||
ranges := fm.MatchedRanges()
|
||||
if len(ranges) > 0 {
|
||||
return ranges[0], score
|
||||
}
|
||||
return -1, score
|
||||
}
|
||||
}
|
||||
|
||||
// parseQuery parses a field-separated symbol query, extracting the special
|
||||
// characters listed below, and returns a matcherFunc corresponding to the AND
|
||||
// of all field queries.
|
||||
|
|
@ -207,7 +221,7 @@ func buildMatcher(matcher SymbolMatcher, query string) matcherFunc {
|
|||
// In all three of these special queries, matches are 'smart-cased', meaning
|
||||
// they are case sensitive if the symbol query contains any upper-case
|
||||
// characters, and case insensitive otherwise.
|
||||
func parseQuery(q string) matcherFunc {
|
||||
func parseQuery(q string, newMatcher func(string) matcherFunc) matcherFunc {
|
||||
fields := strings.Fields(q)
|
||||
if len(fields) == 0 {
|
||||
return func([]string) (int, float64) { return -1, 0 }
|
||||
|
|
@ -238,15 +252,7 @@ func parseQuery(q string) matcherFunc {
|
|||
return -1, 0
|
||||
})
|
||||
default:
|
||||
fm := fuzzy.NewMatcher(field)
|
||||
f = func(chunks []string) (int, float64) {
|
||||
score := float64(fm.ScoreChunks(chunks))
|
||||
ranges := fm.MatchedRanges()
|
||||
if len(ranges) > 0 {
|
||||
return ranges[0], score
|
||||
}
|
||||
return -1, score
|
||||
}
|
||||
f = newMatcher(field)
|
||||
}
|
||||
funcs = append(funcs, f)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func TestParseQuery(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
matcher := parseQuery(test.query)
|
||||
matcher := parseQuery(test.query, newFuzzyMatcher)
|
||||
if _, score := matcher([]string{test.s}); score > 0 != test.wantMatch {
|
||||
t.Errorf("parseQuery(%q) match for %q: %.2g, want match: %t", test.query, test.s, score, test.wantMatch)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue