2019-09-05 22:17:36 -06:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package source
|
|
|
|
|
2019-09-09 11:04:12 -06:00
|
|
|
import (
|
2019-09-09 07:28:25 -06:00
|
|
|
"fmt"
|
2019-09-09 11:04:12 -06:00
|
|
|
"os"
|
2019-09-14 10:29:08 -06:00
|
|
|
"time"
|
2019-09-09 11:04:12 -06:00
|
|
|
|
2019-10-10 18:48:16 -06:00
|
|
|
"golang.org/x/tools/go/analysis"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/asmdecl"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/assign"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/atomic"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/atomicalign"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/bools"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/buildtag"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/cgocall"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/composite"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/copylock"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/httpresponse"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/loopclosure"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/lostcancel"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/nilfunc"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/printf"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/shift"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/sortslice"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/stdmethods"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/structtag"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/tests"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/unmarshal"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/unreachable"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/unsafeptr"
|
|
|
|
"golang.org/x/tools/go/analysis/passes/unusedresult"
|
2019-10-01 16:06:10 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/diff"
|
2019-10-01 16:08:04 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/diff/myers"
|
2019-09-09 11:04:12 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
2019-09-09 07:28:25 -06:00
|
|
|
"golang.org/x/tools/internal/telemetry/tag"
|
|
|
|
errors "golang.org/x/xerrors"
|
2019-09-09 11:04:12 -06:00
|
|
|
)
|
2019-09-05 22:17:36 -06:00
|
|
|
|
|
|
|
var (
|
2019-09-11 11:13:44 -06:00
|
|
|
DefaultOptions = Options{
|
|
|
|
Env: os.Environ(),
|
2019-09-09 07:28:25 -06:00
|
|
|
TextDocumentSyncKind: protocol.Incremental,
|
|
|
|
HoverKind: SynopsisDocumentation,
|
|
|
|
InsertTextFormat: protocol.PlainTextTextFormat,
|
2019-12-06 00:44:16 -07:00
|
|
|
PreferredContentFormat: protocol.Markdown,
|
2019-09-05 22:17:36 -06:00
|
|
|
SupportedCodeActions: map[FileKind]map[protocol.CodeActionKind]bool{
|
|
|
|
Go: {
|
|
|
|
protocol.SourceOrganizeImports: true,
|
|
|
|
protocol.QuickFix: true,
|
|
|
|
},
|
2019-09-18 23:21:54 -06:00
|
|
|
Mod: {
|
|
|
|
protocol.SourceOrganizeImports: true,
|
|
|
|
},
|
2019-09-05 22:17:36 -06:00
|
|
|
Sum: {},
|
|
|
|
},
|
2019-09-18 23:21:54 -06:00
|
|
|
SupportedCommands: []string{
|
|
|
|
"tidy", // for go.mod files
|
|
|
|
},
|
2019-09-05 22:17:36 -06:00
|
|
|
Completion: CompletionOptions{
|
|
|
|
Documentation: true,
|
2019-09-12 11:53:18 -06:00
|
|
|
Deep: true,
|
|
|
|
FuzzyMatching: true,
|
2019-12-06 15:29:09 -07:00
|
|
|
Literal: true,
|
2019-09-14 10:29:08 -06:00
|
|
|
Budget: 100 * time.Millisecond,
|
2019-09-05 22:17:36 -06:00
|
|
|
},
|
2019-10-01 16:08:04 -06:00
|
|
|
ComputeEdits: myers.ComputeEdits,
|
2019-10-10 18:48:16 -06:00
|
|
|
Analyzers: defaultAnalyzers,
|
2019-10-31 16:06:25 -06:00
|
|
|
GoDiff: true,
|
2019-11-28 00:00:44 -07:00
|
|
|
LinkTarget: "pkg.go.dev",
|
2019-09-05 22:17:36 -06:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
type Options struct {
|
|
|
|
// Env is the current set of environment overrides on this view.
|
|
|
|
Env []string
|
|
|
|
|
|
|
|
// BuildFlags is used to adjust the build flags applied to the view.
|
|
|
|
BuildFlags []string
|
|
|
|
|
2019-09-05 22:17:36 -06:00
|
|
|
HoverKind HoverKind
|
|
|
|
DisabledAnalyses map[string]struct{}
|
|
|
|
|
2019-06-12 20:04:42 -06:00
|
|
|
StaticCheck bool
|
2019-10-10 18:48:16 -06:00
|
|
|
GoDiff bool
|
2019-06-12 20:04:42 -06:00
|
|
|
|
2019-09-05 22:17:36 -06:00
|
|
|
WatchFileChanges bool
|
|
|
|
InsertTextFormat protocol.InsertTextFormat
|
|
|
|
ConfigurationSupported bool
|
|
|
|
DynamicConfigurationSupported bool
|
|
|
|
DynamicWatchedFilesSupported bool
|
|
|
|
PreferredContentFormat protocol.MarkupKind
|
|
|
|
LineFoldingOnly bool
|
|
|
|
|
|
|
|
SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool
|
|
|
|
|
2019-09-18 23:21:54 -06:00
|
|
|
SupportedCommands []string
|
|
|
|
|
2019-09-09 07:28:25 -06:00
|
|
|
// TODO: Remove the option once we are certain there are no issues here.
|
2019-09-05 22:17:36 -06:00
|
|
|
TextDocumentSyncKind protocol.TextDocumentSyncKind
|
|
|
|
|
|
|
|
Completion CompletionOptions
|
2019-10-01 16:06:10 -06:00
|
|
|
|
|
|
|
ComputeEdits diff.ComputeEdits
|
2019-10-10 18:48:16 -06:00
|
|
|
|
2019-11-21 16:55:49 -07:00
|
|
|
Analyzers map[string]*analysis.Analyzer
|
2019-11-01 14:19:19 -06:00
|
|
|
|
|
|
|
// LocalPrefix is used to specify goimports's -local behavior.
|
|
|
|
LocalPrefix string
|
2019-11-03 19:09:58 -07:00
|
|
|
|
|
|
|
VerboseOutput bool
|
2019-11-28 00:00:44 -07:00
|
|
|
|
|
|
|
LinkTarget string
|
2019-09-05 22:17:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type CompletionOptions struct {
|
|
|
|
Deep bool
|
|
|
|
FuzzyMatching bool
|
2019-09-26 05:59:06 -06:00
|
|
|
CaseSensitive bool
|
2019-09-05 22:17:36 -06:00
|
|
|
Unimported bool
|
|
|
|
Documentation bool
|
|
|
|
FullDocumentation bool
|
2019-09-04 11:23:14 -06:00
|
|
|
Placeholders bool
|
2019-12-06 15:29:09 -07:00
|
|
|
Literal bool
|
2019-09-14 10:29:08 -06:00
|
|
|
|
|
|
|
// Budget is the soft latency goal for completion requests. Most
|
|
|
|
// requests finish in a couple milliseconds, but in some cases deep
|
|
|
|
// completions can take much longer. As we use up our budget we
|
|
|
|
// dynamically reduce the search scope to ensure we return timely
|
2019-10-24 22:57:53 -06:00
|
|
|
// results. Zero means unlimited.
|
2019-09-14 10:29:08 -06:00
|
|
|
Budget time.Duration
|
2019-09-05 22:17:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type HoverKind int
|
|
|
|
|
|
|
|
const (
|
|
|
|
SingleLine = HoverKind(iota)
|
|
|
|
NoDocumentation
|
|
|
|
SynopsisDocumentation
|
|
|
|
FullDocumentation
|
|
|
|
|
|
|
|
// structured is an experimental setting that returns a structured hover format.
|
|
|
|
// This format separates the signature from the documentation, so that the client
|
|
|
|
// can do more manipulation of these fields.
|
|
|
|
//
|
|
|
|
// This should only be used by clients that support this behavior.
|
|
|
|
Structured
|
|
|
|
)
|
2019-09-09 07:28:25 -06:00
|
|
|
|
|
|
|
type OptionResults []OptionResult
|
|
|
|
|
|
|
|
type OptionResult struct {
|
|
|
|
Name string
|
|
|
|
Value interface{}
|
|
|
|
Error error
|
2019-09-11 11:13:44 -06:00
|
|
|
|
|
|
|
State OptionState
|
|
|
|
Replacement string
|
2019-09-09 07:28:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type OptionState int
|
|
|
|
|
|
|
|
const (
|
|
|
|
OptionHandled = OptionState(iota)
|
|
|
|
OptionDeprecated
|
|
|
|
OptionUnexpected
|
|
|
|
)
|
|
|
|
|
2019-11-28 00:00:44 -07:00
|
|
|
type LinkTarget string
|
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
func SetOptions(options *Options, opts interface{}) OptionResults {
|
2019-09-09 07:28:25 -06:00
|
|
|
var results OptionResults
|
|
|
|
switch opts := opts.(type) {
|
|
|
|
case nil:
|
|
|
|
case map[string]interface{}:
|
|
|
|
for name, value := range opts {
|
|
|
|
results = append(results, options.set(name, value))
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
results = append(results, OptionResult{
|
|
|
|
Value: opts,
|
|
|
|
Error: errors.Errorf("Invalid options type %T", opts),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) {
|
2019-09-09 07:28:25 -06:00
|
|
|
// Check if the client supports snippets in completion items.
|
2019-11-17 12:29:15 -07:00
|
|
|
if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport {
|
2019-09-09 07:28:25 -06:00
|
|
|
o.InsertTextFormat = protocol.SnippetTextFormat
|
|
|
|
}
|
|
|
|
// Check if the client supports configuration messages.
|
|
|
|
o.ConfigurationSupported = caps.Workspace.Configuration
|
|
|
|
o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration
|
|
|
|
o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration
|
|
|
|
|
|
|
|
// Check which types of content format are supported by this client.
|
2019-11-17 12:29:15 -07:00
|
|
|
if hover := caps.TextDocument.Hover; len(hover.ContentFormat) > 0 {
|
2019-09-24 15:51:00 -06:00
|
|
|
o.PreferredContentFormat = hover.ContentFormat[0]
|
2019-09-09 07:28:25 -06:00
|
|
|
}
|
|
|
|
// Check if the client supports only line folding.
|
2019-11-17 12:29:15 -07:00
|
|
|
fr := caps.TextDocument.FoldingRange
|
|
|
|
o.LineFoldingOnly = fr.LineFoldingOnly
|
2019-09-09 07:28:25 -06:00
|
|
|
}
|
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
func (o *Options) set(name string, value interface{}) OptionResult {
|
2019-09-09 07:28:25 -06:00
|
|
|
result := OptionResult{Name: name, Value: value}
|
|
|
|
switch name {
|
2019-09-11 11:13:44 -06:00
|
|
|
case "env":
|
|
|
|
menv, ok := value.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
result.errorf("invalid config gopls.env type %T", value)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
for k, v := range menv {
|
|
|
|
o.Env = append(o.Env, fmt.Sprintf("%s=%s", k, v))
|
|
|
|
}
|
|
|
|
|
|
|
|
case "buildFlags":
|
|
|
|
iflags, ok := value.([]interface{})
|
|
|
|
if !ok {
|
|
|
|
result.errorf("invalid config gopls.buildFlags type %T", value)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
flags := make([]string, 0, len(iflags))
|
|
|
|
for _, flag := range iflags {
|
|
|
|
flags = append(flags, fmt.Sprintf("%s", flag))
|
|
|
|
}
|
|
|
|
o.BuildFlags = flags
|
|
|
|
|
2019-09-09 07:28:25 -06:00
|
|
|
case "noIncrementalSync":
|
|
|
|
if v, ok := result.asBool(); ok && v {
|
|
|
|
o.TextDocumentSyncKind = protocol.Full
|
|
|
|
}
|
|
|
|
case "watchFileChanges":
|
|
|
|
result.setBool(&o.WatchFileChanges)
|
2019-09-11 11:13:44 -06:00
|
|
|
case "completionDocumentation":
|
2019-09-09 07:28:25 -06:00
|
|
|
result.setBool(&o.Completion.Documentation)
|
|
|
|
case "usePlaceholders":
|
|
|
|
result.setBool(&o.Completion.Placeholders)
|
2019-09-11 11:13:44 -06:00
|
|
|
case "deepCompletion":
|
|
|
|
result.setBool(&o.Completion.Deep)
|
|
|
|
case "fuzzyMatching":
|
|
|
|
result.setBool(&o.Completion.FuzzyMatching)
|
2019-09-26 05:59:06 -06:00
|
|
|
case "caseSensitiveCompletion":
|
|
|
|
result.setBool(&o.Completion.CaseSensitive)
|
2019-09-11 11:13:44 -06:00
|
|
|
case "completeUnimported":
|
2019-09-09 07:28:25 -06:00
|
|
|
result.setBool(&o.Completion.Unimported)
|
2019-12-16 17:35:09 -07:00
|
|
|
case "completionTimeout":
|
|
|
|
if v, ok := result.asInt(); ok {
|
|
|
|
o.Completion.Budget = time.Duration(v) * time.Second
|
|
|
|
}
|
2019-09-09 07:28:25 -06:00
|
|
|
|
|
|
|
case "hoverKind":
|
|
|
|
hoverKind, ok := value.(string)
|
|
|
|
if !ok {
|
2019-11-01 14:19:19 -06:00
|
|
|
result.errorf("invalid type %T for string option %q", value, name)
|
2019-09-09 07:28:25 -06:00
|
|
|
break
|
|
|
|
}
|
|
|
|
switch hoverKind {
|
|
|
|
case "NoDocumentation":
|
|
|
|
o.HoverKind = NoDocumentation
|
|
|
|
case "SingleLine":
|
|
|
|
o.HoverKind = SingleLine
|
|
|
|
case "SynopsisDocumentation":
|
|
|
|
o.HoverKind = SynopsisDocumentation
|
|
|
|
case "FullDocumentation":
|
|
|
|
o.HoverKind = FullDocumentation
|
|
|
|
case "Structured":
|
|
|
|
o.HoverKind = Structured
|
|
|
|
default:
|
|
|
|
result.errorf("Unsupported hover kind", tag.Of("HoverKind", hoverKind))
|
|
|
|
}
|
|
|
|
|
2019-11-28 00:00:44 -07:00
|
|
|
case "linkTarget":
|
|
|
|
linkTarget, ok := value.(string)
|
|
|
|
if !ok {
|
|
|
|
result.errorf("invalid type %T for string option %q", value, name)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
o.LinkTarget = linkTarget
|
|
|
|
|
2019-09-09 07:28:25 -06:00
|
|
|
case "experimentalDisabledAnalyses":
|
|
|
|
disabledAnalyses, ok := value.([]interface{})
|
|
|
|
if !ok {
|
|
|
|
result.errorf("Invalid type %T for []string option %q", value, name)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
o.DisabledAnalyses = make(map[string]struct{})
|
|
|
|
for _, a := range disabledAnalyses {
|
|
|
|
o.DisabledAnalyses[fmt.Sprint(a)] = struct{}{}
|
|
|
|
}
|
|
|
|
|
2019-06-12 20:04:42 -06:00
|
|
|
case "staticcheck":
|
|
|
|
result.setBool(&o.StaticCheck)
|
|
|
|
|
2019-10-10 18:48:16 -06:00
|
|
|
case "go-diff":
|
|
|
|
result.setBool(&o.GoDiff)
|
|
|
|
|
2019-11-01 14:19:19 -06:00
|
|
|
case "local":
|
|
|
|
localPrefix, ok := value.(string)
|
|
|
|
if !ok {
|
|
|
|
result.errorf("invalid type %T for string option %q", value, name)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
o.LocalPrefix = localPrefix
|
|
|
|
|
2019-11-03 19:09:58 -07:00
|
|
|
case "verboseOutput":
|
|
|
|
result.setBool(&o.VerboseOutput)
|
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
// Deprecated settings.
|
2019-09-09 07:28:25 -06:00
|
|
|
case "wantSuggestedFixes":
|
|
|
|
result.State = OptionDeprecated
|
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
case "disableDeepCompletion":
|
|
|
|
result.State = OptionDeprecated
|
|
|
|
result.Replacement = "deepCompletion"
|
2019-09-09 07:28:25 -06:00
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
case "disableFuzzyMatching":
|
|
|
|
result.State = OptionDeprecated
|
|
|
|
result.Replacement = "fuzzyMatching"
|
2019-09-09 07:28:25 -06:00
|
|
|
|
2019-09-11 11:13:44 -06:00
|
|
|
case "wantCompletionDocumentation":
|
|
|
|
result.State = OptionDeprecated
|
|
|
|
result.Replacement = "completionDocumentation"
|
|
|
|
|
|
|
|
case "wantUnimportedCompletions":
|
|
|
|
result.State = OptionDeprecated
|
|
|
|
result.Replacement = "completeUnimported"
|
2019-09-09 07:28:25 -06:00
|
|
|
|
|
|
|
default:
|
|
|
|
result.State = OptionUnexpected
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *OptionResult) errorf(msg string, values ...interface{}) {
|
|
|
|
r.Error = errors.Errorf(msg, values...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *OptionResult) asBool() (bool, bool) {
|
|
|
|
b, ok := r.Value.(bool)
|
|
|
|
if !ok {
|
|
|
|
r.errorf("Invalid type %T for bool option %q", r.Value, r.Name)
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
return b, true
|
|
|
|
}
|
|
|
|
|
2019-12-16 17:35:09 -07:00
|
|
|
func (r *OptionResult) asInt() (int, bool) {
|
|
|
|
b, ok := r.Value.(int)
|
|
|
|
if !ok {
|
|
|
|
r.errorf("Invalid type %T for int option %q", r.Value, r.Name)
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
return b, true
|
|
|
|
}
|
|
|
|
|
2019-09-09 07:28:25 -06:00
|
|
|
func (r *OptionResult) setBool(b *bool) {
|
|
|
|
if v, ok := r.asBool(); ok {
|
|
|
|
*b = v
|
|
|
|
}
|
|
|
|
}
|
2019-10-10 18:48:16 -06:00
|
|
|
|
2019-11-21 16:55:49 -07:00
|
|
|
var defaultAnalyzers = map[string]*analysis.Analyzer{
|
2019-10-10 18:48:16 -06:00
|
|
|
// The traditional vet suite:
|
2019-11-21 16:55:49 -07:00
|
|
|
asmdecl.Analyzer.Name: asmdecl.Analyzer,
|
|
|
|
assign.Analyzer.Name: assign.Analyzer,
|
|
|
|
atomic.Analyzer.Name: atomic.Analyzer,
|
|
|
|
atomicalign.Analyzer.Name: atomicalign.Analyzer,
|
|
|
|
bools.Analyzer.Name: bools.Analyzer,
|
|
|
|
buildtag.Analyzer.Name: buildtag.Analyzer,
|
|
|
|
cgocall.Analyzer.Name: cgocall.Analyzer,
|
|
|
|
composite.Analyzer.Name: composite.Analyzer,
|
|
|
|
copylock.Analyzer.Name: copylock.Analyzer,
|
|
|
|
httpresponse.Analyzer.Name: httpresponse.Analyzer,
|
|
|
|
loopclosure.Analyzer.Name: loopclosure.Analyzer,
|
|
|
|
lostcancel.Analyzer.Name: lostcancel.Analyzer,
|
|
|
|
nilfunc.Analyzer.Name: nilfunc.Analyzer,
|
|
|
|
printf.Analyzer.Name: printf.Analyzer,
|
|
|
|
shift.Analyzer.Name: shift.Analyzer,
|
|
|
|
stdmethods.Analyzer.Name: stdmethods.Analyzer,
|
|
|
|
structtag.Analyzer.Name: structtag.Analyzer,
|
|
|
|
tests.Analyzer.Name: tests.Analyzer,
|
|
|
|
unmarshal.Analyzer.Name: unmarshal.Analyzer,
|
|
|
|
unreachable.Analyzer.Name: unreachable.Analyzer,
|
|
|
|
unsafeptr.Analyzer.Name: unsafeptr.Analyzer,
|
|
|
|
unusedresult.Analyzer.Name: unusedresult.Analyzer,
|
|
|
|
|
2019-10-10 18:48:16 -06:00
|
|
|
// Non-vet analyzers
|
2019-11-21 16:55:49 -07:00
|
|
|
sortslice.Analyzer.Name: sortslice.Analyzer,
|
2019-10-10 18:48:16 -06:00
|
|
|
}
|