From ca5866bcf920e9307b6a12ffa893cc7a4b951892 Mon Sep 17 00:00:00 2001 From: Paul Jolly Date: Wed, 8 Apr 2020 22:56:24 +0100 Subject: [PATCH] internal/lsp: add config option for SymbolMatch In preparation for later changes to the workspace Symbol method, we add a separate configuration option keyed by "symbolMatcher" that specifies the type of matcher to use for workspace symbol requests. We also define a new type SymbolMatcher, the type of this new option. We require SymbolMatcher to be a separate type from Matcher because a later CL adds a type of symbol matcher that does not make sense in the context of other uses of Matcher, e.g. completion. Change-Id: Icde7d270b9efb64444f35675a8d0b48ad3b8b3dd Reviewed-on: https://go-review.googlesource.com/c/tools/+/228122 Reviewed-by: Robert Findley --- gopls/doc/settings.md | 20 +++++++++++++++++++ internal/lsp/cmd/cmd.go | 10 +++++----- internal/lsp/cmd/workspace_symbol.go | 6 +++--- internal/lsp/fake/editor.go | 8 ++++++++ internal/lsp/lsp_test.go | 10 +++++----- internal/lsp/source/options.go | 26 +++++++++++++++++++++++++ internal/lsp/source/source_test.go | 8 ++++---- internal/lsp/source/workspace_symbol.go | 14 ++++++------- internal/lsp/workspace_symbol.go | 2 +- 9 files changed, 79 insertions(+), 25 deletions(-) diff --git a/gopls/doc/settings.md b/gopls/doc/settings.md index 6355c5243b..7f0c22e187 100644 --- a/gopls/doc/settings.md +++ b/gopls/doc/settings.md @@ -139,3 +139,23 @@ Default: `true`. ### **staticcheck** *boolean* If true, it enables the use of the staticcheck.io analyzers. + +### **matcher** *string* + +Defines the algorithm that is used when calculating completion candidates. Must be one of: + +* `"fuzzy"` +* `"caseSensitive"` +* `"caseInsensitive"` + +Default: `"caseInsensitive"`. + +### **symbolMatcher** *string* + +Defines the algorithm that is used when calculating workspace symbol results. Must be one of: + +* `"fuzzy"` +* `"caseSensitive"` +* `"caseInsensitive"` + +Default: `"caseInsensitive"`. diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go index 5d4c0e3e04..64011e1a0b 100644 --- a/internal/lsp/cmd/cmd.go +++ b/internal/lsp/cmd/cmd.go @@ -241,10 +241,10 @@ func (app *Application) connectRemote(ctx context.Context, remote string) (*conn return connection, connection.initialize(ctx, app.options) } -var matcherString = map[source.Matcher]string{ - source.Fuzzy: "fuzzy", - source.CaseSensitive: "caseSensitive", - source.CaseInsensitive: "default", +var matcherString = map[source.SymbolMatcher]string{ + source.SymbolFuzzy: "fuzzy", + source.SymbolCaseSensitive: "caseSensitive", + source.SymbolCaseInsensitive: "default", } func (c *connection) initialize(ctx context.Context, options func(*source.Options)) error { @@ -262,7 +262,7 @@ func (c *connection) initialize(ctx context.Context, options func(*source.Option } params.Capabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport = opts.HierarchicalDocumentSymbolSupport params.InitializationOptions = map[string]interface{}{ - "matcher": matcherString[opts.Matcher], + "symbolMatcher": matcherString[opts.SymbolMatcher], } if _, err := c.Server.Initialize(ctx, params); err != nil { return err diff --git a/internal/lsp/cmd/workspace_symbol.go b/internal/lsp/cmd/workspace_symbol.go index f0b2302ee0..b2632622b7 100644 --- a/internal/lsp/cmd/workspace_symbol.go +++ b/internal/lsp/cmd/workspace_symbol.go @@ -47,11 +47,11 @@ func (r *workspaceSymbol) Run(ctx context.Context, args ...string) error { } switch r.Matcher { case "fuzzy": - o.Matcher = source.Fuzzy + o.SymbolMatcher = source.SymbolFuzzy case "caseSensitive": - o.Matcher = source.CaseSensitive + o.SymbolMatcher = source.SymbolCaseSensitive default: - o.Matcher = source.CaseInsensitive + o.SymbolMatcher = source.SymbolCaseInsensitive } } diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go index 8df543be98..9ae5b84666 100644 --- a/internal/lsp/fake/editor.go +++ b/internal/lsp/fake/editor.go @@ -59,6 +59,10 @@ type EditorConfig struct { // codeLens command. CodeLens which are not present in this map are left in // their default state. CodeLens map[string]bool + + // SymbolMatcher is the config associated with the "symbolMatcher" gopls + // config option. + SymbolMatcher *string } // NewEditor Creates a new Editor. @@ -135,6 +139,10 @@ func (e *Editor) configuration() map[string]interface{} { config["codelens"] = e.Config.CodeLens } + if e.Config.SymbolMatcher != nil { + config["symbolMatcher"] = *e.Config.SymbolMatcher + } + return config } diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go index 65527c9cd4..ff421153c1 100644 --- a/internal/lsp/lsp_test.go +++ b/internal/lsp/lsp_test.go @@ -820,23 +820,23 @@ func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol. } func (r *runner) WorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) { - r.callWorkspaceSymbols(t, query, source.CaseInsensitive, dirs, expectedSymbols) + r.callWorkspaceSymbols(t, query, source.SymbolCaseInsensitive, dirs, expectedSymbols) } func (r *runner) FuzzyWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) { - r.callWorkspaceSymbols(t, query, source.Fuzzy, dirs, expectedSymbols) + r.callWorkspaceSymbols(t, query, source.SymbolFuzzy, dirs, expectedSymbols) } func (r *runner) CaseSensitiveWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) { - r.callWorkspaceSymbols(t, query, source.CaseSensitive, dirs, expectedSymbols) + r.callWorkspaceSymbols(t, query, source.SymbolCaseSensitive, dirs, expectedSymbols) } -func (r *runner) callWorkspaceSymbols(t *testing.T, query string, matcher source.Matcher, dirs map[string]struct{}, expectedSymbols []protocol.SymbolInformation) { +func (r *runner) callWorkspaceSymbols(t *testing.T, query string, matcher source.SymbolMatcher, dirs map[string]struct{}, expectedSymbols []protocol.SymbolInformation) { t.Helper() original := r.server.session.Options() modified := original - modified.Matcher = matcher + modified.SymbolMatcher = matcher r.server.session.SetOptions(modified) defer r.server.session.SetOptions(original) diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go index 31f64750d2..363dcc550a 100644 --- a/internal/lsp/source/options.go +++ b/internal/lsp/source/options.go @@ -98,6 +98,7 @@ func DefaultOptions() Options { HoverKind: FullDocumentation, LinkTarget: "pkg.go.dev", Matcher: Fuzzy, + SymbolMatcher: SymbolFuzzy, DeepCompletion: true, UnimportedCompletion: true, CompletionDocumentation: true, @@ -193,6 +194,9 @@ type UserOptions struct { // Matcher specifies the type of matcher to use for completion requests. Matcher Matcher + // SymbolMatcher specifies the type of matcher to use for symbol requests. + SymbolMatcher SymbolMatcher + // DeepCompletion allows completion to perform nested searches through // possible candidates. DeepCompletion bool @@ -267,6 +271,14 @@ const ( CaseSensitive ) +type SymbolMatcher int + +const ( + SymbolFuzzy = SymbolMatcher(iota) + SymbolCaseInsensitive + SymbolCaseSensitive +) + type HoverKind int const ( @@ -399,6 +411,20 @@ func (o *Options) set(name string, value interface{}) OptionResult { o.Matcher = CaseInsensitive } + case "symbolMatcher": + matcher, ok := result.asString() + if !ok { + break + } + switch matcher { + case "fuzzy": + o.SymbolMatcher = SymbolFuzzy + case "caseSensitive": + o.SymbolMatcher = SymbolCaseSensitive + default: + o.SymbolMatcher = SymbolCaseInsensitive + } + case "hoverKind": hoverKind, ok := result.asString() if !ok { diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go index 643e53e96f..219388f156 100644 --- a/internal/lsp/source/source_test.go +++ b/internal/lsp/source/source_test.go @@ -805,18 +805,18 @@ func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol. } func (r *runner) WorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) { - r.callWorkspaceSymbols(t, query, source.CaseInsensitive, dirs, expectedSymbols) + r.callWorkspaceSymbols(t, query, source.SymbolCaseInsensitive, dirs, expectedSymbols) } func (r *runner) FuzzyWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) { - r.callWorkspaceSymbols(t, query, source.Fuzzy, dirs, expectedSymbols) + r.callWorkspaceSymbols(t, query, source.SymbolFuzzy, dirs, expectedSymbols) } func (r *runner) CaseSensitiveWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) { - r.callWorkspaceSymbols(t, query, source.CaseSensitive, dirs, expectedSymbols) + r.callWorkspaceSymbols(t, query, source.SymbolCaseSensitive, dirs, expectedSymbols) } -func (r *runner) callWorkspaceSymbols(t *testing.T, query string, matcher source.Matcher, dirs map[string]struct{}, expectedSymbols []protocol.SymbolInformation) { +func (r *runner) callWorkspaceSymbols(t *testing.T, query string, matcher source.SymbolMatcher, dirs map[string]struct{}, expectedSymbols []protocol.SymbolInformation) { t.Helper() got, err := source.WorkspaceSymbols(r.ctx, matcher, []source.View{r.view}, query) if err != nil { diff --git a/internal/lsp/source/workspace_symbol.go b/internal/lsp/source/workspace_symbol.go index cbbb29a6ec..b4d98b4214 100644 --- a/internal/lsp/source/workspace_symbol.go +++ b/internal/lsp/source/workspace_symbol.go @@ -19,7 +19,7 @@ import ( const maxSymbols = 100 // WorkspaceSymbols matches symbols across views using the given query, -// according to the matcher Matcher. +// according to the SymbolMatcher matcher. // // The workspace symbol method is defined in the spec as follows: // @@ -32,10 +32,10 @@ const maxSymbols = 100 // WorkspaceSymbols receives the views []View. // // However, it then becomes unclear what it would mean to call WorkspaceSymbols -// with a different configured Matcher per View. Therefore we assume that -// Session level configuration will define the Matcher to be used for the +// with a different configured SymbolMatcher per View. Therefore we assume that +// Session level configuration will define the SymbolMatcher to be used for the // WorkspaceSymbols method. -func WorkspaceSymbols(ctx context.Context, matcherType Matcher, views []View, query string) ([]protocol.SymbolInformation, error) { +func WorkspaceSymbols(ctx context.Context, matcherType SymbolMatcher, views []View, query string) ([]protocol.SymbolInformation, error) { ctx, done := event.Start(ctx, "source.WorkspaceSymbols") defer done() if query == "" { @@ -102,14 +102,14 @@ type symbolInformation struct { type matcherFunc func(string) bool -func makeMatcher(m Matcher, query string) matcherFunc { +func makeMatcher(m SymbolMatcher, query string) matcherFunc { switch m { - case Fuzzy: + case SymbolFuzzy: fm := fuzzy.NewMatcher(query) return func(s string) bool { return fm.Score(s) > 0 } - case CaseSensitive: + case SymbolCaseSensitive: return func(s string) bool { return strings.Contains(s, query) } diff --git a/internal/lsp/workspace_symbol.go b/internal/lsp/workspace_symbol.go index 3dd68209df..a233d448a4 100644 --- a/internal/lsp/workspace_symbol.go +++ b/internal/lsp/workspace_symbol.go @@ -17,6 +17,6 @@ func (s *Server) symbol(ctx context.Context, params *protocol.WorkspaceSymbolPar defer done() views := s.session.Views() - matcher := s.session.Options().Matcher + matcher := s.session.Options().SymbolMatcher return source.WorkspaceSymbols(ctx, matcher, views, params.Query) }