internal/lsp/source: support some fzf-like tokens in symbol matching
It's useful to be able to switch between case sensitive, case
insensitive, and fuzzy matching for symbol without having to reload
gopls. FZF has some nice syntax for this:
https://github.com/junegunn/fzf#search-syntax
Adopt a subset of this syntax for our symbol search:
' for exact matching
^ for prefix matching
$ for suffix matching
It would be straightforward to also support inversion, using
'!', but I deemed this unnecessary.
I think we should adopt this, since none of these symbols conflicts with
Go identifiers, or (AFAIK) with special syntax in major LSP clients.
Change-Id: If2e4d372d4a45ace5ab5d4e76c460f1dcca0bc2b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/248418
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/source/workspace_symbol_test.go b/internal/lsp/source/workspace_symbol_test.go
index 0d3a9cd..f3d9dbb 100644
--- a/internal/lsp/source/workspace_symbol_test.go
+++ b/internal/lsp/source/workspace_symbol_test.go
@@ -9,6 +9,43 @@
"testing"
)
+func TestParseQuery(t *testing.T) {
+ tests := []struct {
+ query, s string
+ wantMatch bool
+ }{
+ {"", "anything", false},
+ {"any", "anything", true},
+ {"any$", "anything", false},
+ {"ing$", "anything", true},
+ {"ing$", "anythinG", true},
+ {"inG$", "anything", false},
+ {"^any", "anything", true},
+ {"^any", "Anything", true},
+ {"^Any", "anything", false},
+ {"at", "anything", true},
+ // TODO: this appears to be a bug in the fuzzy matching algorithm. 'At'
+ // should cause a case-sensitive match.
+ // {"At", "anything", false},
+ {"At", "Anything", true},
+ {"'yth", "Anything", true},
+ {"'yti", "Anything", false},
+ {"'any 'thing", "Anything", true},
+ {"anythn nythg", "Anything", true},
+ {"ntx", "Anything", false},
+ {"anythn", "anything", true},
+ {"ing", "anything", true},
+ {"anythn nythgx", "anything", false},
+ }
+
+ for _, test := range tests {
+ matcher := parseQuery(test.query)
+ if score := matcher(test.s); score > 0 != test.wantMatch {
+ t.Errorf("parseQuery(%q) match for %q: %.2g, want match: %t", test.query, test.s, score, test.wantMatch)
+ }
+ }
+}
+
func TestBestMatch(t *testing.T) {
tests := []struct {
desc string