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 5package source 6 7import ( 8 "fmt" 9 "os" 10 "time" 11 12 "golang.org/x/tools/go/analysis" 13 "golang.org/x/tools/go/analysis/passes/asmdecl" 14 "golang.org/x/tools/go/analysis/passes/assign" 15 "golang.org/x/tools/go/analysis/passes/atomic" 16 "golang.org/x/tools/go/analysis/passes/atomicalign" 17 "golang.org/x/tools/go/analysis/passes/bools" 18 "golang.org/x/tools/go/analysis/passes/buildtag" 19 "golang.org/x/tools/go/analysis/passes/cgocall" 20 "golang.org/x/tools/go/analysis/passes/composite" 21 "golang.org/x/tools/go/analysis/passes/copylock" 22 "golang.org/x/tools/go/analysis/passes/httpresponse" 23 "golang.org/x/tools/go/analysis/passes/loopclosure" 24 "golang.org/x/tools/go/analysis/passes/lostcancel" 25 "golang.org/x/tools/go/analysis/passes/nilfunc" 26 "golang.org/x/tools/go/analysis/passes/printf" 27 "golang.org/x/tools/go/analysis/passes/shift" 28 "golang.org/x/tools/go/analysis/passes/sortslice" 29 "golang.org/x/tools/go/analysis/passes/stdmethods" 30 "golang.org/x/tools/go/analysis/passes/structtag" 31 "golang.org/x/tools/go/analysis/passes/tests" 32 "golang.org/x/tools/go/analysis/passes/unmarshal" 33 "golang.org/x/tools/go/analysis/passes/unreachable" 34 "golang.org/x/tools/go/analysis/passes/unsafeptr" 35 "golang.org/x/tools/go/analysis/passes/unusedresult" 36 "golang.org/x/tools/internal/lsp/diff" 37 "golang.org/x/tools/internal/lsp/diff/myers" 38 "golang.org/x/tools/internal/lsp/protocol" 39 "golang.org/x/tools/internal/telemetry/tag" 40 errors "golang.org/x/xerrors" 41) 42 43var ( 44 DefaultOptions = Options{ 45 Env: os.Environ(), 46 TextDocumentSyncKind: protocol.Incremental, 47 HoverKind: SynopsisDocumentation, 48 InsertTextFormat: protocol.PlainTextTextFormat, 49 PreferredContentFormat: protocol.PlainText, 50 SupportedCodeActions: map[FileKind]map[protocol.CodeActionKind]bool{ 51 Go: { 52 protocol.SourceOrganizeImports: true, 53 protocol.QuickFix: true, 54 }, 55 Mod: { 56 protocol.SourceOrganizeImports: true, 57 }, 58 Sum: {}, 59 }, 60 SupportedCommands: []string{ 61 "tidy", // for go.mod files 62 }, 63 Completion: CompletionOptions{ 64 Documentation: true, 65 Deep: true, 66 FuzzyMatching: true, 67 Budget: 100 * time.Millisecond, 68 }, 69 ComputeEdits: myers.ComputeEdits, 70 Analyzers: defaultAnalyzers, 71 GoDiff: true, 72 } 73) 74 75type Options struct { 76 // Env is the current set of environment overrides on this view. 77 Env []string 78 79 // BuildFlags is used to adjust the build flags applied to the view. 80 BuildFlags []string 81 82 HoverKind HoverKind 83 DisabledAnalyses map[string]struct{} 84 85 StaticCheck bool 86 GoDiff bool 87 88 WatchFileChanges bool 89 InsertTextFormat protocol.InsertTextFormat 90 ConfigurationSupported bool 91 DynamicConfigurationSupported bool 92 DynamicWatchedFilesSupported bool 93 PreferredContentFormat protocol.MarkupKind 94 LineFoldingOnly bool 95 96 SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool 97 98 SupportedCommands []string 99 100 // TODO: Remove the option once we are certain there are no issues here. 101 TextDocumentSyncKind protocol.TextDocumentSyncKind 102 103 Completion CompletionOptions 104 105 ComputeEdits diff.ComputeEdits 106 107 Analyzers []*analysis.Analyzer 108 109 // LocalPrefix is used to specify goimports's -local behavior. 110 LocalPrefix string 111 112 VerboseOutput bool 113} 114 115type CompletionOptions struct { 116 Deep bool 117 FuzzyMatching bool 118 CaseSensitive bool 119 Unimported bool 120 Documentation bool 121 FullDocumentation bool 122 Placeholders bool 123 124 // Budget is the soft latency goal for completion requests. Most 125 // requests finish in a couple milliseconds, but in some cases deep 126 // completions can take much longer. As we use up our budget we 127 // dynamically reduce the search scope to ensure we return timely 128 // results. Zero means unlimited. 129 Budget time.Duration 130} 131 132type HoverKind int 133 134const ( 135 SingleLine = HoverKind(iota) 136 NoDocumentation 137 SynopsisDocumentation 138 FullDocumentation 139 140 // structured is an experimental setting that returns a structured hover format. 141 // This format separates the signature from the documentation, so that the client 142 // can do more manipulation of these fields. 143 // 144 // This should only be used by clients that support this behavior. 145 Structured 146) 147 148type OptionResults []OptionResult 149 150type OptionResult struct { 151 Name string 152 Value interface{} 153 Error error 154 155 State OptionState 156 Replacement string 157} 158 159type OptionState int 160 161const ( 162 OptionHandled = OptionState(iota) 163 OptionDeprecated 164 OptionUnexpected 165) 166 167func SetOptions(options *Options, opts interface{}) OptionResults { 168 var results OptionResults 169 switch opts := opts.(type) { 170 case nil: 171 case map[string]interface{}: 172 for name, value := range opts { 173 results = append(results, options.set(name, value)) 174 } 175 default: 176 results = append(results, OptionResult{ 177 Value: opts, 178 Error: errors.Errorf("Invalid options type %T", opts), 179 }) 180 } 181 return results 182} 183 184func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) { 185 // Check if the client supports snippets in completion items. 186 if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport { 187 o.InsertTextFormat = protocol.SnippetTextFormat 188 } 189 // Check if the client supports configuration messages. 190 o.ConfigurationSupported = caps.Workspace.Configuration 191 o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration 192 o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration 193 194 // Check which types of content format are supported by this client. 195 if hover := caps.TextDocument.Hover; len(hover.ContentFormat) > 0 { 196 o.PreferredContentFormat = hover.ContentFormat[0] 197 } 198 // Check if the client supports only line folding. 199 fr := caps.TextDocument.FoldingRange 200 o.LineFoldingOnly = fr.LineFoldingOnly 201} 202 203func (o *Options) set(name string, value interface{}) OptionResult { 204 result := OptionResult{Name: name, Value: value} 205 switch name { 206 case "env": 207 menv, ok := value.(map[string]interface{}) 208 if !ok { 209 result.errorf("invalid config gopls.env type %T", value) 210 break 211 } 212 for k, v := range menv { 213 o.Env = append(o.Env, fmt.Sprintf("%s=%s", k, v)) 214 } 215 216 case "buildFlags": 217 iflags, ok := value.([]interface{}) 218 if !ok { 219 result.errorf("invalid config gopls.buildFlags type %T", value) 220 break 221 } 222 flags := make([]string, 0, len(iflags)) 223 for _, flag := range iflags { 224 flags = append(flags, fmt.Sprintf("%s", flag)) 225 } 226 o.BuildFlags = flags 227 228 case "noIncrementalSync": 229 if v, ok := result.asBool(); ok && v { 230 o.TextDocumentSyncKind = protocol.Full 231 } 232 case "watchFileChanges": 233 result.setBool(&o.WatchFileChanges) 234 case "completionDocumentation": 235 result.setBool(&o.Completion.Documentation) 236 case "usePlaceholders": 237 result.setBool(&o.Completion.Placeholders) 238 case "deepCompletion": 239 result.setBool(&o.Completion.Deep) 240 case "fuzzyMatching": 241 result.setBool(&o.Completion.FuzzyMatching) 242 case "caseSensitiveCompletion": 243 result.setBool(&o.Completion.CaseSensitive) 244 case "completeUnimported": 245 result.setBool(&o.Completion.Unimported) 246 247 case "hoverKind": 248 hoverKind, ok := value.(string) 249 if !ok { 250 result.errorf("invalid type %T for string option %q", value, name) 251 break 252 } 253 switch hoverKind { 254 case "NoDocumentation": 255 o.HoverKind = NoDocumentation 256 case "SingleLine": 257 o.HoverKind = SingleLine 258 case "SynopsisDocumentation": 259 o.HoverKind = SynopsisDocumentation 260 case "FullDocumentation": 261 o.HoverKind = FullDocumentation 262 case "Structured": 263 o.HoverKind = Structured 264 default: 265 result.errorf("Unsupported hover kind", tag.Of("HoverKind", hoverKind)) 266 } 267 268 case "experimentalDisabledAnalyses": 269 disabledAnalyses, ok := value.([]interface{}) 270 if !ok { 271 result.errorf("Invalid type %T for []string option %q", value, name) 272 break 273 } 274 o.DisabledAnalyses = make(map[string]struct{}) 275 for _, a := range disabledAnalyses { 276 o.DisabledAnalyses[fmt.Sprint(a)] = struct{}{} 277 } 278 279 case "staticcheck": 280 result.setBool(&o.StaticCheck) 281 282 case "go-diff": 283 result.setBool(&o.GoDiff) 284 285 case "local": 286 localPrefix, ok := value.(string) 287 if !ok { 288 result.errorf("invalid type %T for string option %q", value, name) 289 break 290 } 291 o.LocalPrefix = localPrefix 292 293 case "verboseOutput": 294 result.setBool(&o.VerboseOutput) 295 296 // Deprecated settings. 297 case "wantSuggestedFixes": 298 result.State = OptionDeprecated 299 300 case "disableDeepCompletion": 301 result.State = OptionDeprecated 302 result.Replacement = "deepCompletion" 303 304 case "disableFuzzyMatching": 305 result.State = OptionDeprecated 306 result.Replacement = "fuzzyMatching" 307 308 case "wantCompletionDocumentation": 309 result.State = OptionDeprecated 310 result.Replacement = "completionDocumentation" 311 312 case "wantUnimportedCompletions": 313 result.State = OptionDeprecated 314 result.Replacement = "completeUnimported" 315 316 default: 317 result.State = OptionUnexpected 318 } 319 return result 320} 321 322func (r *OptionResult) errorf(msg string, values ...interface{}) { 323 r.Error = errors.Errorf(msg, values...) 324} 325 326func (r *OptionResult) asBool() (bool, bool) { 327 b, ok := r.Value.(bool) 328 if !ok { 329 r.errorf("Invalid type %T for bool option %q", r.Value, r.Name) 330 return false, false 331 } 332 return b, true 333} 334 335func (r *OptionResult) setBool(b *bool) { 336 if v, ok := r.asBool(); ok { 337 *b = v 338 } 339} 340 341var defaultAnalyzers = []*analysis.Analyzer{ 342 // The traditional vet suite: 343 asmdecl.Analyzer, 344 assign.Analyzer, 345 atomic.Analyzer, 346 atomicalign.Analyzer, 347 bools.Analyzer, 348 buildtag.Analyzer, 349 cgocall.Analyzer, 350 composite.Analyzer, 351 copylock.Analyzer, 352 httpresponse.Analyzer, 353 loopclosure.Analyzer, 354 lostcancel.Analyzer, 355 nilfunc.Analyzer, 356 printf.Analyzer, 357 shift.Analyzer, 358 stdmethods.Analyzer, 359 structtag.Analyzer, 360 tests.Analyzer, 361 unmarshal.Analyzer, 362 unreachable.Analyzer, 363 unsafeptr.Analyzer, 364 unusedresult.Analyzer, 365 // Non-vet analyzers 366 sortslice.Analyzer, 367} 368