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