github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/source/options.go (about)

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