internal/lsp/semantic.go: add the skeleton for supporting semantic tokens
This Cl adds all the support for producing semantic tokens other than
generating them. That includes new stubs for LSP, a new option for
the user to choose semantic tokens, and the skeleton for producing
semantic tokens, except that 0 semantic tokens are produced.
vscode 1.49.1 (the current version) does not ask for semantic tokens;
vscode-insiders does.
Change-Id: Iceb8fff974deb9283281319bb5878271c3d3170d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/257578
Run-TryBot: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Peter Weinberger <pjw@google.com>
diff --git a/gopls/doc/settings.md b/gopls/doc/settings.md
index 78a3ccb..c180f70 100644
--- a/gopls/doc/settings.md
+++ b/gopls/doc/settings.md
@@ -218,6 +218,12 @@
Default: `false`.
+### **semanticTokens** *bool*
+semanticTokens controls whether the LSP server will send
+semantic tokens to the client.
+
+
+Default: `false`.
### **expandWorkspaceToModule** *bool*
expandWorkspaceToModule instructs `gopls` to expand the scope of the workspace to include the
modules containing the workspace folders. Set this to false to avoid loading
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index 0ca97c9..27df063 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -86,7 +86,7 @@
goplsVer := &bytes.Buffer{}
debug.PrintVersionInfo(ctx, goplsVer, true, debug.PlainText)
- return &protocol.InitializeResult{
+ ans := &protocol.InitializeResult{
Capabilities: protocol.ServerCapabilities{
CallHierarchyProvider: true,
CodeActionProvider: codeActionProvider,
@@ -132,7 +132,25 @@
Name: "gopls",
Version: goplsVer.String(),
},
- }, nil
+ }
+
+ st := params.Capabilities.TextDocument.SemanticTokens
+ if st != nil {
+ tokTypes, tokModifiers := rememberToks(st.TokenTypes, st.TokenModifiers)
+ // check that st.TokenFormat is "relative"
+ v := &protocol.SemanticTokensOptions{
+ Legend: protocol.SemanticTokensLegend{
+ // TODO(pjw): trim these to what we use (and an unused one
+ // at position 0 of TokTypes, to catch typos)
+ TokenTypes: tokTypes,
+ TokenModifiers: tokModifiers,
+ },
+ Range: true,
+ Full: true,
+ }
+ ans.Capabilities.SemanticTokensProvider = v
+ }
+ return ans, nil
}
func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go
index 5d1c0a0..c3532b5 100644
--- a/internal/lsp/protocol/tsprotocol.go
+++ b/internal/lsp/protocol/tsprotocol.go
@@ -3298,6 +3298,9 @@
* @since 3.16.0
*/
CallHierarchy CallHierarchyClientCapabilities `json:"callHierarchy,omitempty"`
+
+ // missing in source, generated
+ SemanticTokens *SemanticTokensClientCapabilities `json:"semanticTokens,omitempty"`
}
/**
@@ -3861,6 +3864,20 @@
PartialResultParams
}
+// generated
+type SemanticTokensClientCapabilities struct {
+ TokenModifiers []string
+ Formats []string
+ Requests struct {
+ Range bool
+ Full struct {
+ Delta bool
+ }
+ }
+ DynamicRegistration bool
+ TokenTypes []string
+}
+
const (
/**
* Empty kind.
diff --git a/internal/lsp/semantic.go b/internal/lsp/semantic.go
new file mode 100644
index 0000000..71dd320
--- /dev/null
+++ b/internal/lsp/semantic.go
@@ -0,0 +1,308 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package lsp
+
+import (
+ "context"
+ "fmt"
+ "go/ast"
+ "go/types"
+ "log"
+ "sort"
+ "strings"
+ "time"
+
+ "golang.org/x/tools/internal/event"
+ "golang.org/x/tools/internal/lsp/protocol"
+ "golang.org/x/tools/internal/lsp/source"
+ errors "golang.org/x/xerrors"
+)
+
+func (s *Server) semanticTokensFull(ctx context.Context, p *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
+ now := time.Now()
+ ret, err := s.computeSemanticTokens(ctx, p.TextDocument, nil)
+ if err == nil {
+ event.Log(ctx, fmt.Sprintf("Full(%v): %d items for %s in %s",
+ s.session.Options().SemanticTokens, len(ret.Data)/5, p.TextDocument.URI.SpanURI().Filename(), time.Since(now)))
+ } else {
+ event.Error(ctx, fmt.Sprintf("Full failed for %s in %s", p.TextDocument.URI.SpanURI().Filename(), time.Since(now)), err)
+ }
+ return ret, err
+}
+
+func (s *Server) semanticTokensFullDelta(ctx context.Context, p *protocol.SemanticTokensDeltaParams) (interface{}, error) {
+ return nil, errors.Errorf("implement SemanticTokensFullDelta")
+}
+
+func (s *Server) semanticTokensRange(ctx context.Context, p *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) {
+ now := time.Now()
+ ret, err := s.computeSemanticTokens(ctx, p.TextDocument, &p.Range)
+ if err == nil {
+ event.Log(ctx, fmt.Sprintf("Range(%v): %d items for %s %s in %s",
+ s.session.Options().SemanticTokens, len(ret.Data)/5, p.TextDocument.URI.SpanURI().Filename(),
+ p.Range, time.Since(now)))
+ } else {
+ event.Error(ctx, "semanticTokensRange failed", err)
+ }
+ return ret, err
+}
+
+func (s *Server) semanticTokensRefresh(ctx context.Context) error {
+ // in the code, but not in the protocol spec
+ return errors.Errorf("implement SemanticTokensRefresh")
+}
+
+func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocumentIdentifier, rng *protocol.Range) (*protocol.SemanticTokens, error) {
+ ans := protocol.SemanticTokens{
+ Data: []float64{},
+ }
+ snapshot, _, ok, release, err := s.beginFileRequest(ctx, td.URI, source.Go)
+ defer release()
+ if !ok {
+ return nil, err
+ }
+ vv, err := s.session.ViewOf(td.URI.SpanURI())
+ if err != nil {
+ return nil, err
+ }
+ if !vv.Options().SemanticTokens {
+ // return an error, so if the option changes
+ // the client won't remember the wrong answer
+ return nil, errors.Errorf("semantictokens are disabled")
+ }
+ pkg, err := snapshot.PackageForFile(ctx, td.URI.SpanURI(), source.TypecheckFull, source.WidestPackage)
+ if err != nil {
+ return nil, err
+ }
+ info := pkg.GetTypesInfo()
+ pgf, err := pkg.File(td.URI.SpanURI())
+ if err != nil {
+ return nil, err
+ }
+ e := &encoded{
+ pgf: pgf,
+ rng: rng,
+ ti: info,
+ }
+ if err := e.init(); err != nil {
+ return nil, err
+ }
+ e.semantics()
+ ans.Data, err = e.Data()
+ if err != nil {
+ // this is an internal error, likely caused by a typo
+ // for a token or modifier
+ return nil, err
+ }
+ // for small cache, some day. for now, the client ignores this
+ ans.ResultID = fmt.Sprintf("%v", time.Now())
+ return &ans, nil
+}
+
+func (e *encoded) semantics() {
+ f := e.pgf.File
+ inspect := func(n ast.Node) bool {
+ return e.inspector(n)
+ }
+ for _, d := range f.Decls {
+ // only look at the decls that overlap the range
+ start, end := d.Pos(), d.End()
+ if int(end) <= e.start || int(start) >= e.end {
+ continue
+ }
+ ast.Inspect(d, inspect)
+ }
+}
+
+// semItem represents a token found walking the parse tree
+type semItem struct {
+ line, start, len int
+ typeStr string
+ mods []string
+}
+
+type encoded struct {
+ // the generated data
+ items []semItem
+
+ pgf *source.ParsedGoFile
+ rng *protocol.Range
+ ti *types.Info
+ // allowed starting and ending token.Pos, set by init
+ // used to avoid looking at declarations not in range
+ start, end int
+ // path from the root of the parse tree, used for debugging
+ stack []ast.Node
+}
+
+// convert the stack to a string, for debugging
+func (e *encoded) strStack() string {
+ msg := []string{"["}
+ for _, s := range e.stack {
+ msg = append(msg, fmt.Sprintf("%T", s)[5:])
+ }
+ if len(e.stack) > 0 {
+ loc := e.stack[len(e.stack)-1].Pos()
+ add := e.pgf.Tok.PositionFor(loc, false)
+ msg = append(msg, fmt.Sprintf("(%d:%d)", add.Line, add.Column))
+ }
+ msg = append(msg, "]")
+ return strings.Join(msg, " ")
+}
+
+func (e *encoded) inspector(n ast.Node) bool {
+ // this will be filled in, in the next CL
+ pop := func() {
+ e.stack = e.stack[:len(e.stack)-1]
+ }
+ if n == nil {
+ pop()
+ return true
+ }
+ e.stack = append(e.stack, n)
+ switch x := n.(type) {
+ case *ast.ArrayType:
+ case *ast.AssignStmt:
+ case *ast.BasicLit:
+ case *ast.BinaryExpr:
+ case *ast.BlockStmt:
+ case *ast.BranchStmt:
+ case *ast.CallExpr:
+ case *ast.CaseClause:
+ case *ast.ChanType:
+ case *ast.CommClause:
+ case *ast.CompositeLit:
+ case *ast.DeclStmt:
+ case *ast.DeferStmt:
+ case *ast.Ellipsis:
+ case *ast.EmptyStmt:
+ case *ast.ExprStmt:
+ case *ast.Field:
+ case *ast.FieldList:
+ case *ast.ForStmt:
+ case *ast.FuncDecl:
+ case *ast.FuncLit:
+ case *ast.FuncType:
+ case *ast.GenDecl:
+ case *ast.GoStmt:
+ case *ast.Ident:
+ case *ast.IfStmt:
+ case *ast.ImportSpec:
+ pop()
+ return false
+ case *ast.IncDecStmt:
+ case *ast.IndexExpr:
+ case *ast.InterfaceType:
+ case *ast.KeyValueExpr:
+ case *ast.LabeledStmt:
+ case *ast.MapType:
+ case *ast.ParenExpr:
+ case *ast.RangeStmt:
+ case *ast.ReturnStmt:
+ case *ast.SelectStmt:
+ case *ast.SelectorExpr:
+ case *ast.SendStmt:
+ case *ast.SliceExpr:
+ case *ast.StarExpr:
+ case *ast.StructType:
+ case *ast.SwitchStmt:
+ case *ast.TypeAssertExpr:
+ case *ast.TypeSpec:
+ case *ast.TypeSwitchStmt:
+ case *ast.UnaryExpr:
+ case *ast.ValueSpec:
+ // things we won't see
+ case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt,
+ *ast.File, *ast.Package:
+ log.Printf("implement %T %s", x, e.pgf.Tok.PositionFor(x.Pos(), false))
+ // things we knowingly ignore
+ case *ast.Comment, *ast.CommentGroup:
+ pop()
+ return false
+ default: // just to be super safe.
+ panic(fmt.Sprintf("failed to implement %T", x))
+ }
+ return true
+}
+
+func (e *encoded) init() error {
+ e.start = e.pgf.Tok.Base()
+ e.end = e.start + e.pgf.Tok.Size()
+ if e.rng == nil {
+ return nil
+ }
+ span, err := e.pgf.Mapper.RangeSpan(*e.rng)
+ if err != nil {
+ return errors.Errorf("range span error for %s", e.pgf.File.Name)
+ }
+ e.end = e.start + span.End().Offset()
+ e.start += span.Start().Offset()
+ return nil
+}
+
+func (e *encoded) Data() ([]float64, error) {
+ // binary operators, at least, will be out of order
+ sort.Slice(e.items, func(i, j int) bool {
+ if e.items[i].line != e.items[j].line {
+ return e.items[i].line < e.items[j].line
+ }
+ return e.items[i].start < e.items[j].start
+ })
+ // each semantic token needs five values
+ // (see Integer Encoding for Tokens in the LSP spec)
+ x := make([]int, 5*len(e.items))
+ for i := 0; i < len(e.items); i++ {
+ j := 5 * i
+ if i == 0 {
+ x[0] = e.items[0].line
+ } else {
+ x[j] = e.items[i].line - e.items[i-1].line
+ }
+ x[j+1] = e.items[i].start
+ if i > 0 && e.items[i].line == e.items[i-1].line {
+ x[j+1] = e.items[i].start - e.items[i-1].start
+ }
+ x[j+2] = e.items[i].len
+ x[j+3] = SemanticMemo.typeMap[e.items[i].typeStr]
+ for _, s := range e.items[i].mods {
+ x[j+4] |= SemanticMemo.modMap[s]
+ }
+ }
+ // As the client never sends these, we could fix the generated
+ // type to want []int rather than []float64.
+ ans := make([]float64, len(x))
+ for i, y := range x {
+ ans[i] = float64(y)
+ }
+ return ans, nil
+}
+
+// SemMemo supports semantic token translations between numbers and strings
+type SemMemo struct {
+ tokTypes, tokMods []string
+ typeMap map[string]int
+ modMap map[string]int
+}
+
+var SemanticMemo *SemMemo
+
+// save what the client sent
+func rememberToks(toks []string, mods []string) ([]string, []string) {
+ SemanticMemo = &SemMemo{
+ tokTypes: toks,
+ tokMods: mods,
+ typeMap: make(map[string]int),
+ modMap: make(map[string]int),
+ }
+ for i, t := range toks {
+ SemanticMemo.typeMap[t] = i
+ }
+ for i, m := range mods {
+ SemanticMemo.modMap[m] = 1 << uint(i)
+ }
+ // we could have pruned or rearranged them.
+ // But then change the list in cmd.go too
+ return SemanticMemo.tokTypes, SemanticMemo.tokMods
+}
diff --git a/internal/lsp/server_gen.go b/internal/lsp/server_gen.go
index d22a5eb..99fc6b0 100644
--- a/internal/lsp/server_gen.go
+++ b/internal/lsp/server_gen.go
@@ -164,20 +164,20 @@
return nil, notImplemented("SelectionRange")
}
-func (s *Server) SemanticTokensFull(context.Context, *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
- return nil, notImplemented("SemanticTokensFull")
+func (s *Server) SemanticTokensFull(ctx context.Context, p *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
+ return s.semanticTokensFull(ctx, p)
}
-func (s *Server) SemanticTokensFullDelta(context.Context, *protocol.SemanticTokensDeltaParams) (interface{}, error) {
- return nil, notImplemented("SemanticTokensFullDelta")
+func (s *Server) SemanticTokensFullDelta(ctx context.Context, p *protocol.SemanticTokensDeltaParams) (interface{}, error) {
+ return s.semanticTokensFullDelta(ctx, p)
}
-func (s *Server) SemanticTokensRange(context.Context, *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) {
- return nil, notImplemented("SemanticTokensRange")
+func (s *Server) SemanticTokensRange(ctx context.Context, p *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) {
+ return s.semanticTokensRange(ctx, p)
}
-func (s *Server) SemanticTokensRefresh(context.Context) error {
- return notImplemented("SemanticTokensRefresh")
+func (s *Server) SemanticTokensRefresh(ctx context.Context) error {
+ return s.semanticTokensRefresh(ctx)
}
func (s *Server) SetTrace(context.Context, *protocol.SetTraceParams) error {
diff --git a/internal/lsp/source/api_json.go b/internal/lsp/source/api_json.go
index df859c5..7295d64 100755
--- a/internal/lsp/source/api_json.go
+++ b/internal/lsp/source/api_json.go
@@ -2,4 +2,4 @@
package source
-const GeneratedAPIJSON = "{\"Options\":{\"Debugging\":[{\"Name\":\"verboseOutput\",\"Type\":\"bool\",\"Doc\":\"verboseOutput enables additional debug logging.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"completionBudget\",\"Type\":\"time.Duration\",\"Doc\":\"completionBudget is the soft latency goal for completion requests. Most\\nrequests finish in a couple milliseconds, but in some cases deep\\ncompletions can take much longer. As we use up our budget we\\ndynamically reduce the search scope to ensure we return timely\\nresults. Zero means unlimited.\\n\",\"EnumValues\":null,\"Default\":\"100000000\"}],\"Experimental\":[{\"Name\":\"analyses\",\"Type\":\"map[string]bool\",\"Doc\":\"analyses specify analyses that the user would like to enable or disable.\\nA map of the names of analysis passes that should be enabled/disabled.\\nA full list of analyzers that gopls uses can be found [here](analyzers.md)\\n\\nExample Usage:\\n```json5\\n...\\n\\\"analyses\\\": {\\n \\\"unreachable\\\": false, // Disable the unreachable analyzer.\\n \\\"unusedparams\\\": true // Enable the unusedparams analyzer.\\n}\\n...\\n```\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"codelens\",\"Type\":\"map[string]bool\",\"Doc\":\"codelens overrides the enabled/disabled state of code lenses. See the \\\"Code Lenses\\\"\\nsection of settings.md for the list of supported lenses.\\n\\nExample Usage:\\n```json5\\n\\\"gopls\\\": {\\n...\\n \\\"codelens\\\": {\\n \\\"generate\\\": false, // Don't show the `go generate` lens.\\n \\\"gc_details\\\": true // Show a code lens toggling the display of gc's choices.\\n }\\n...\\n}\\n```\\n\",\"EnumValues\":null,\"Default\":\"{\\\"gc_details\\\":false,\\\"generate\\\":true,\\\"regenerate_cgo\\\":true,\\\"tidy\\\":true,\\\"upgrade_dependency\\\":true,\\\"vendor\\\":true}\"},{\"Name\":\"completionDocumentation\",\"Type\":\"bool\",\"Doc\":\"completionDocumentation enables documentation with completion results.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"completeUnimported\",\"Type\":\"bool\",\"Doc\":\"completeUnimported enables completion for packages that you do not currently import.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"deepCompletion\",\"Type\":\"bool\",\"Doc\":\"deepCompletion enables the ability to return completions from deep inside relevant entities, rather than just the locally accessible ones.\\n\\nConsider this example:\\n\\n```go\\npackage main\\n\\nimport \\\"fmt\\\"\\n\\ntype wrapString struct {\\n str string\\n}\\n\\nfunc main() {\\n x := wrapString{\\\"hello world\\\"}\\n fmt.Printf(\\u003c\\u003e)\\n}\\n```\\n\\nAt the location of the `\\u003c\\u003e` in this program, deep completion would suggest the result `x.str`.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"matcher\",\"Type\":\"enum\",\"Doc\":\"matcher sets the algorithm that is used when calculating completion candidates.\\n\",\"EnumValues\":[\"\\\"CaseInsensitive\\\"\",\"\\\"CaseSensitive\\\"\",\"\\\"Fuzzy\\\"\"],\"Default\":\"\\\"Fuzzy\\\"\"},{\"Name\":\"annotations\",\"Type\":\"map[string]bool\",\"Doc\":\"annotations suppress various kinds of optimization diagnostics\\nthat would be reported by the gc_details command.\\n * noNilcheck suppresses display of nilchecks.\\n * noEscape suppresses escape choices.\\n * noInline suppresses inlining choices.\\n * noBounds suppresses bounds checking diagnostics.\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"staticcheck\",\"Type\":\"bool\",\"Doc\":\"staticcheck enables additional analyses from staticcheck.io.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"symbolMatcher\",\"Type\":\"enum\",\"Doc\":\"symbolMatcher sets the algorithm that is used when finding workspace symbols.\\n\",\"EnumValues\":[\"\\\"CaseInsensitive\\\"\",\"\\\"CaseSensitive\\\"\",\"\\\"Fuzzy\\\"\"],\"Default\":\"\\\"Fuzzy\\\"\"},{\"Name\":\"symbolStyle\",\"Type\":\"enum\",\"Doc\":\"symbolStyle specifies what style of symbols to return in symbol requests.\\n\",\"EnumValues\":[\"\\\"Dynamic\\\"\",\"\\\"Full\\\"\",\"\\\"Package\\\"\"],\"Default\":\"\\\"Package\\\"\"},{\"Name\":\"linksInHover\",\"Type\":\"bool\",\"Doc\":\"linksInHover toggles the presence of links to documentation in hover.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"tempModfile\",\"Type\":\"bool\",\"Doc\":\"tempModfile controls the use of the -modfile flag in Go 1.14.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"importShortcut\",\"Type\":\"enum\",\"Doc\":\"importShortcut specifies whether import statements should link to\\ndocumentation or go to definitions.\\n\",\"EnumValues\":[\"\\\"Both\\\"\",\"\\\"Definition\\\"\",\"\\\"Link\\\"\"],\"Default\":\"\\\"Both\\\"\"},{\"Name\":\"verboseWorkDoneProgress\",\"Type\":\"bool\",\"Doc\":\"verboseWorkDoneProgress controls whether the LSP server should send\\nprogress reports for all work done outside the scope of an RPC.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"expandWorkspaceToModule\",\"Type\":\"bool\",\"Doc\":\"expandWorkspaceToModule instructs `gopls` to expand the scope of the workspace to include the\\nmodules containing the workspace folders. Set this to false to avoid loading\\nyour entire module. This is particularly useful for those working in a monorepo.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"experimentalWorkspaceModule\",\"Type\":\"bool\",\"Doc\":\"experimentalWorkspaceModule opts a user into the experimental support\\nfor multi-module workspaces.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"literalCompletions\",\"Type\":\"bool\",\"Doc\":\"literalCompletions controls whether literal candidates such as\\n\\\"\\u0026someStruct{}\\\" are offered. Tests disable this flag to simplify\\ntheir expected values.\\n\",\"EnumValues\":null,\"Default\":\"true\"}],\"User\":[{\"Name\":\"buildFlags\",\"Type\":\"[]string\",\"Doc\":\"buildFlags is the set of flags passed on to the build system when invoked.\\nIt is applied to queries like `go list`, which is used when discovering files.\\nThe most common use is to set `-tags`.\\n\",\"EnumValues\":null,\"Default\":\"[]\"},{\"Name\":\"env\",\"Type\":\"[]string\",\"Doc\":\"env adds environment variables to external commands run by `gopls`, most notably `go list`.\\n\",\"EnumValues\":null,\"Default\":\"[]\"},{\"Name\":\"hoverKind\",\"Type\":\"enum\",\"Doc\":\"hoverKind controls the information that appears in the hover text.\\nSingleLine and Structured are intended for use only by authors of editor plugins.\\n\",\"EnumValues\":[\"\\\"FullDocumentation\\\"\",\"\\\"NoDocumentation\\\"\",\"\\\"SingleLine\\\"\",\"\\\"Structured\\\"\",\"\\\"SynopsisDocumentation\\\"\"],\"Default\":\"\\\"FullDocumentation\\\"\"},{\"Name\":\"usePlaceholders\",\"Type\":\"bool\",\"Doc\":\"placeholders enables placeholders for function parameters or struct fields in completion responses.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"linkTarget\",\"Type\":\"string\",\"Doc\":\"linkTarget controls where documentation links go.\\nIt might be one of:\\n\\n* `\\\"godoc.org\\\"`\\n* `\\\"pkg.go.dev\\\"`\\n\\nIf company chooses to use its own `godoc.org`, its address can be used as well.\\n\",\"EnumValues\":null,\"Default\":\"\\\"pkg.go.dev\\\"\"},{\"Name\":\"local\",\"Type\":\"string\",\"Doc\":\"local is the equivalent of the `goimports -local` flag, which puts imports beginning with this string after 3rd-party packages.\\nIt should be the prefix of the import path whose imports should be grouped separately.\\n\",\"EnumValues\":null,\"Default\":\"\\\"\\\"\"},{\"Name\":\"gofumpt\",\"Type\":\"bool\",\"Doc\":\"gofumpt indicates if we should run gofumpt formatting.\\n\",\"EnumValues\":null,\"Default\":\"false\"}]},\"Commands\":[{\"Command\":\"generate\",\"Title\":\"Run go generate\",\"Doc\":\"generate runs `go generate` for a given directory.\\n\"},{\"Command\":\"fill_struct\",\"Title\":\"fill_struct\",\"Doc\":\"fill_struct is a gopls command to fill a struct with default\\nvalues.\\n\"},{\"Command\":\"regenerate_cgo\",\"Title\":\"Regenerate cgo\",\"Doc\":\"regenerate_cgo regenerates cgo definitions.\\n\"},{\"Command\":\"test\",\"Title\":\"Run test(s)\",\"Doc\":\"test runs `go test` for a specific test function.\\n\"},{\"Command\":\"tidy\",\"Title\":\"Run go mod tidy\",\"Doc\":\"tidy runs `go mod tidy` for a module.\\n\"},{\"Command\":\"undeclared_name\",\"Title\":\"undeclared_name\",\"Doc\":\"undeclared_name adds a variable declaration for an undeclared\\nname.\\n\"},{\"Command\":\"upgrade_dependency\",\"Title\":\"Upgrade dependency\",\"Doc\":\"upgrade_dependency upgrades a dependency.\\n\"},{\"Command\":\"vendor\",\"Title\":\"Run go mod vendor\",\"Doc\":\"vendor runs `go mod vendor` for a module.\\n\"},{\"Command\":\"extract_variable\",\"Title\":\"Extract to variable\",\"Doc\":\"extract_variable extracts an expression to a variable.\\n\"},{\"Command\":\"extract_function\",\"Title\":\"Extract to function\",\"Doc\":\"extract_function extracts statements to a function.\\n\"},{\"Command\":\"gc_details\",\"Title\":\"Toggle gc_details\",\"Doc\":\"gc_details controls calculation of gc annotations.\\n\"},{\"Command\":\"generate_gopls_mod\",\"Title\":\"Generate gopls.mod\",\"Doc\":\"generate_gopls_mod (re)generates the gopls.mod file.\\n\"}],\"Lenses\":[{\"Lens\":\"generate\",\"Title\":\"Run go generate\",\"Doc\":\"generate runs `go generate` for a given directory.\\n\"},{\"Lens\":\"regenerate_cgo\",\"Title\":\"Regenerate cgo\",\"Doc\":\"regenerate_cgo regenerates cgo definitions.\\n\"},{\"Lens\":\"test\",\"Title\":\"Run test(s)\",\"Doc\":\"test runs `go test` for a specific test function.\\n\"},{\"Lens\":\"tidy\",\"Title\":\"Run go mod tidy\",\"Doc\":\"tidy runs `go mod tidy` for a module.\\n\"},{\"Lens\":\"upgrade_dependency\",\"Title\":\"Upgrade dependency\",\"Doc\":\"upgrade_dependency upgrades a dependency.\\n\"},{\"Lens\":\"vendor\",\"Title\":\"Run go mod vendor\",\"Doc\":\"vendor runs `go mod vendor` for a module.\\n\"},{\"Lens\":\"gc_details\",\"Title\":\"Toggle gc_details\",\"Doc\":\"gc_details controls calculation of gc annotations.\\n\"}]}"
+const GeneratedAPIJSON = "{\"Options\":{\"Debugging\":[{\"Name\":\"verboseOutput\",\"Type\":\"bool\",\"Doc\":\"verboseOutput enables additional debug logging.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"completionBudget\",\"Type\":\"time.Duration\",\"Doc\":\"completionBudget is the soft latency goal for completion requests. Most\\nrequests finish in a couple milliseconds, but in some cases deep\\ncompletions can take much longer. As we use up our budget we\\ndynamically reduce the search scope to ensure we return timely\\nresults. Zero means unlimited.\\n\",\"EnumValues\":null,\"Default\":\"100000000\"}],\"Experimental\":[{\"Name\":\"analyses\",\"Type\":\"map[string]bool\",\"Doc\":\"analyses specify analyses that the user would like to enable or disable.\\nA map of the names of analysis passes that should be enabled/disabled.\\nA full list of analyzers that gopls uses can be found [here](analyzers.md)\\n\\nExample Usage:\\n```json5\\n...\\n\\\"analyses\\\": {\\n \\\"unreachable\\\": false, // Disable the unreachable analyzer.\\n \\\"unusedparams\\\": true // Enable the unusedparams analyzer.\\n}\\n...\\n```\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"codelens\",\"Type\":\"map[string]bool\",\"Doc\":\"codelens overrides the enabled/disabled state of code lenses. See the \\\"Code Lenses\\\"\\nsection of settings.md for the list of supported lenses.\\n\\nExample Usage:\\n```json5\\n\\\"gopls\\\": {\\n...\\n \\\"codelens\\\": {\\n \\\"generate\\\": false, // Don't show the `go generate` lens.\\n \\\"gc_details\\\": true // Show a code lens toggling the display of gc's choices.\\n }\\n...\\n}\\n```\\n\",\"EnumValues\":null,\"Default\":\"{\\\"gc_details\\\":false,\\\"generate\\\":true,\\\"regenerate_cgo\\\":true,\\\"tidy\\\":true,\\\"upgrade_dependency\\\":true,\\\"vendor\\\":true}\"},{\"Name\":\"completionDocumentation\",\"Type\":\"bool\",\"Doc\":\"completionDocumentation enables documentation with completion results.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"completeUnimported\",\"Type\":\"bool\",\"Doc\":\"completeUnimported enables completion for packages that you do not currently import.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"deepCompletion\",\"Type\":\"bool\",\"Doc\":\"deepCompletion enables the ability to return completions from deep inside relevant entities, rather than just the locally accessible ones.\\n\\nConsider this example:\\n\\n```go\\npackage main\\n\\nimport \\\"fmt\\\"\\n\\ntype wrapString struct {\\n str string\\n}\\n\\nfunc main() {\\n x := wrapString{\\\"hello world\\\"}\\n fmt.Printf(\\u003c\\u003e)\\n}\\n```\\n\\nAt the location of the `\\u003c\\u003e` in this program, deep completion would suggest the result `x.str`.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"matcher\",\"Type\":\"enum\",\"Doc\":\"matcher sets the algorithm that is used when calculating completion candidates.\\n\",\"EnumValues\":[\"\\\"CaseInsensitive\\\"\",\"\\\"CaseSensitive\\\"\",\"\\\"Fuzzy\\\"\"],\"Default\":\"\\\"Fuzzy\\\"\"},{\"Name\":\"annotations\",\"Type\":\"map[string]bool\",\"Doc\":\"annotations suppress various kinds of optimization diagnostics\\nthat would be reported by the gc_details command.\\n * noNilcheck suppresses display of nilchecks.\\n * noEscape suppresses escape choices.\\n * noInline suppresses inlining choices.\\n * noBounds suppresses bounds checking diagnostics.\\n\",\"EnumValues\":null,\"Default\":\"{}\"},{\"Name\":\"staticcheck\",\"Type\":\"bool\",\"Doc\":\"staticcheck enables additional analyses from staticcheck.io.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"symbolMatcher\",\"Type\":\"enum\",\"Doc\":\"symbolMatcher sets the algorithm that is used when finding workspace symbols.\\n\",\"EnumValues\":[\"\\\"CaseInsensitive\\\"\",\"\\\"CaseSensitive\\\"\",\"\\\"Fuzzy\\\"\"],\"Default\":\"\\\"Fuzzy\\\"\"},{\"Name\":\"symbolStyle\",\"Type\":\"enum\",\"Doc\":\"symbolStyle specifies what style of symbols to return in symbol requests.\\n\",\"EnumValues\":[\"\\\"Dynamic\\\"\",\"\\\"Full\\\"\",\"\\\"Package\\\"\"],\"Default\":\"\\\"Package\\\"\"},{\"Name\":\"linksInHover\",\"Type\":\"bool\",\"Doc\":\"linksInHover toggles the presence of links to documentation in hover.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"tempModfile\",\"Type\":\"bool\",\"Doc\":\"tempModfile controls the use of the -modfile flag in Go 1.14.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"importShortcut\",\"Type\":\"enum\",\"Doc\":\"importShortcut specifies whether import statements should link to\\ndocumentation or go to definitions.\\n\",\"EnumValues\":[\"\\\"Both\\\"\",\"\\\"Definition\\\"\",\"\\\"Link\\\"\"],\"Default\":\"\\\"Both\\\"\"},{\"Name\":\"verboseWorkDoneProgress\",\"Type\":\"bool\",\"Doc\":\"verboseWorkDoneProgress controls whether the LSP server should send\\nprogress reports for all work done outside the scope of an RPC.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"semanticTokens\",\"Type\":\"bool\",\"Doc\":\"semanticTokens controls whether the LSP server will send\\nsemantic tokens to the client.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"expandWorkspaceToModule\",\"Type\":\"bool\",\"Doc\":\"expandWorkspaceToModule instructs `gopls` to expand the scope of the workspace to include the\\nmodules containing the workspace folders. Set this to false to avoid loading\\nyour entire module. This is particularly useful for those working in a monorepo.\\n\",\"EnumValues\":null,\"Default\":\"true\"},{\"Name\":\"experimentalWorkspaceModule\",\"Type\":\"bool\",\"Doc\":\"experimentalWorkspaceModule opts a user into the experimental support\\nfor multi-module workspaces.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"literalCompletions\",\"Type\":\"bool\",\"Doc\":\"literalCompletions controls whether literal candidates such as\\n\\\"\\u0026someStruct{}\\\" are offered. Tests disable this flag to simplify\\ntheir expected values.\\n\",\"EnumValues\":null,\"Default\":\"true\"}],\"User\":[{\"Name\":\"buildFlags\",\"Type\":\"[]string\",\"Doc\":\"buildFlags is the set of flags passed on to the build system when invoked.\\nIt is applied to queries like `go list`, which is used when discovering files.\\nThe most common use is to set `-tags`.\\n\",\"EnumValues\":null,\"Default\":\"[]\"},{\"Name\":\"env\",\"Type\":\"[]string\",\"Doc\":\"env adds environment variables to external commands run by `gopls`, most notably `go list`.\\n\",\"EnumValues\":null,\"Default\":\"[]\"},{\"Name\":\"hoverKind\",\"Type\":\"enum\",\"Doc\":\"hoverKind controls the information that appears in the hover text.\\nSingleLine and Structured are intended for use only by authors of editor plugins.\\n\",\"EnumValues\":[\"\\\"FullDocumentation\\\"\",\"\\\"NoDocumentation\\\"\",\"\\\"SingleLine\\\"\",\"\\\"Structured\\\"\",\"\\\"SynopsisDocumentation\\\"\"],\"Default\":\"\\\"FullDocumentation\\\"\"},{\"Name\":\"usePlaceholders\",\"Type\":\"bool\",\"Doc\":\"placeholders enables placeholders for function parameters or struct fields in completion responses.\\n\",\"EnumValues\":null,\"Default\":\"false\"},{\"Name\":\"linkTarget\",\"Type\":\"string\",\"Doc\":\"linkTarget controls where documentation links go.\\nIt might be one of:\\n\\n* `\\\"godoc.org\\\"`\\n* `\\\"pkg.go.dev\\\"`\\n\\nIf company chooses to use its own `godoc.org`, its address can be used as well.\\n\",\"EnumValues\":null,\"Default\":\"\\\"pkg.go.dev\\\"\"},{\"Name\":\"local\",\"Type\":\"string\",\"Doc\":\"local is the equivalent of the `goimports -local` flag, which puts imports beginning with this string after 3rd-party packages.\\nIt should be the prefix of the import path whose imports should be grouped separately.\\n\",\"EnumValues\":null,\"Default\":\"\\\"\\\"\"},{\"Name\":\"gofumpt\",\"Type\":\"bool\",\"Doc\":\"gofumpt indicates if we should run gofumpt formatting.\\n\",\"EnumValues\":null,\"Default\":\"false\"}]},\"Commands\":[{\"Command\":\"generate\",\"Title\":\"Run go generate\",\"Doc\":\"generate runs `go generate` for a given directory.\\n\"},{\"Command\":\"fill_struct\",\"Title\":\"fill_struct\",\"Doc\":\"fill_struct is a gopls command to fill a struct with default\\nvalues.\\n\"},{\"Command\":\"regenerate_cgo\",\"Title\":\"Regenerate cgo\",\"Doc\":\"regenerate_cgo regenerates cgo definitions.\\n\"},{\"Command\":\"test\",\"Title\":\"Run test(s)\",\"Doc\":\"test runs `go test` for a specific test function.\\n\"},{\"Command\":\"tidy\",\"Title\":\"Run go mod tidy\",\"Doc\":\"tidy runs `go mod tidy` for a module.\\n\"},{\"Command\":\"undeclared_name\",\"Title\":\"undeclared_name\",\"Doc\":\"undeclared_name adds a variable declaration for an undeclared\\nname.\\n\"},{\"Command\":\"upgrade_dependency\",\"Title\":\"Upgrade dependency\",\"Doc\":\"upgrade_dependency upgrades a dependency.\\n\"},{\"Command\":\"vendor\",\"Title\":\"Run go mod vendor\",\"Doc\":\"vendor runs `go mod vendor` for a module.\\n\"},{\"Command\":\"extract_variable\",\"Title\":\"Extract to variable\",\"Doc\":\"extract_variable extracts an expression to a variable.\\n\"},{\"Command\":\"extract_function\",\"Title\":\"Extract to function\",\"Doc\":\"extract_function extracts statements to a function.\\n\"},{\"Command\":\"gc_details\",\"Title\":\"Toggle gc_details\",\"Doc\":\"gc_details controls calculation of gc annotations.\\n\"},{\"Command\":\"generate_gopls_mod\",\"Title\":\"Generate gopls.mod\",\"Doc\":\"generate_gopls_mod (re)generates the gopls.mod file.\\n\"}],\"Lenses\":[{\"Lens\":\"generate\",\"Title\":\"Run go generate\",\"Doc\":\"generate runs `go generate` for a given directory.\\n\"},{\"Lens\":\"regenerate_cgo\",\"Title\":\"Regenerate cgo\",\"Doc\":\"regenerate_cgo regenerates cgo definitions.\\n\"},{\"Lens\":\"test\",\"Title\":\"Run test(s)\",\"Doc\":\"test runs `go test` for a specific test function.\\n\"},{\"Lens\":\"tidy\",\"Title\":\"Run go mod tidy\",\"Doc\":\"tidy runs `go mod tidy` for a module.\\n\"},{\"Lens\":\"upgrade_dependency\",\"Title\":\"Upgrade dependency\",\"Doc\":\"upgrade_dependency upgrades a dependency.\\n\"},{\"Lens\":\"vendor\",\"Title\":\"Run go mod vendor\",\"Doc\":\"vendor runs `go mod vendor` for a module.\\n\"},{\"Lens\":\"gc_details\",\"Title\":\"Toggle gc_details\",\"Doc\":\"gc_details controls calculation of gc annotations.\\n\"}]}"
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 9140f4e..0ce5fb7 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -313,6 +313,10 @@
// progress reports for all work done outside the scope of an RPC.
VerboseWorkDoneProgress bool
+ // SemanticTokens controls whether the LSP server will send
+ // semantic tokens to the client.
+ SemanticTokens bool
+
// ExpandWorkspaceToModule instructs `gopls` to expand the scope of the workspace to include the
// modules containing the workspace folders. Set this to false to avoid loading
// your entire module. This is particularly useful for those working in a monorepo.
@@ -680,6 +684,9 @@
case "gofumpt":
result.setBool(&o.Gofumpt)
+ case "semantictokens":
+ result.setBool(&o.SemanticTokens)
+
case "expandWorkspaceToModule":
result.setBool(&o.ExpandWorkspaceToModule)