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