github.com/jd-ly/tools@v0.5.7/internal/lsp/source/options.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package source 6 7 import ( 8 "context" 9 "fmt" 10 "regexp" 11 "strings" 12 "sync" 13 "time" 14 15 "github.com/jd-ly/tools/go/analysis" 16 "github.com/jd-ly/tools/go/analysis/passes/asmdecl" 17 "github.com/jd-ly/tools/go/analysis/passes/assign" 18 "github.com/jd-ly/tools/go/analysis/passes/atomic" 19 "github.com/jd-ly/tools/go/analysis/passes/atomicalign" 20 "github.com/jd-ly/tools/go/analysis/passes/bools" 21 "github.com/jd-ly/tools/go/analysis/passes/buildtag" 22 "github.com/jd-ly/tools/go/analysis/passes/cgocall" 23 "github.com/jd-ly/tools/go/analysis/passes/composite" 24 "github.com/jd-ly/tools/go/analysis/passes/copylock" 25 "github.com/jd-ly/tools/go/analysis/passes/deepequalerrors" 26 "github.com/jd-ly/tools/go/analysis/passes/errorsas" 27 "github.com/jd-ly/tools/go/analysis/passes/fieldalignment" 28 "github.com/jd-ly/tools/go/analysis/passes/httpresponse" 29 "github.com/jd-ly/tools/go/analysis/passes/ifaceassert" 30 "github.com/jd-ly/tools/go/analysis/passes/loopclosure" 31 "github.com/jd-ly/tools/go/analysis/passes/lostcancel" 32 "github.com/jd-ly/tools/go/analysis/passes/nilfunc" 33 "github.com/jd-ly/tools/go/analysis/passes/printf" 34 "github.com/jd-ly/tools/go/analysis/passes/shift" 35 "github.com/jd-ly/tools/go/analysis/passes/sortslice" 36 "github.com/jd-ly/tools/go/analysis/passes/stdmethods" 37 "github.com/jd-ly/tools/go/analysis/passes/stringintconv" 38 "github.com/jd-ly/tools/go/analysis/passes/structtag" 39 "github.com/jd-ly/tools/go/analysis/passes/testinggoroutine" 40 "github.com/jd-ly/tools/go/analysis/passes/tests" 41 "github.com/jd-ly/tools/go/analysis/passes/unmarshal" 42 "github.com/jd-ly/tools/go/analysis/passes/unreachable" 43 "github.com/jd-ly/tools/go/analysis/passes/unsafeptr" 44 "github.com/jd-ly/tools/go/analysis/passes/unusedresult" 45 "github.com/jd-ly/tools/internal/lsp/analysis/fillreturns" 46 "github.com/jd-ly/tools/internal/lsp/analysis/fillstruct" 47 "github.com/jd-ly/tools/internal/lsp/analysis/nonewvars" 48 "github.com/jd-ly/tools/internal/lsp/analysis/noresultvalues" 49 "github.com/jd-ly/tools/internal/lsp/analysis/simplifycompositelit" 50 "github.com/jd-ly/tools/internal/lsp/analysis/simplifyrange" 51 "github.com/jd-ly/tools/internal/lsp/analysis/simplifyslice" 52 "github.com/jd-ly/tools/internal/lsp/analysis/undeclaredname" 53 "github.com/jd-ly/tools/internal/lsp/analysis/unusedparams" 54 "github.com/jd-ly/tools/internal/lsp/diff" 55 "github.com/jd-ly/tools/internal/lsp/diff/myers" 56 "github.com/jd-ly/tools/internal/lsp/protocol" 57 errors "golang.org/x/xerrors" 58 ) 59 60 var ( 61 optionsOnce sync.Once 62 defaultOptions *Options 63 ) 64 65 //go:generate go run github.com/jd-ly/tools/internal/lsp/source/genapijson -output api_json.go 66 67 // DefaultOptions is the options that are used for Gopls execution independent 68 // of any externally provided configuration (LSP initialization, command 69 // invokation, etc.). 70 func DefaultOptions() *Options { 71 optionsOnce.Do(func() { 72 var commands []string 73 for _, c := range Commands { 74 commands = append(commands, c.ID()) 75 } 76 defaultOptions = &Options{ 77 ClientOptions: ClientOptions{ 78 InsertTextFormat: protocol.PlainTextTextFormat, 79 PreferredContentFormat: protocol.Markdown, 80 ConfigurationSupported: true, 81 DynamicConfigurationSupported: true, 82 DynamicWatchedFilesSupported: true, 83 LineFoldingOnly: false, 84 HierarchicalDocumentSymbolSupport: true, 85 }, 86 ServerOptions: ServerOptions{ 87 SupportedCodeActions: map[FileKind]map[protocol.CodeActionKind]bool{ 88 Go: { 89 protocol.SourceFixAll: true, 90 protocol.SourceOrganizeImports: true, 91 protocol.QuickFix: true, 92 protocol.RefactorRewrite: true, 93 protocol.RefactorExtract: true, 94 }, 95 Mod: { 96 protocol.SourceOrganizeImports: true, 97 }, 98 Sum: {}, 99 }, 100 SupportedCommands: commands, 101 }, 102 UserOptions: UserOptions{ 103 HoverKind: FullDocumentation, 104 LinkTarget: "pkg.go.dev", 105 Codelenses: map[string]bool{ 106 CommandGenerate.Name: true, 107 CommandRegenerateCgo.Name: true, 108 CommandTidy.Name: true, 109 CommandToggleDetails.Name: false, 110 CommandUpgradeDependency.Name: true, 111 CommandVendor.Name: true, 112 }, 113 LinksInHover: true, 114 ImportShortcut: Both, 115 Matcher: Fuzzy, 116 SymbolMatcher: SymbolFuzzy, 117 SymbolStyle: DynamicSymbols, 118 }, 119 DebuggingOptions: DebuggingOptions{ 120 CompletionBudget: 100 * time.Millisecond, 121 }, 122 ExperimentalOptions: ExperimentalOptions{ 123 ExpandWorkspaceToModule: true, 124 ExperimentalPackageCacheKey: true, 125 ExperimentalDiagnosticsDelay: 250 * time.Millisecond, 126 }, 127 InternalOptions: InternalOptions{ 128 LiteralCompletions: true, 129 TempModfile: true, 130 CompleteUnimported: true, 131 CompletionDocumentation: true, 132 DeepCompletion: true, 133 }, 134 Hooks: Hooks{ 135 ComputeEdits: myers.ComputeEdits, 136 URLRegexp: urlRegexp(), 137 DefaultAnalyzers: defaultAnalyzers(), 138 TypeErrorAnalyzers: typeErrorAnalyzers(), 139 ConvenienceAnalyzers: convenienceAnalyzers(), 140 StaticcheckAnalyzers: map[string]Analyzer{}, 141 GoDiff: true, 142 }, 143 } 144 }) 145 return defaultOptions 146 } 147 148 // Options holds various configuration that affects Gopls execution, organized 149 // by the nature or origin of the settings. 150 type Options struct { 151 ClientOptions 152 ServerOptions 153 UserOptions 154 DebuggingOptions 155 ExperimentalOptions 156 InternalOptions 157 Hooks 158 } 159 160 // ClientOptions holds LSP-specific configuration that is provided by the 161 // client. 162 type ClientOptions struct { 163 InsertTextFormat protocol.InsertTextFormat 164 ConfigurationSupported bool 165 DynamicConfigurationSupported bool 166 DynamicWatchedFilesSupported bool 167 PreferredContentFormat protocol.MarkupKind 168 LineFoldingOnly bool 169 HierarchicalDocumentSymbolSupport bool 170 SemanticTypes []string 171 SemanticMods []string 172 } 173 174 // ServerOptions holds LSP-specific configuration that is provided by the 175 // server. 176 type ServerOptions struct { 177 SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool 178 SupportedCommands []string 179 } 180 181 // UserOptions holds custom Gopls configuration (not part of the LSP) that is 182 // modified by the client. 183 type UserOptions struct { 184 // BuildFlags is the set of flags passed on to the build system when invoked. 185 // It is applied to queries like `go list`, which is used when discovering files. 186 // The most common use is to set `-tags`. 187 BuildFlags []string 188 189 // Env adds environment variables to external commands run by `gopls`, most notably `go list`. 190 Env map[string]string 191 192 // HoverKind controls the information that appears in the hover text. 193 // SingleLine and Structured are intended for use only by authors of editor plugins. 194 HoverKind HoverKind 195 196 // Placeholders enables placeholders for function parameters or struct fields in completion responses. 197 UsePlaceholders bool 198 199 // LinkTarget controls where documentation links go. 200 // It might be one of: 201 // 202 // * `"godoc.org"` 203 // * `"pkg.go.dev"` 204 // 205 // If company chooses to use its own `godoc.org`, its address can be used as well. 206 LinkTarget string 207 208 // Local is the equivalent of the `goimports -local` flag, which puts imports beginning with this string after 3rd-party packages. 209 // It should be the prefix of the import path whose imports should be grouped separately. 210 Local string 211 212 // Gofumpt indicates if we should run gofumpt formatting. 213 Gofumpt bool 214 215 // Analyses specify analyses that the user would like to enable or disable. 216 // A map of the names of analysis passes that should be enabled/disabled. 217 // A full list of analyzers that gopls uses can be found [here](analyzers.md) 218 // 219 // Example Usage: 220 // ```json5 221 // ... 222 // "analyses": { 223 // "unreachable": false, // Disable the unreachable analyzer. 224 // "unusedparams": true // Enable the unusedparams analyzer. 225 // } 226 // ... 227 // ``` 228 Analyses map[string]bool 229 230 // Codelenses overrides the enabled/disabled state of code lenses. See the "Code Lenses" 231 // section of settings.md for the list of supported lenses. 232 // 233 // Example Usage: 234 // ```json5 235 // "gopls": { 236 // ... 237 // "codelens": { 238 // "generate": false, // Don't show the `go generate` lens. 239 // "gc_details": true // Show a code lens toggling the display of gc's choices. 240 // } 241 // ... 242 // } 243 // ``` 244 Codelenses map[string]bool 245 246 // LinksInHover toggles the presence of links to documentation in hover. 247 LinksInHover bool 248 249 // ImportShortcut specifies whether import statements should link to 250 // documentation or go to definitions. 251 ImportShortcut ImportShortcut 252 253 // Matcher sets the algorithm that is used when calculating completion candidates. 254 Matcher Matcher 255 256 // SymbolMatcher sets the algorithm that is used when finding workspace symbols. 257 SymbolMatcher SymbolMatcher 258 259 // SymbolStyle controls how symbols are qualified in symbol responses. 260 // 261 // Example Usage: 262 // ```json5 263 // "gopls": { 264 // ... 265 // "symbolStyle": "dynamic", 266 // ... 267 // } 268 // ``` 269 SymbolStyle SymbolStyle 270 } 271 272 // EnvSlice returns Env as a slice of k=v strings. 273 func (u *UserOptions) EnvSlice() []string { 274 var result []string 275 for k, v := range u.Env { 276 result = append(result, fmt.Sprintf("%v=%v", k, v)) 277 } 278 return result 279 } 280 281 // SetEnvSlice sets Env from a slice of k=v strings. 282 func (u *UserOptions) SetEnvSlice(env []string) { 283 u.Env = map[string]string{} 284 for _, kv := range env { 285 split := strings.SplitN(kv, "=", 2) 286 if len(split) != 2 { 287 continue 288 } 289 u.Env[split[0]] = split[1] 290 } 291 } 292 293 // Hooks contains configuration that is provided to the Gopls command by the 294 // main package. 295 type Hooks struct { 296 GoDiff bool 297 ComputeEdits diff.ComputeEdits 298 URLRegexp *regexp.Regexp 299 GofumptFormat func(ctx context.Context, src []byte) ([]byte, error) 300 DefaultAnalyzers map[string]Analyzer 301 TypeErrorAnalyzers map[string]Analyzer 302 ConvenienceAnalyzers map[string]Analyzer 303 StaticcheckAnalyzers map[string]Analyzer 304 } 305 306 // ExperimentalOptions defines configuration for features under active 307 // development. WARNING: This configuration will be changed in the future. It 308 // only exists while these features are under development. 309 type ExperimentalOptions struct { 310 311 // Annotations suppress various kinds of optimization diagnostics 312 // that would be reported by the gc_details command. 313 // * noNilcheck suppresses display of nilchecks. 314 // * noEscape suppresses escape choices. 315 // * noInline suppresses inlining choices. 316 // * noBounds suppresses bounds checking diagnostics. 317 Annotations map[string]bool 318 319 // Staticcheck enables additional analyses from staticcheck.io. 320 Staticcheck bool 321 322 // SemanticTokens controls whether the LSP server will send 323 // semantic tokens to the client. 324 SemanticTokens bool 325 326 // ExpandWorkspaceToModule instructs `gopls` to adjust the scope of the 327 // workspace to find the best available module root. `gopls` first looks for 328 // a go.mod file in any parent directory of the workspace folder, expanding 329 // the scope to that directory if it exists. If no viable parent directory is 330 // found, gopls will check if there is exactly one child directory containing 331 // a go.mod file, narrowing the scope to that directory if it exists. 332 ExpandWorkspaceToModule bool 333 334 // ExperimentalWorkspaceModule opts a user into the experimental support 335 // for multi-module workspaces. 336 ExperimentalWorkspaceModule bool 337 338 // ExperimentalDiagnosticsDelay controls the amount of time that gopls waits 339 // after the most recent file modification before computing deep diagnostics. 340 // Simple diagnostics (parsing and type-checking) are always run immediately 341 // on recently modified packages. 342 // 343 // This option must be set to a valid duration string, for example `"250ms"`. 344 ExperimentalDiagnosticsDelay time.Duration 345 346 // ExperimentalPackageCacheKey controls whether to use a coarser cache key 347 // for package type information to increase cache hits. This setting removes 348 // the user's environment, build flags, and working directory from the cache 349 // key, which should be a safe change as all relevant inputs into the type 350 // checking pass are already hashed into the key. This is temporarily guarded 351 // by an experiment because caching behavior is subtle and difficult to 352 // comprehensively test. 353 ExperimentalPackageCacheKey bool 354 355 // AllowModfileModifications disables -mod=readonly, allowing imports from 356 // out-of-scope modules. This option will eventually be removed. 357 AllowModfileModifications bool 358 359 // AllowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module 360 // downloads rather than requiring user action. This option will eventually 361 // be removed. 362 AllowImplicitNetworkAccess bool 363 } 364 365 // DebuggingOptions should not affect the logical execution of Gopls, but may 366 // be altered for debugging purposes. 367 type DebuggingOptions struct { 368 // VerboseOutput enables additional debug logging. 369 VerboseOutput bool 370 371 // CompletionBudget is the soft latency goal for completion requests. Most 372 // requests finish in a couple milliseconds, but in some cases deep 373 // completions can take much longer. As we use up our budget we 374 // dynamically reduce the search scope to ensure we return timely 375 // results. Zero means unlimited. 376 CompletionBudget time.Duration 377 } 378 379 // InternalOptions contains settings that are not intended for use by the 380 // average user. These may be settings used by tests or outdated settings that 381 // will soon be deprecated. Some of these settings may not even be configurable 382 // by the user. 383 type InternalOptions struct { 384 // LiteralCompletions controls whether literal candidates such as 385 // "&someStruct{}" are offered. Tests disable this flag to simplify 386 // their expected values. 387 LiteralCompletions bool 388 389 // VerboseWorkDoneProgress controls whether the LSP server should send 390 // progress reports for all work done outside the scope of an RPC. 391 // Used by the regression tests. 392 VerboseWorkDoneProgress bool 393 394 // The following options were previously available to users, but they 395 // really shouldn't be configured by anyone other than "power users". 396 397 // CompletionDocumentation enables documentation with completion results. 398 CompletionDocumentation bool 399 400 // CompleteUnimported enables completion for packages that you do not 401 // currently import. 402 CompleteUnimported bool 403 404 // DeepCompletion enables the ability to return completions from deep 405 // inside relevant entities, rather than just the locally accessible ones. 406 // 407 // Consider this example: 408 // 409 // ```go 410 // package main 411 // 412 // import "fmt" 413 // 414 // type wrapString struct { 415 // str string 416 // } 417 // 418 // func main() { 419 // x := wrapString{"hello world"} 420 // fmt.Printf(<>) 421 // } 422 // ``` 423 // 424 // At the location of the `<>` in this program, deep completion would suggest the result `x.str`. 425 DeepCompletion bool 426 427 // TempModfile controls the use of the -modfile flag in Go 1.14. 428 TempModfile bool 429 } 430 431 type ImportShortcut string 432 433 const ( 434 Both ImportShortcut = "Both" 435 Link ImportShortcut = "Link" 436 Definition ImportShortcut = "Definition" 437 ) 438 439 func (s ImportShortcut) ShowLinks() bool { 440 return s == Both || s == Link 441 } 442 443 func (s ImportShortcut) ShowDefinition() bool { 444 return s == Both || s == Definition 445 } 446 447 type Matcher string 448 449 const ( 450 Fuzzy Matcher = "Fuzzy" 451 CaseInsensitive Matcher = "CaseInsensitive" 452 CaseSensitive Matcher = "CaseSensitive" 453 ) 454 455 type SymbolMatcher string 456 457 const ( 458 SymbolFuzzy SymbolMatcher = "Fuzzy" 459 SymbolCaseInsensitive SymbolMatcher = "CaseInsensitive" 460 SymbolCaseSensitive SymbolMatcher = "CaseSensitive" 461 ) 462 463 type SymbolStyle string 464 465 const ( 466 // PackageQualifiedSymbols is package qualified symbols i.e. 467 // "pkg.Foo.Field". 468 PackageQualifiedSymbols SymbolStyle = "Package" 469 // FullyQualifiedSymbols is fully qualified symbols, i.e. 470 // "path/to/pkg.Foo.Field". 471 FullyQualifiedSymbols SymbolStyle = "Full" 472 // DynamicSymbols uses whichever qualifier results in the highest scoring 473 // match for the given symbol query. Here a "qualifier" is any "/" or "." 474 // delimited suffix of the fully qualified symbol. i.e. "to/pkg.Foo.Field" or 475 // just "Foo.Field". 476 DynamicSymbols SymbolStyle = "Dynamic" 477 ) 478 479 type HoverKind string 480 481 const ( 482 SingleLine HoverKind = "SingleLine" 483 NoDocumentation HoverKind = "NoDocumentation" 484 SynopsisDocumentation HoverKind = "SynopsisDocumentation" 485 FullDocumentation HoverKind = "FullDocumentation" 486 487 // Structured is an experimental setting that returns a structured hover format. 488 // This format separates the signature from the documentation, so that the client 489 // can do more manipulation of these fields. 490 // 491 // This should only be used by clients that support this behavior. 492 Structured HoverKind = "Structured" 493 ) 494 495 type OptionResults []OptionResult 496 497 type OptionResult struct { 498 Name string 499 Value interface{} 500 Error error 501 502 State OptionState 503 Replacement string 504 } 505 506 type OptionState int 507 508 const ( 509 OptionHandled = OptionState(iota) 510 OptionDeprecated 511 OptionUnexpected 512 ) 513 514 type LinkTarget string 515 516 func SetOptions(options *Options, opts interface{}) OptionResults { 517 var results OptionResults 518 switch opts := opts.(type) { 519 case nil: 520 case map[string]interface{}: 521 // If the user's settings contains "allExperiments", set that first, 522 // and then let them override individual settings independently. 523 var enableExperiments bool 524 for name, value := range opts { 525 if b, ok := value.(bool); name == "allExperiments" && ok && b { 526 enableExperiments = true 527 options.enableAllExperiments() 528 } 529 } 530 for name, value := range opts { 531 results = append(results, options.set(name, value)) 532 } 533 // Finally, enable any experimental features that are specified in 534 // maps, which allows users to individually toggle them on or off. 535 if enableExperiments { 536 options.enableAllExperimentMaps() 537 } 538 default: 539 results = append(results, OptionResult{ 540 Value: opts, 541 Error: errors.Errorf("Invalid options type %T", opts), 542 }) 543 } 544 return results 545 } 546 547 func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) { 548 // Check if the client supports snippets in completion items. 549 if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport { 550 o.InsertTextFormat = protocol.SnippetTextFormat 551 } 552 // Check if the client supports configuration messages. 553 o.ConfigurationSupported = caps.Workspace.Configuration 554 o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration 555 o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration 556 557 // Check which types of content format are supported by this client. 558 if hover := caps.TextDocument.Hover; len(hover.ContentFormat) > 0 { 559 o.PreferredContentFormat = hover.ContentFormat[0] 560 } 561 // Check if the client supports only line folding. 562 fr := caps.TextDocument.FoldingRange 563 o.LineFoldingOnly = fr.LineFoldingOnly 564 // Check if the client supports hierarchical document symbols. 565 o.HierarchicalDocumentSymbolSupport = caps.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport 566 // Check if the client supports semantic tokens 567 o.SemanticTypes = caps.TextDocument.SemanticTokens.TokenTypes 568 o.SemanticMods = caps.TextDocument.SemanticTokens.TokenModifiers 569 // we don't need Requests, as we support full functionality 570 // we don't need Formats, as there is only one, for now 571 572 } 573 574 func (o *Options) Clone() *Options { 575 result := &Options{ 576 ClientOptions: o.ClientOptions, 577 DebuggingOptions: o.DebuggingOptions, 578 ExperimentalOptions: o.ExperimentalOptions, 579 InternalOptions: o.InternalOptions, 580 Hooks: Hooks{ 581 GoDiff: o.Hooks.GoDiff, 582 ComputeEdits: o.Hooks.ComputeEdits, 583 GofumptFormat: o.GofumptFormat, 584 URLRegexp: o.URLRegexp, 585 }, 586 ServerOptions: o.ServerOptions, 587 UserOptions: o.UserOptions, 588 } 589 // Fully clone any slice or map fields. Only Hooks, ExperimentalOptions, 590 // and UserOptions can be modified. 591 copyStringMap := func(src map[string]bool) map[string]bool { 592 dst := make(map[string]bool) 593 for k, v := range src { 594 dst[k] = v 595 } 596 return dst 597 } 598 result.Analyses = copyStringMap(o.Analyses) 599 result.Annotations = copyStringMap(o.Annotations) 600 result.Codelenses = copyStringMap(o.Codelenses) 601 602 copySlice := func(src []string) []string { 603 dst := make([]string, len(src)) 604 copy(dst, src) 605 return dst 606 } 607 result.SetEnvSlice(o.EnvSlice()) 608 result.BuildFlags = copySlice(o.BuildFlags) 609 610 copyAnalyzerMap := func(src map[string]Analyzer) map[string]Analyzer { 611 dst := make(map[string]Analyzer) 612 for k, v := range src { 613 dst[k] = v 614 } 615 return dst 616 } 617 result.DefaultAnalyzers = copyAnalyzerMap(o.DefaultAnalyzers) 618 result.TypeErrorAnalyzers = copyAnalyzerMap(o.TypeErrorAnalyzers) 619 result.ConvenienceAnalyzers = copyAnalyzerMap(o.ConvenienceAnalyzers) 620 result.StaticcheckAnalyzers = copyAnalyzerMap(o.StaticcheckAnalyzers) 621 return result 622 } 623 624 func (o *Options) AddStaticcheckAnalyzer(a *analysis.Analyzer) { 625 o.StaticcheckAnalyzers[a.Name] = Analyzer{Analyzer: a, Enabled: true} 626 } 627 628 // enableAllExperiments turns on all of the experimental "off-by-default" 629 // features offered by gopls. Any experimental features specified in maps 630 // should be enabled in enableAllExperimentMaps. 631 func (o *Options) enableAllExperiments() { 632 // There are currently no experimental features in development. 633 } 634 635 func (o *Options) enableAllExperimentMaps() { 636 if _, ok := o.Codelenses[CommandToggleDetails.Name]; !ok { 637 o.Codelenses[CommandToggleDetails.Name] = true 638 } 639 if _, ok := o.Analyses[unusedparams.Analyzer.Name]; !ok { 640 o.Analyses[unusedparams.Analyzer.Name] = true 641 } 642 } 643 644 func (o *Options) set(name string, value interface{}) OptionResult { 645 result := OptionResult{Name: name, Value: value} 646 switch name { 647 case "env": 648 menv, ok := value.(map[string]interface{}) 649 if !ok { 650 result.errorf("invalid type %T, expect map", value) 651 break 652 } 653 for k, v := range menv { 654 o.Env[k] = fmt.Sprint(v) 655 } 656 657 case "buildFlags": 658 iflags, ok := value.([]interface{}) 659 if !ok { 660 result.errorf("invalid type %T, expect list", value) 661 break 662 } 663 flags := make([]string, 0, len(iflags)) 664 for _, flag := range iflags { 665 flags = append(flags, fmt.Sprintf("%s", flag)) 666 } 667 o.BuildFlags = flags 668 669 case "completionDocumentation": 670 result.setBool(&o.CompletionDocumentation) 671 case "usePlaceholders": 672 result.setBool(&o.UsePlaceholders) 673 case "deepCompletion": 674 result.setBool(&o.DeepCompletion) 675 case "completeUnimported": 676 result.setBool(&o.CompleteUnimported) 677 case "completionBudget": 678 result.setDuration(&o.CompletionBudget) 679 case "matcher": 680 if s, ok := result.asOneOf( 681 string(Fuzzy), 682 string(CaseSensitive), 683 string(CaseInsensitive), 684 ); ok { 685 o.Matcher = Matcher(s) 686 } 687 688 case "symbolMatcher": 689 if s, ok := result.asOneOf( 690 string(SymbolFuzzy), 691 string(SymbolCaseInsensitive), 692 string(SymbolCaseSensitive), 693 ); ok { 694 o.SymbolMatcher = SymbolMatcher(s) 695 } 696 697 case "symbolStyle": 698 if s, ok := result.asOneOf( 699 string(FullyQualifiedSymbols), 700 string(PackageQualifiedSymbols), 701 string(DynamicSymbols), 702 ); ok { 703 o.SymbolStyle = SymbolStyle(s) 704 } 705 706 case "hoverKind": 707 if s, ok := result.asOneOf( 708 string(NoDocumentation), 709 string(SingleLine), 710 string(SynopsisDocumentation), 711 string(FullDocumentation), 712 string(Structured), 713 ); ok { 714 o.HoverKind = HoverKind(s) 715 } 716 717 case "linkTarget": 718 result.setString(&o.LinkTarget) 719 720 case "linksInHover": 721 result.setBool(&o.LinksInHover) 722 723 case "importShortcut": 724 if s, ok := result.asOneOf(string(Both), string(Link), string(Definition)); ok { 725 o.ImportShortcut = ImportShortcut(s) 726 } 727 728 case "analyses": 729 result.setBoolMap(&o.Analyses) 730 731 case "annotations": 732 result.setBoolMap(&o.Annotations) 733 for k := range o.Annotations { 734 switch k { 735 case "noEscape", "noNilcheck", "noInline", "noBounds": 736 continue 737 default: 738 result.Name += ":" + k // put mistake(s) in the message 739 result.State = OptionUnexpected 740 } 741 } 742 743 case "codelenses", "codelens": 744 var lensOverrides map[string]bool 745 result.setBoolMap(&lensOverrides) 746 if result.Error == nil { 747 if o.Codelenses == nil { 748 o.Codelenses = make(map[string]bool) 749 } 750 for lens, enabled := range lensOverrides { 751 o.Codelenses[lens] = enabled 752 } 753 } 754 755 // codelens is deprecated, but still works for now. 756 // TODO(rstambler): Remove this for the gopls/v0.7.0 release. 757 if name == "codelens" { 758 result.State = OptionDeprecated 759 result.Replacement = "codelenses" 760 } 761 762 case "staticcheck": 763 result.setBool(&o.Staticcheck) 764 765 case "local": 766 result.setString(&o.Local) 767 768 case "verboseOutput": 769 result.setBool(&o.VerboseOutput) 770 771 case "verboseWorkDoneProgress": 772 result.setBool(&o.VerboseWorkDoneProgress) 773 774 case "tempModfile": 775 result.setBool(&o.TempModfile) 776 777 case "gofumpt": 778 result.setBool(&o.Gofumpt) 779 780 case "semanticTokens": 781 result.setBool(&o.SemanticTokens) 782 783 case "expandWorkspaceToModule": 784 result.setBool(&o.ExpandWorkspaceToModule) 785 786 case "experimentalWorkspaceModule": 787 result.setBool(&o.ExperimentalWorkspaceModule) 788 789 case "experimentalDiagnosticsDelay": 790 result.setDuration(&o.ExperimentalDiagnosticsDelay) 791 792 case "experimentalPackageCacheKey": 793 result.setBool(&o.ExperimentalPackageCacheKey) 794 795 case "allowModfileModifications": 796 result.setBool(&o.AllowModfileModifications) 797 798 case "allowImplicitNetworkAccess": 799 result.setBool(&o.AllowImplicitNetworkAccess) 800 801 case "allExperiments": 802 // This setting should be handled before all of the other options are 803 // processed, so do nothing here. 804 805 // Replaced settings. 806 case "experimentalDisabledAnalyses": 807 result.State = OptionDeprecated 808 result.Replacement = "analyses" 809 810 case "disableDeepCompletion": 811 result.State = OptionDeprecated 812 result.Replacement = "deepCompletion" 813 814 case "disableFuzzyMatching": 815 result.State = OptionDeprecated 816 result.Replacement = "fuzzyMatching" 817 818 case "wantCompletionDocumentation": 819 result.State = OptionDeprecated 820 result.Replacement = "completionDocumentation" 821 822 case "wantUnimportedCompletions": 823 result.State = OptionDeprecated 824 result.Replacement = "completeUnimported" 825 826 case "fuzzyMatching": 827 result.State = OptionDeprecated 828 result.Replacement = "matcher" 829 830 case "caseSensitiveCompletion": 831 result.State = OptionDeprecated 832 result.Replacement = "matcher" 833 834 // Deprecated settings. 835 case "wantSuggestedFixes": 836 result.State = OptionDeprecated 837 838 case "noIncrementalSync": 839 result.State = OptionDeprecated 840 841 case "watchFileChanges": 842 result.State = OptionDeprecated 843 844 case "go-diff": 845 result.State = OptionDeprecated 846 847 default: 848 result.State = OptionUnexpected 849 } 850 return result 851 } 852 853 func (r *OptionResult) errorf(msg string, values ...interface{}) { 854 prefix := fmt.Sprintf("parsing setting %q: ", r.Name) 855 r.Error = errors.Errorf(prefix+msg, values...) 856 } 857 858 func (r *OptionResult) asBool() (bool, bool) { 859 b, ok := r.Value.(bool) 860 if !ok { 861 r.errorf("invalid type %T, expect bool", r.Value) 862 return false, false 863 } 864 return b, true 865 } 866 867 func (r *OptionResult) setBool(b *bool) { 868 if v, ok := r.asBool(); ok { 869 *b = v 870 } 871 } 872 873 func (r *OptionResult) setDuration(d *time.Duration) { 874 if v, ok := r.asString(); ok { 875 parsed, err := time.ParseDuration(v) 876 if err != nil { 877 r.errorf("failed to parse duration %q: %v", v, err) 878 return 879 } 880 *d = parsed 881 } 882 } 883 884 func (r *OptionResult) setBoolMap(bm *map[string]bool) { 885 all, ok := r.Value.(map[string]interface{}) 886 if !ok { 887 r.errorf("invalid type %T for map[string]bool option", r.Value) 888 return 889 } 890 m := make(map[string]bool) 891 for a, enabled := range all { 892 if enabled, ok := enabled.(bool); ok { 893 m[a] = enabled 894 } else { 895 r.errorf("invalid type %T for map key %q", enabled, a) 896 return 897 } 898 } 899 *bm = m 900 } 901 902 func (r *OptionResult) asString() (string, bool) { 903 b, ok := r.Value.(string) 904 if !ok { 905 r.errorf("invalid type %T, expect string", r.Value) 906 return "", false 907 } 908 return b, true 909 } 910 911 func (r *OptionResult) asOneOf(options ...string) (string, bool) { 912 s, ok := r.asString() 913 if !ok { 914 return "", false 915 } 916 lower := strings.ToLower(s) 917 for _, opt := range options { 918 if strings.ToLower(opt) == lower { 919 return opt, true 920 } 921 } 922 r.errorf("invalid option %q for enum", r.Value) 923 return "", false 924 } 925 926 func (r *OptionResult) setString(s *string) { 927 if v, ok := r.asString(); ok { 928 *s = v 929 } 930 } 931 932 // EnabledAnalyzers returns all of the analyzers enabled for the given 933 // snapshot. 934 func EnabledAnalyzers(snapshot Snapshot) (analyzers []Analyzer) { 935 for _, a := range snapshot.View().Options().DefaultAnalyzers { 936 if a.IsEnabled(snapshot.View()) { 937 analyzers = append(analyzers, a) 938 } 939 } 940 for _, a := range snapshot.View().Options().TypeErrorAnalyzers { 941 if a.IsEnabled(snapshot.View()) { 942 analyzers = append(analyzers, a) 943 } 944 } 945 for _, a := range snapshot.View().Options().ConvenienceAnalyzers { 946 if a.IsEnabled(snapshot.View()) { 947 analyzers = append(analyzers, a) 948 } 949 } 950 for _, a := range snapshot.View().Options().StaticcheckAnalyzers { 951 if a.IsEnabled(snapshot.View()) { 952 analyzers = append(analyzers, a) 953 } 954 } 955 return analyzers 956 } 957 958 func typeErrorAnalyzers() map[string]Analyzer { 959 return map[string]Analyzer{ 960 fillreturns.Analyzer.Name: { 961 Analyzer: fillreturns.Analyzer, 962 FixesError: fillreturns.FixesError, 963 HighConfidence: true, 964 Enabled: true, 965 }, 966 nonewvars.Analyzer.Name: { 967 Analyzer: nonewvars.Analyzer, 968 FixesError: nonewvars.FixesError, 969 Enabled: true, 970 }, 971 noresultvalues.Analyzer.Name: { 972 Analyzer: noresultvalues.Analyzer, 973 FixesError: noresultvalues.FixesError, 974 Enabled: true, 975 }, 976 undeclaredname.Analyzer.Name: { 977 Analyzer: undeclaredname.Analyzer, 978 FixesError: undeclaredname.FixesError, 979 Command: CommandUndeclaredName, 980 Enabled: true, 981 }, 982 } 983 } 984 985 func convenienceAnalyzers() map[string]Analyzer { 986 return map[string]Analyzer{ 987 fillstruct.Analyzer.Name: { 988 Analyzer: fillstruct.Analyzer, 989 Command: CommandFillStruct, 990 Enabled: true, 991 }, 992 } 993 } 994 995 func defaultAnalyzers() map[string]Analyzer { 996 return map[string]Analyzer{ 997 // The traditional vet suite: 998 asmdecl.Analyzer.Name: {Analyzer: asmdecl.Analyzer, Enabled: true}, 999 assign.Analyzer.Name: {Analyzer: assign.Analyzer, Enabled: true}, 1000 atomic.Analyzer.Name: {Analyzer: atomic.Analyzer, Enabled: true}, 1001 bools.Analyzer.Name: {Analyzer: bools.Analyzer, Enabled: true}, 1002 buildtag.Analyzer.Name: {Analyzer: buildtag.Analyzer, Enabled: true}, 1003 cgocall.Analyzer.Name: {Analyzer: cgocall.Analyzer, Enabled: true}, 1004 composite.Analyzer.Name: {Analyzer: composite.Analyzer, Enabled: true}, 1005 copylock.Analyzer.Name: {Analyzer: copylock.Analyzer, Enabled: true}, 1006 errorsas.Analyzer.Name: {Analyzer: errorsas.Analyzer, Enabled: true}, 1007 httpresponse.Analyzer.Name: {Analyzer: httpresponse.Analyzer, Enabled: true}, 1008 ifaceassert.Analyzer.Name: {Analyzer: ifaceassert.Analyzer, Enabled: true}, 1009 loopclosure.Analyzer.Name: {Analyzer: loopclosure.Analyzer, Enabled: true}, 1010 lostcancel.Analyzer.Name: {Analyzer: lostcancel.Analyzer, Enabled: true}, 1011 nilfunc.Analyzer.Name: {Analyzer: nilfunc.Analyzer, Enabled: true}, 1012 printf.Analyzer.Name: {Analyzer: printf.Analyzer, Enabled: true}, 1013 shift.Analyzer.Name: {Analyzer: shift.Analyzer, Enabled: true}, 1014 stdmethods.Analyzer.Name: {Analyzer: stdmethods.Analyzer, Enabled: true}, 1015 stringintconv.Analyzer.Name: {Analyzer: stringintconv.Analyzer, Enabled: true}, 1016 structtag.Analyzer.Name: {Analyzer: structtag.Analyzer, Enabled: true}, 1017 tests.Analyzer.Name: {Analyzer: tests.Analyzer, Enabled: true}, 1018 unmarshal.Analyzer.Name: {Analyzer: unmarshal.Analyzer, Enabled: true}, 1019 unreachable.Analyzer.Name: {Analyzer: unreachable.Analyzer, Enabled: true}, 1020 unsafeptr.Analyzer.Name: {Analyzer: unsafeptr.Analyzer, Enabled: true}, 1021 unusedresult.Analyzer.Name: {Analyzer: unusedresult.Analyzer, Enabled: true}, 1022 1023 // Non-vet analyzers: 1024 atomicalign.Analyzer.Name: {Analyzer: atomicalign.Analyzer, Enabled: true}, 1025 deepequalerrors.Analyzer.Name: {Analyzer: deepequalerrors.Analyzer, Enabled: true}, 1026 fieldalignment.Analyzer.Name: {Analyzer: fieldalignment.Analyzer, Enabled: false}, 1027 sortslice.Analyzer.Name: {Analyzer: sortslice.Analyzer, Enabled: true}, 1028 testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, Enabled: true}, 1029 unusedparams.Analyzer.Name: {Analyzer: unusedparams.Analyzer, Enabled: false}, 1030 1031 // gofmt -s suite: 1032 simplifycompositelit.Analyzer.Name: {Analyzer: simplifycompositelit.Analyzer, Enabled: true, HighConfidence: true}, 1033 simplifyrange.Analyzer.Name: {Analyzer: simplifyrange.Analyzer, Enabled: true, HighConfidence: true}, 1034 simplifyslice.Analyzer.Name: {Analyzer: simplifyslice.Analyzer, Enabled: true, HighConfidence: true}, 1035 } 1036 } 1037 1038 func urlRegexp() *regexp.Regexp { 1039 // Ensure links are matched as full words, not anywhere. 1040 re := regexp.MustCompile(`\b(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?\b`) 1041 re.Longest() 1042 return re 1043 } 1044 1045 type APIJSON struct { 1046 Options map[string][]*OptionJSON 1047 Commands []*CommandJSON 1048 Lenses []*LensJSON 1049 } 1050 1051 type OptionJSON struct { 1052 Name string 1053 Type string 1054 Doc string 1055 EnumValues []EnumValue 1056 Default string 1057 } 1058 1059 type EnumValue struct { 1060 Value string 1061 Doc string 1062 } 1063 1064 type CommandJSON struct { 1065 Command string 1066 Title string 1067 Doc string 1068 } 1069 1070 type LensJSON struct { 1071 Lens string 1072 Title string 1073 Doc string 1074 }