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  }