blob: 02e9f700efd794601eb7569eb77a2422c13f9a88 [file] [log] [blame]
Robert Findleyb15dac22022-08-30 14:40:12 -04001// 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 lsp
6
7import (
Robert Findleyb15dac22022-08-30 14:40:12 -04008 "context"
9 "encoding/json"
10 "fmt"
Rob Findley5ff5cbb2023-05-22 13:24:35 -040011 "go/build"
Robert Findleyb15dac22022-08-30 14:40:12 -040012 "log"
13 "os"
14 "path"
15 "path/filepath"
Robert Findley89b43352022-10-07 13:43:27 -040016 "sort"
17 "strings"
Robert Findleyb15dac22022-08-30 14:40:12 -040018 "sync"
19
Alan Donovan4baa3dc2023-04-25 10:21:06 -040020 "golang.org/x/tools/gopls/internal/bug"
Robert Findleyb15dac22022-08-30 14:40:12 -040021 "golang.org/x/tools/gopls/internal/lsp/debug"
22 "golang.org/x/tools/gopls/internal/lsp/protocol"
23 "golang.org/x/tools/gopls/internal/lsp/source"
Alan Donovan26a95e62022-10-07 10:40:32 -040024 "golang.org/x/tools/gopls/internal/span"
Hana (Hyang-Ah) Kim4b271f92023-07-19 14:18:26 -040025 "golang.org/x/tools/gopls/internal/telemetry"
Peter Weinberger9250e222022-08-16 11:12:59 -040026 "golang.org/x/tools/internal/event"
27 "golang.org/x/tools/internal/jsonrpc2"
Robert Findleyb15dac22022-08-30 14:40:12 -040028)
29
30func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
Robert Findleyf7963612023-03-28 21:34:10 -040031 ctx, done := event.Start(ctx, "lsp.Server.initialize")
32 defer done()
33
Hana (Hyang-Ah) Kim4b271f92023-07-19 14:18:26 -040034 telemetry.RecordClientInfo(params)
35
Robert Findleyb15dac22022-08-30 14:40:12 -040036 s.stateMu.Lock()
37 if s.state >= serverInitializing {
38 defer s.stateMu.Unlock()
39 return nil, fmt.Errorf("%w: initialize called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
40 }
41 s.state = serverInitializing
42 s.stateMu.Unlock()
43
44 // For uniqueness, use the gopls PID rather than params.ProcessID (the client
45 // pid). Some clients might start multiple gopls servers, though they
46 // probably shouldn't.
47 pid := os.Getpid()
48 s.tempDir = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.%s", pid, s.session.ID()))
49 err := os.Mkdir(s.tempDir, 0700)
50 if err != nil {
51 // MkdirTemp could fail due to permissions issues. This is a problem with
52 // the user's environment, but should not block gopls otherwise behaving.
53 // All usage of s.tempDir should be predicated on having a non-empty
54 // s.tempDir.
55 event.Error(ctx, "creating temp dir", err)
56 s.tempDir = ""
57 }
58 s.progress.SetSupportsWorkDoneProgress(params.Capabilities.Window.WorkDoneProgress)
59
Rob Findleyfb4bd112023-09-06 15:44:41 -040060 options := s.Options().Clone()
61 // TODO(rfindley): remove the error return from handleOptionResults, and
62 // eliminate this defer.
63 defer func() { s.SetOptions(options) }()
Robert Findleyb15dac22022-08-30 14:40:12 -040064
65 if err := s.handleOptionResults(ctx, source.SetOptions(options, params.InitializationOptions)); err != nil {
66 return nil, err
67 }
Rob Findley3a5dbf32023-05-21 11:03:28 -040068 options.ForClientCapabilities(params.ClientInfo, params.Capabilities)
Robert Findleyb15dac22022-08-30 14:40:12 -040069
70 if options.ShowBugReports {
71 // Report the next bug that occurs on the server.
Alan Donovane2f3b252023-04-25 15:21:16 -040072 bug.Handle(func(b bug.Bug) {
Robert Findleyb15dac22022-08-30 14:40:12 -040073 msg := &protocol.ShowMessageParams{
74 Type: protocol.Error,
75 Message: fmt.Sprintf("A bug occurred on the server: %s\nLocation:%s", b.Description, b.Key),
76 }
Alan Donovane2f3b252023-04-25 15:21:16 -040077 go func() {
78 if err := s.eventuallyShowMessage(context.Background(), msg); err != nil {
79 log.Printf("error showing bug: %v", err)
80 }
81 }()
82 })
Robert Findleyb15dac22022-08-30 14:40:12 -040083 }
84
85 folders := params.WorkspaceFolders
86 if len(folders) == 0 {
87 if params.RootURI != "" {
88 folders = []protocol.WorkspaceFolder{{
89 URI: string(params.RootURI),
90 Name: path.Base(params.RootURI.SpanURI().Filename()),
91 }}
92 }
93 }
94 for _, folder := range folders {
95 uri := span.URIFromURI(folder.URI)
96 if !uri.IsFile() {
97 continue
98 }
99 s.pendingFolders = append(s.pendingFolders, folder)
100 }
101 // gopls only supports URIs with a file:// scheme, so if we have no
102 // workspace folders with a supported scheme, fail to initialize.
103 if len(folders) > 0 && len(s.pendingFolders) == 0 {
104 return nil, fmt.Errorf("unsupported URI schemes: %v (gopls only supports file URIs)", folders)
105 }
106
107 var codeActionProvider interface{} = true
108 if ca := params.Capabilities.TextDocument.CodeAction; len(ca.CodeActionLiteralSupport.CodeActionKind.ValueSet) > 0 {
109 // If the client has specified CodeActionLiteralSupport,
110 // send the code actions we support.
111 //
112 // Using CodeActionOptions is only valid if codeActionLiteralSupport is set.
113 codeActionProvider = &protocol.CodeActionOptions{
114 CodeActionKinds: s.getSupportedCodeActions(),
115 }
116 }
117 var renameOpts interface{} = true
Peter Weinberger49420522023-03-01 14:46:20 -0500118 if r := params.Capabilities.TextDocument.Rename; r != nil && r.PrepareSupport {
Robert Findleyb15dac22022-08-30 14:40:12 -0400119 renameOpts = protocol.RenameOptions{
120 PrepareProvider: r.PrepareSupport,
121 }
122 }
123
124 versionInfo := debug.VersionInfo()
125
126 // golang/go#45732: Warn users who've installed sergi/go-diff@v1.2.0, since
127 // it will corrupt the formatting of their files.
128 for _, dep := range versionInfo.Deps {
129 if dep.Path == "github.com/sergi/go-diff" && dep.Version == "v1.2.0" {
130 if err := s.eventuallyShowMessage(ctx, &protocol.ShowMessageParams{
131 Message: `It looks like you have a bad gopls installation.
132Please reinstall gopls by running 'GO111MODULE=on go install golang.org/x/tools/gopls@latest'.
133See https://github.com/golang/go/issues/45732 for more information.`,
134 Type: protocol.Error,
135 }); err != nil {
136 return nil, err
137 }
138 }
139 }
140
141 goplsVersion, err := json.Marshal(versionInfo)
142 if err != nil {
143 return nil, err
144 }
145
146 return &protocol.InitializeResult{
147 Capabilities: protocol.ServerCapabilities{
Peter Weinbergere85b5332023-02-18 15:25:18 -0500148 CallHierarchyProvider: &protocol.Or_ServerCapabilities_callHierarchyProvider{Value: true},
Robert Findleyb15dac22022-08-30 14:40:12 -0400149 CodeActionProvider: codeActionProvider,
Suzy Mueller15782442022-09-19 14:13:01 -0400150 CodeLensProvider: &protocol.CodeLensOptions{}, // must be non-nil to enable the code lens capability
Peter Weinberger49420522023-03-01 14:46:20 -0500151 CompletionProvider: &protocol.CompletionOptions{
Robert Findleyb15dac22022-08-30 14:40:12 -0400152 TriggerCharacters: []string{"."},
153 },
Peter Weinbergere85b5332023-02-18 15:25:18 -0500154 DefinitionProvider: &protocol.Or_ServerCapabilities_definitionProvider{Value: true},
155 TypeDefinitionProvider: &protocol.Or_ServerCapabilities_typeDefinitionProvider{Value: true},
156 ImplementationProvider: &protocol.Or_ServerCapabilities_implementationProvider{Value: true},
157 DocumentFormattingProvider: &protocol.Or_ServerCapabilities_documentFormattingProvider{Value: true},
158 DocumentSymbolProvider: &protocol.Or_ServerCapabilities_documentSymbolProvider{Value: true},
159 WorkspaceSymbolProvider: &protocol.Or_ServerCapabilities_workspaceSymbolProvider{Value: true},
Peter Weinberger49420522023-03-01 14:46:20 -0500160 ExecuteCommandProvider: &protocol.ExecuteCommandOptions{
Peter Weinberger64920582023-04-08 18:42:05 -0400161 Commands: nonNilSliceString(options.SupportedCommands),
Robert Findleyb15dac22022-08-30 14:40:12 -0400162 },
Peter Weinbergere85b5332023-02-18 15:25:18 -0500163 FoldingRangeProvider: &protocol.Or_ServerCapabilities_foldingRangeProvider{Value: true},
164 HoverProvider: &protocol.Or_ServerCapabilities_hoverProvider{Value: true},
165 DocumentHighlightProvider: &protocol.Or_ServerCapabilities_documentHighlightProvider{Value: true},
Peter Weinberger49420522023-03-01 14:46:20 -0500166 DocumentLinkProvider: &protocol.DocumentLinkOptions{},
Robert Findleyb15dac22022-08-30 14:40:12 -0400167 InlayHintProvider: protocol.InlayHintOptions{},
Peter Weinbergere85b5332023-02-18 15:25:18 -0500168 ReferencesProvider: &protocol.Or_ServerCapabilities_referencesProvider{Value: true},
Robert Findleyb15dac22022-08-30 14:40:12 -0400169 RenameProvider: renameOpts,
Peter Weinbergere85b5332023-02-18 15:25:18 -0500170 SelectionRangeProvider: &protocol.Or_ServerCapabilities_selectionRangeProvider{Value: true},
Peter Weinbergerb5d65e02023-01-14 15:58:41 -0500171 SemanticTokensProvider: protocol.SemanticTokensOptions{
Peter Weinbergere85b5332023-02-18 15:25:18 -0500172 Range: &protocol.Or_SemanticTokensOptions_range{Value: true},
173 Full: &protocol.Or_SemanticTokensOptions_full{Value: true},
Peter Weinbergerb5d65e02023-01-14 15:58:41 -0500174 Legend: protocol.SemanticTokensLegend{
Rob Findleyfda3fe32023-10-09 09:33:23 -0400175 TokenTypes: nonNilSliceString(options.SemanticTypes),
176 TokenModifiers: nonNilSliceString(options.SemanticMods),
Peter Weinbergerb5d65e02023-01-14 15:58:41 -0500177 },
178 },
Peter Weinberger49420522023-03-01 14:46:20 -0500179 SignatureHelpProvider: &protocol.SignatureHelpOptions{
Robert Findleyb15dac22022-08-30 14:40:12 -0400180 TriggerCharacters: []string{"(", ","},
181 },
182 TextDocumentSync: &protocol.TextDocumentSyncOptions{
183 Change: protocol.Incremental,
184 OpenClose: true,
Peter Weinberger49420522023-03-01 14:46:20 -0500185 Save: &protocol.SaveOptions{
Robert Findleyb15dac22022-08-30 14:40:12 -0400186 IncludeText: false,
187 },
188 },
Peter Weinberger49420522023-03-01 14:46:20 -0500189 Workspace: &protocol.Workspace6Gn{
190 WorkspaceFolders: &protocol.WorkspaceFolders5Gn{
Robert Findleyb15dac22022-08-30 14:40:12 -0400191 Supported: true,
192 ChangeNotifications: "workspace/didChangeWorkspaceFolders",
193 },
194 },
195 },
Peter Weinberger49420522023-03-01 14:46:20 -0500196 ServerInfo: &protocol.PServerInfoMsg_initialize{
Robert Findleyb15dac22022-08-30 14:40:12 -0400197 Name: "gopls",
198 Version: string(goplsVersion),
199 },
200 }, nil
201}
202
203func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
Robert Findleyf7963612023-03-28 21:34:10 -0400204 ctx, done := event.Start(ctx, "lsp.Server.initialized")
205 defer done()
206
Robert Findleyb15dac22022-08-30 14:40:12 -0400207 s.stateMu.Lock()
208 if s.state >= serverInitialized {
209 defer s.stateMu.Unlock()
210 return fmt.Errorf("%w: initialized called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
211 }
212 s.state = serverInitialized
213 s.stateMu.Unlock()
214
215 for _, not := range s.notifications {
216 s.client.ShowMessage(ctx, not)
217 }
218 s.notifications = nil
219
Robert Findleyb15dac22022-08-30 14:40:12 -0400220 if err := s.addFolders(ctx, s.pendingFolders); err != nil {
221 return err
222 }
223 s.pendingFolders = nil
Robert Findley20c1ee72022-09-28 13:10:58 -0400224 s.checkViewGoVersions()
Robert Findleyb15dac22022-08-30 14:40:12 -0400225
226 var registrations []protocol.Registration
Rob Findleyfda3fe32023-10-09 09:33:23 -0400227 options := s.Options()
Robert Findleyb15dac22022-08-30 14:40:12 -0400228 if options.ConfigurationSupported && options.DynamicConfigurationSupported {
229 registrations = append(registrations, protocol.Registration{
230 ID: "workspace/didChangeConfiguration",
231 Method: "workspace/didChangeConfiguration",
232 })
233 }
Robert Findleyb15dac22022-08-30 14:40:12 -0400234 if len(registrations) > 0 {
235 if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
236 Registrations: registrations,
237 }); err != nil {
238 return err
239 }
240 }
Rob Findleya3c6fd82023-09-12 13:08:54 -0400241
242 // Ask (maybe) about enabling telemetry. Do this asynchronously, as it's OK
243 // for users to ignore or dismiss the question.
Hana (Hyang-Ah) Kim64beb952023-09-22 09:19:20 -0400244 go s.maybePromptForTelemetry(ctx, options.TelemetryPrompt)
Rob Findleya3c6fd82023-09-12 13:08:54 -0400245
Robert Findleyb15dac22022-08-30 14:40:12 -0400246 return nil
247}
248
Robert Findley42cb7be2022-10-26 18:57:08 -0400249// GoVersionTable maps Go versions to the gopls version in which support will
250// be deprecated, and the final gopls version supporting them without warnings.
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400251// Keep this in sync with gopls/README.md.
Robert Findley20c1ee72022-09-28 13:10:58 -0400252//
Robert Findley42cb7be2022-10-26 18:57:08 -0400253// Must be sorted in ascending order of Go version.
254//
255// Mutable for testing.
256var GoVersionTable = []GoVersionSupport{
257 {12, "", "v0.7.5"},
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400258 {15, "", "v0.9.5"},
259 {16, "v0.13.0", "v0.11.0"},
260 {17, "v0.13.0", "v0.11.0"},
Robert Findley42cb7be2022-10-26 18:57:08 -0400261}
262
263// GoVersionSupport holds information about end-of-life Go version support.
264type GoVersionSupport struct {
265 GoVersion int
266 DeprecatedVersion string // if unset, the version is already deprecated
267 InstallGoplsVersion string
268}
269
270// OldestSupportedGoVersion is the last X in Go 1.X that this version of gopls
271// supports.
272func OldestSupportedGoVersion() int {
273 return GoVersionTable[len(GoVersionTable)-1].GoVersion + 1
274}
275
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400276// versionMessage returns the warning/error message to display if the user has
277// the given Go version, if any. The goVersion variable is the X in Go 1.X. If
278// fromBuild is set, the Go version is the version used to build gopls.
279// Otherwise, it is the go command version.
Robert Findleye074ef82022-10-28 09:11:20 -0400280//
281// If goVersion is invalid (< 0), it returns "", 0.
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400282func versionMessage(goVersion int, fromBuild bool) (string, protocol.MessageType) {
Robert Findleye074ef82022-10-28 09:11:20 -0400283 if goVersion < 0 {
284 return "", 0
285 }
286
Robert Findley42cb7be2022-10-26 18:57:08 -0400287 for _, v := range GoVersionTable {
Robert Findleye074ef82022-10-28 09:11:20 -0400288 if goVersion <= v.GoVersion {
Robert Findley42cb7be2022-10-26 18:57:08 -0400289 var msgBuilder strings.Builder
290
291 mType := protocol.Error
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400292 if fromBuild {
293 fmt.Fprintf(&msgBuilder, "Gopls was built with Go version 1.%d", goVersion)
294 } else {
295 fmt.Fprintf(&msgBuilder, "Found Go version 1.%d", goVersion)
296 }
Robert Findley42cb7be2022-10-26 18:57:08 -0400297 if v.DeprecatedVersion != "" {
298 // not deprecated yet, just a warning
299 fmt.Fprintf(&msgBuilder, ", which will be unsupported by gopls %s. ", v.DeprecatedVersion)
300 mType = protocol.Warning
301 } else {
302 fmt.Fprint(&msgBuilder, ", which is not supported by this version of gopls. ")
303 }
304 fmt.Fprintf(&msgBuilder, "Please upgrade to Go 1.%d or later and reinstall gopls. ", OldestSupportedGoVersion())
305 fmt.Fprintf(&msgBuilder, "If you can't upgrade and want this message to go away, please install gopls %s. ", v.InstallGoplsVersion)
306 fmt.Fprint(&msgBuilder, "See https://go.dev/s/gopls-support-policy for more details.")
307
308 return msgBuilder.String(), mType
309 }
310 }
311 return "", 0
312}
Robert Findley20c1ee72022-09-28 13:10:58 -0400313
314// checkViewGoVersions checks whether any Go version used by a view is too old,
315// raising a showMessage notification if so.
316//
317// It should be called after views change.
318func (s *Server) checkViewGoVersions() {
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400319 oldestVersion, fromBuild := go1Point(), true
Robert Findley20c1ee72022-09-28 13:10:58 -0400320 for _, view := range s.session.Views() {
321 viewVersion := view.GoVersion()
322 if oldestVersion == -1 || viewVersion < oldestVersion {
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400323 oldestVersion, fromBuild = viewVersion, false
Robert Findley20c1ee72022-09-28 13:10:58 -0400324 }
Hana (Hyang-Ah) Kim33da5c02023-08-03 18:26:07 -0400325 telemetry.RecordViewGoVersion(viewVersion)
Robert Findley20c1ee72022-09-28 13:10:58 -0400326 }
327
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400328 if msg, mType := versionMessage(oldestVersion, fromBuild); msg != "" {
Robert Findley20c1ee72022-09-28 13:10:58 -0400329 s.eventuallyShowMessage(context.Background(), &protocol.ShowMessageParams{
Robert Findley42cb7be2022-10-26 18:57:08 -0400330 Type: mType,
Robert Findley20c1ee72022-09-28 13:10:58 -0400331 Message: msg,
332 })
333 }
334}
335
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400336// go1Point returns the x in Go 1.x. If an error occurs extracting the go
337// version, it returns -1.
338//
339// Copied from the testenv package.
340func go1Point() int {
341 for i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- {
342 var version int
343 if _, err := fmt.Sscanf(build.Default.ReleaseTags[i], "go1.%d", &version); err != nil {
344 continue
345 }
346 return version
347 }
348 return -1
349}
350
Robert Findleyb15dac22022-08-30 14:40:12 -0400351func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) error {
352 originalViews := len(s.session.Views())
353 viewErrors := make(map[span.URI]error)
354
Alan Donovan32e1cb72022-10-27 12:24:51 -0400355 var ndiagnose sync.WaitGroup // number of unfinished diagnose calls
Rob Findleyfb4bd112023-09-06 15:44:41 -0400356 if s.Options().VerboseWorkDoneProgress {
Robert Findleyb15dac22022-08-30 14:40:12 -0400357 work := s.progress.Start(ctx, DiagnosticWorkTitle(FromInitialWorkspaceLoad), "Calculating diagnostics for initial workspace load...", nil, nil)
358 defer func() {
359 go func() {
Alan Donovan32e1cb72022-10-27 12:24:51 -0400360 ndiagnose.Wait()
Robert Findleyb15dac22022-08-30 14:40:12 -0400361 work.End(ctx, "Done.")
362 }()
363 }()
364 }
365 // Only one view gets to have a workspace.
Alan Donovan32e1cb72022-10-27 12:24:51 -0400366 var nsnapshots sync.WaitGroup // number of unfinished snapshot initializations
Robert Findleyb15dac22022-08-30 14:40:12 -0400367 for _, folder := range folders {
368 uri := span.URIFromURI(folder.URI)
369 // Ignore non-file URIs.
370 if !uri.IsFile() {
371 continue
372 }
373 work := s.progress.Start(ctx, "Setting up workspace", "Loading packages...", nil, nil)
374 snapshot, release, err := s.addView(ctx, folder.Name, uri)
375 if err != nil {
376 if err == source.ErrViewExists {
377 continue
378 }
379 viewErrors[uri] = err
380 work.End(ctx, fmt.Sprintf("Error loading packages: %s", err))
381 continue
382 }
383 // Inv: release() must be called once.
384
Alan Donovan32e1cb72022-10-27 12:24:51 -0400385 // Initialize snapshot asynchronously.
386 initialized := make(chan struct{})
387 nsnapshots.Add(1)
388 go func() {
389 snapshot.AwaitInitialized(ctx)
390 work.End(ctx, "Finished loading packages.")
391 nsnapshots.Done()
392 close(initialized) // signal
393 }()
394
395 // Diagnose the newly created view asynchronously.
396 ndiagnose.Add(1)
Robert Findleyb15dac22022-08-30 14:40:12 -0400397 go func() {
Rob Findleye286d222023-04-20 17:21:15 -0400398 s.diagnoseSnapshot(snapshot, nil, false, 0)
Alan Donovan32e1cb72022-10-27 12:24:51 -0400399 <-initialized
Robert Findleyb15dac22022-08-30 14:40:12 -0400400 release()
Alan Donovan32e1cb72022-10-27 12:24:51 -0400401 ndiagnose.Done()
Robert Findleyb15dac22022-08-30 14:40:12 -0400402 }()
403 }
404
Alan Donovan32e1cb72022-10-27 12:24:51 -0400405 // Wait for snapshots to be initialized so that all files are known.
406 // (We don't need to wait for diagnosis to finish.)
407 nsnapshots.Wait()
408
Robert Findleyb15dac22022-08-30 14:40:12 -0400409 // Register for file watching notifications, if they are supported.
Robert Findleyb15dac22022-08-30 14:40:12 -0400410 if err := s.updateWatchedDirectories(ctx); err != nil {
411 event.Error(ctx, "failed to register for file watching notifications", err)
412 }
413
414 if len(viewErrors) > 0 {
415 errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(folders), len(s.session.Views())-originalViews)
416 for uri, err := range viewErrors {
417 errMsg += fmt.Sprintf("failed to load view for %s: %v\n", uri, err)
418 }
419 return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
420 Type: protocol.Error,
421 Message: errMsg,
422 })
423 }
424 return nil
425}
426
427// updateWatchedDirectories compares the current set of directories to watch
428// with the previously registered set of directories. If the set of directories
429// has changed, we unregister and re-register for file watching notifications.
430// updatedSnapshots is the set of snapshots that have been updated.
431func (s *Server) updateWatchedDirectories(ctx context.Context) error {
432 patterns := s.session.FileWatchingGlobPatterns(ctx)
433
434 s.watchedGlobPatternsMu.Lock()
435 defer s.watchedGlobPatternsMu.Unlock()
436
437 // Nothing to do if the set of workspace directories is unchanged.
438 if equalURISet(s.watchedGlobPatterns, patterns) {
439 return nil
440 }
441
442 // If the set of directories to watch has changed, register the updates and
443 // unregister the previously watched directories. This ordering avoids a
444 // period where no files are being watched. Still, if a user makes on-disk
445 // changes before these updates are complete, we may miss them for the new
446 // directories.
447 prevID := s.watchRegistrationCount - 1
448 if err := s.registerWatchedDirectoriesLocked(ctx, patterns); err != nil {
449 return err
450 }
451 if prevID >= 0 {
452 return s.client.UnregisterCapability(ctx, &protocol.UnregistrationParams{
453 Unregisterations: []protocol.Unregistration{{
454 ID: watchedFilesCapabilityID(prevID),
455 Method: "workspace/didChangeWatchedFiles",
456 }},
457 })
458 }
459 return nil
460}
461
462func watchedFilesCapabilityID(id int) string {
463 return fmt.Sprintf("workspace/didChangeWatchedFiles-%d", id)
464}
465
466func equalURISet(m1, m2 map[string]struct{}) bool {
467 if len(m1) != len(m2) {
468 return false
469 }
470 for k := range m1 {
471 _, ok := m2[k]
472 if !ok {
473 return false
474 }
475 }
476 return true
477}
478
479// registerWatchedDirectoriesLocked sends the workspace/didChangeWatchedFiles
480// registrations to the client and updates s.watchedDirectories.
Alan Donovan2ec42992023-05-18 13:45:43 -0400481// The caller must not subsequently mutate patterns.
Robert Findleyb15dac22022-08-30 14:40:12 -0400482func (s *Server) registerWatchedDirectoriesLocked(ctx context.Context, patterns map[string]struct{}) error {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400483 if !s.Options().DynamicWatchedFilesSupported {
Robert Findleyb15dac22022-08-30 14:40:12 -0400484 return nil
485 }
Alan Donovan2ec42992023-05-18 13:45:43 -0400486 s.watchedGlobPatterns = patterns
487 watchers := make([]protocol.FileSystemWatcher, 0, len(patterns)) // must be a slice
Peter Weinberger49420522023-03-01 14:46:20 -0500488 val := protocol.WatchChange | protocol.WatchDelete | protocol.WatchCreate
Robert Findleyb15dac22022-08-30 14:40:12 -0400489 for pattern := range patterns {
490 watchers = append(watchers, protocol.FileSystemWatcher{
491 GlobPattern: pattern,
Peter Weinberger49420522023-03-01 14:46:20 -0500492 Kind: &val,
Robert Findleyb15dac22022-08-30 14:40:12 -0400493 })
494 }
495
496 if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
497 Registrations: []protocol.Registration{{
498 ID: watchedFilesCapabilityID(s.watchRegistrationCount),
499 Method: "workspace/didChangeWatchedFiles",
500 RegisterOptions: protocol.DidChangeWatchedFilesRegistrationOptions{
501 Watchers: watchers,
502 },
503 }},
504 }); err != nil {
505 return err
506 }
507 s.watchRegistrationCount++
Robert Findleyb15dac22022-08-30 14:40:12 -0400508 return nil
509}
510
Rob Findleyfb4bd112023-09-06 15:44:41 -0400511// Options returns the current server options.
512//
513// The caller must not modify the result.
514func (s *Server) Options() *source.Options {
515 s.optionsMu.Lock()
516 defer s.optionsMu.Unlock()
517 return s.options
518}
519
520// SetOptions sets the current server options.
521//
522// The caller must not subsequently modify the options.
523func (s *Server) SetOptions(opts *source.Options) {
524 s.optionsMu.Lock()
525 defer s.optionsMu.Unlock()
526 s.options = opts
527}
528
529func (s *Server) fetchFolderOptions(ctx context.Context, folder span.URI) (*source.Options, error) {
530 if opts := s.Options(); !opts.ConfigurationSupported {
531 return opts, nil
Robert Findleyb15dac22022-08-30 14:40:12 -0400532 }
533 configs, err := s.client.Configuration(ctx, &protocol.ParamConfiguration{
Peter Weinberger9250e222022-08-16 11:12:59 -0400534 Items: []protocol.ConfigurationItem{{
535 ScopeURI: string(folder),
536 Section: "gopls",
537 }},
538 },
539 )
Robert Findleyb15dac22022-08-30 14:40:12 -0400540 if err != nil {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400541 return nil, fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err)
Robert Findleyb15dac22022-08-30 14:40:12 -0400542 }
Rob Findleyfb4bd112023-09-06 15:44:41 -0400543
544 folderOpts := s.Options().Clone()
Robert Findleyb15dac22022-08-30 14:40:12 -0400545 for _, config := range configs {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400546 if err := s.handleOptionResults(ctx, source.SetOptions(folderOpts, config)); err != nil {
547 return nil, err
Robert Findleyb15dac22022-08-30 14:40:12 -0400548 }
549 }
Rob Findleyfb4bd112023-09-06 15:44:41 -0400550 return folderOpts, nil
Robert Findleyb15dac22022-08-30 14:40:12 -0400551}
552
553func (s *Server) eventuallyShowMessage(ctx context.Context, msg *protocol.ShowMessageParams) error {
554 s.stateMu.Lock()
555 defer s.stateMu.Unlock()
556 if s.state == serverInitialized {
557 return s.client.ShowMessage(ctx, msg)
558 }
559 s.notifications = append(s.notifications, msg)
560 return nil
561}
562
563func (s *Server) handleOptionResults(ctx context.Context, results source.OptionResults) error {
Robert Findley89b43352022-10-07 13:43:27 -0400564 var warnings, errors []string
Robert Findleyb15dac22022-08-30 14:40:12 -0400565 for _, result := range results {
Robert Findleyb15dac22022-08-30 14:40:12 -0400566 switch result.Error.(type) {
567 case nil:
568 // nothing to do
569 case *source.SoftError:
Robert Findley89b43352022-10-07 13:43:27 -0400570 warnings = append(warnings, result.Error.Error())
Robert Findleyb15dac22022-08-30 14:40:12 -0400571 default:
Robert Findley89b43352022-10-07 13:43:27 -0400572 errors = append(errors, result.Error.Error())
Robert Findleyb15dac22022-08-30 14:40:12 -0400573 }
574 }
Robert Findley89b43352022-10-07 13:43:27 -0400575
576 // Sort messages, but put errors first.
577 //
578 // Having stable content for the message allows clients to de-duplicate. This
579 // matters because we may send duplicate warnings for clients that support
580 // dynamic configuration: one for the initial settings, and then more for the
581 // individual view settings.
582 var msgs []string
583 msgType := protocol.Warning
584 if len(errors) > 0 {
585 msgType = protocol.Error
586 sort.Strings(errors)
587 msgs = append(msgs, errors...)
588 }
589 if len(warnings) > 0 {
590 sort.Strings(warnings)
591 msgs = append(msgs, warnings...)
592 }
593
594 if len(msgs) > 0 {
595 // Settings
596 combined := "Invalid settings: " + strings.Join(msgs, "; ")
597 params := &protocol.ShowMessageParams{
598 Type: msgType,
599 Message: combined,
600 }
601 return s.eventuallyShowMessage(ctx, params)
602 }
603
Robert Findleyb15dac22022-08-30 14:40:12 -0400604 return nil
605}
606
607// beginFileRequest checks preconditions for a file-oriented request and routes
608// it to a snapshot.
609// We don't want to return errors for benign conditions like wrong file type,
610// so callers should do if !ok { return err } rather than if err != nil.
611// The returned cleanup function is non-nil even in case of false/error result.
Robert Findleya7f033a2023-01-19 18:12:18 -0500612func (s *Server) beginFileRequest(ctx context.Context, pURI protocol.DocumentURI, expectKind source.FileKind) (source.Snapshot, source.FileHandle, bool, func(), error) {
Robert Findleyb15dac22022-08-30 14:40:12 -0400613 uri := pURI.SpanURI()
614 if !uri.IsFile() {
615 // Not a file URI. Stop processing the request, but don't return an error.
616 return nil, nil, false, func() {}, nil
617 }
618 view, err := s.session.ViewOf(uri)
619 if err != nil {
620 return nil, nil, false, func() {}, err
621 }
Robert Findleyc4c6aa62023-01-19 20:24:55 -0500622 snapshot, release, err := view.Snapshot()
623 if err != nil {
624 return nil, nil, false, func() {}, err
625 }
Alan Donovan36ed0b12023-03-13 14:20:23 -0400626 fh, err := snapshot.ReadFile(ctx, uri)
Robert Findleyb15dac22022-08-30 14:40:12 -0400627 if err != nil {
628 release()
629 return nil, nil, false, func() {}, err
630 }
Rob Findley0a9721c2023-09-06 13:49:15 -0400631 if expectKind != source.UnknownKind && snapshot.FileKind(fh) != expectKind {
Robert Findleyb15dac22022-08-30 14:40:12 -0400632 // Wrong kind of file. Nothing to do.
633 release()
634 return nil, nil, false, func() {}, nil
635 }
636 return snapshot, fh, true, release, nil
637}
638
639// shutdown implements the 'shutdown' LSP handler. It releases resources
640// associated with the server and waits for all ongoing work to complete.
641func (s *Server) shutdown(ctx context.Context) error {
Robert Findleyf7963612023-03-28 21:34:10 -0400642 ctx, done := event.Start(ctx, "lsp.Server.shutdown")
643 defer done()
644
Robert Findleyb15dac22022-08-30 14:40:12 -0400645 s.stateMu.Lock()
646 defer s.stateMu.Unlock()
647 if s.state < serverInitialized {
648 event.Log(ctx, "server shutdown without initialization")
649 }
650 if s.state != serverShutDown {
651 // drop all the active views
652 s.session.Shutdown(ctx)
653 s.state = serverShutDown
654 if s.tempDir != "" {
655 if err := os.RemoveAll(s.tempDir); err != nil {
656 event.Error(ctx, "removing temp dir", err)
657 }
658 }
659 }
660 return nil
661}
662
663func (s *Server) exit(ctx context.Context) error {
Robert Findleyf7963612023-03-28 21:34:10 -0400664 ctx, done := event.Start(ctx, "lsp.Server.exit")
665 defer done()
666
Robert Findleyb15dac22022-08-30 14:40:12 -0400667 s.stateMu.Lock()
668 defer s.stateMu.Unlock()
669
670 s.client.Close()
671
672 if s.state != serverShutDown {
673 // TODO: We should be able to do better than this.
674 os.Exit(1)
675 }
Rob Findleye286d222023-04-20 17:21:15 -0400676 // We don't terminate the process on a normal exit, we just allow it to
Robert Findleyb15dac22022-08-30 14:40:12 -0400677 // close naturally if needed after the connection is closed.
678 return nil
679}
Peter Weinberger64920582023-04-08 18:42:05 -0400680
681// TODO: when we can assume go1.18, replace with generic
682// (after retiring support for go1.17)
683func nonNilSliceString(x []string) []string {
684 if x == nil {
685 return []string{}
686 }
687 return x
688}
689func nonNilSliceTextEdit(x []protocol.TextEdit) []protocol.TextEdit {
690 if x == nil {
691 return []protocol.TextEdit{}
692 }
693
694 return x
695}
696func nonNilSliceCompletionItemTag(x []protocol.CompletionItemTag) []protocol.CompletionItemTag {
697 if x == nil {
698 return []protocol.CompletionItemTag{}
699 }
700 return x
701}
702func emptySliceDiagnosticTag(x []protocol.DiagnosticTag) []protocol.DiagnosticTag {
703 if x == nil {
704 return []protocol.DiagnosticTag{}
705 }
706 return x
707}