github.com/jd-ly/tools@v0.5.7/internal/lsp/source/options.go (about)

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