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