blob: f5e3604929a9589251221bf409944aed254953e7 [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
Alan Donovan6e052bb2023-11-29 09:27:28 -05005package server
Robert Findleyb15dac22022-08-30 14:40:12 -04006
Alan Donovan147b88d2023-11-29 10:03:49 -05007// This file defines server methods related to initialization,
8// options, shutdown, and exit.
9
Robert Findleyb15dac22022-08-30 14:40:12 -040010import (
Robert Findleyb15dac22022-08-30 14:40:12 -040011 "context"
12 "encoding/json"
13 "fmt"
Rob Findley5ff5cbb2023-05-22 13:24:35 -040014 "go/build"
Robert Findleyb15dac22022-08-30 14:40:12 -040015 "log"
16 "os"
17 "path"
18 "path/filepath"
Robert Findley89b43352022-10-07 13:43:27 -040019 "sort"
20 "strings"
Robert Findleyb15dac22022-08-30 14:40:12 -040021 "sync"
22
Rob Findleyc8234882023-11-17 16:14:35 -050023 "golang.org/x/tools/gopls/internal/file"
Alan Donovancae49bd2023-11-13 18:08:16 -050024 "golang.org/x/tools/gopls/internal/goversion"
Rob Findleyfdf06f22023-11-15 16:24:53 -050025 "golang.org/x/tools/gopls/internal/lsp/cache"
Robert Findleyb15dac22022-08-30 14:40:12 -040026 "golang.org/x/tools/gopls/internal/lsp/debug"
27 "golang.org/x/tools/gopls/internal/lsp/protocol"
Rob Findley42d97792023-11-17 17:45:31 -050028 "golang.org/x/tools/gopls/internal/settings"
Hana (Hyang-Ah) Kim4b271f92023-07-19 14:18:26 -040029 "golang.org/x/tools/gopls/internal/telemetry"
Alan Donovandb5acf62023-11-29 10:17:12 -050030 "golang.org/x/tools/gopls/internal/util/bug"
Peter Weinberger9250e222022-08-16 11:12:59 -040031 "golang.org/x/tools/internal/event"
32 "golang.org/x/tools/internal/jsonrpc2"
Robert Findleyb15dac22022-08-30 14:40:12 -040033)
34
Alan Donovan25294812023-11-19 13:24:36 -050035func (s *server) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) {
Robert Findleyf7963612023-03-28 21:34:10 -040036 ctx, done := event.Start(ctx, "lsp.Server.initialize")
37 defer done()
38
Hana (Hyang-Ah) Kim4b271f92023-07-19 14:18:26 -040039 telemetry.RecordClientInfo(params)
40
Robert Findleyb15dac22022-08-30 14:40:12 -040041 s.stateMu.Lock()
42 if s.state >= serverInitializing {
43 defer s.stateMu.Unlock()
44 return nil, fmt.Errorf("%w: initialize called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
45 }
46 s.state = serverInitializing
47 s.stateMu.Unlock()
48
49 // For uniqueness, use the gopls PID rather than params.ProcessID (the client
50 // pid). Some clients might start multiple gopls servers, though they
51 // probably shouldn't.
52 pid := os.Getpid()
53 s.tempDir = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.%s", pid, s.session.ID()))
54 err := os.Mkdir(s.tempDir, 0700)
55 if err != nil {
56 // MkdirTemp could fail due to permissions issues. This is a problem with
57 // the user's environment, but should not block gopls otherwise behaving.
58 // All usage of s.tempDir should be predicated on having a non-empty
59 // s.tempDir.
60 event.Error(ctx, "creating temp dir", err)
61 s.tempDir = ""
62 }
63 s.progress.SetSupportsWorkDoneProgress(params.Capabilities.Window.WorkDoneProgress)
64
Rob Findleyfb4bd112023-09-06 15:44:41 -040065 options := s.Options().Clone()
66 // TODO(rfindley): remove the error return from handleOptionResults, and
67 // eliminate this defer.
68 defer func() { s.SetOptions(options) }()
Robert Findleyb15dac22022-08-30 14:40:12 -040069
Rob Findley42d97792023-11-17 17:45:31 -050070 if err := s.handleOptionResults(ctx, settings.SetOptions(options, params.InitializationOptions)); err != nil {
Robert Findleyb15dac22022-08-30 14:40:12 -040071 return nil, err
72 }
Rob Findley3a5dbf32023-05-21 11:03:28 -040073 options.ForClientCapabilities(params.ClientInfo, params.Capabilities)
Robert Findleyb15dac22022-08-30 14:40:12 -040074
75 if options.ShowBugReports {
76 // Report the next bug that occurs on the server.
Alan Donovane2f3b252023-04-25 15:21:16 -040077 bug.Handle(func(b bug.Bug) {
Robert Findleyb15dac22022-08-30 14:40:12 -040078 msg := &protocol.ShowMessageParams{
79 Type: protocol.Error,
80 Message: fmt.Sprintf("A bug occurred on the server: %s\nLocation:%s", b.Description, b.Key),
81 }
Alan Donovane2f3b252023-04-25 15:21:16 -040082 go func() {
83 if err := s.eventuallyShowMessage(context.Background(), msg); err != nil {
84 log.Printf("error showing bug: %v", err)
85 }
86 }()
87 })
Robert Findleyb15dac22022-08-30 14:40:12 -040088 }
89
90 folders := params.WorkspaceFolders
91 if len(folders) == 0 {
92 if params.RootURI != "" {
93 folders = []protocol.WorkspaceFolder{{
94 URI: string(params.RootURI),
Alan Donovan1cab1272023-11-17 16:16:59 -050095 Name: path.Base(params.RootURI.Path()),
Robert Findleyb15dac22022-08-30 14:40:12 -040096 }}
97 }
98 }
99 for _, folder := range folders {
Alan Donovaneee280c2023-11-18 14:43:57 -0500100 if folder.URI == "" {
101 return nil, fmt.Errorf("empty WorkspaceFolder.URI")
102 }
103 if _, err := protocol.ParseDocumentURI(folder.URI); err != nil {
104 return nil, fmt.Errorf("invalid WorkspaceFolder.URI: %v", err)
Robert Findleyb15dac22022-08-30 14:40:12 -0400105 }
106 s.pendingFolders = append(s.pendingFolders, folder)
107 }
Robert Findleyb15dac22022-08-30 14:40:12 -0400108
109 var codeActionProvider interface{} = true
110 if ca := params.Capabilities.TextDocument.CodeAction; len(ca.CodeActionLiteralSupport.CodeActionKind.ValueSet) > 0 {
111 // If the client has specified CodeActionLiteralSupport,
112 // send the code actions we support.
113 //
114 // Using CodeActionOptions is only valid if codeActionLiteralSupport is set.
115 codeActionProvider = &protocol.CodeActionOptions{
116 CodeActionKinds: s.getSupportedCodeActions(),
117 }
118 }
119 var renameOpts interface{} = true
Peter Weinberger49420522023-03-01 14:46:20 -0500120 if r := params.Capabilities.TextDocument.Rename; r != nil && r.PrepareSupport {
Robert Findleyb15dac22022-08-30 14:40:12 -0400121 renameOpts = protocol.RenameOptions{
122 PrepareProvider: r.PrepareSupport,
123 }
124 }
125
126 versionInfo := debug.VersionInfo()
127
128 // golang/go#45732: Warn users who've installed sergi/go-diff@v1.2.0, since
129 // it will corrupt the formatting of their files.
130 for _, dep := range versionInfo.Deps {
131 if dep.Path == "github.com/sergi/go-diff" && dep.Version == "v1.2.0" {
132 if err := s.eventuallyShowMessage(ctx, &protocol.ShowMessageParams{
133 Message: `It looks like you have a bad gopls installation.
134Please reinstall gopls by running 'GO111MODULE=on go install golang.org/x/tools/gopls@latest'.
135See https://github.com/golang/go/issues/45732 for more information.`,
136 Type: protocol.Error,
137 }); err != nil {
138 return nil, err
139 }
140 }
141 }
142
143 goplsVersion, err := json.Marshal(versionInfo)
144 if err != nil {
145 return nil, err
146 }
147
148 return &protocol.InitializeResult{
149 Capabilities: protocol.ServerCapabilities{
Peter Weinbergere85b5332023-02-18 15:25:18 -0500150 CallHierarchyProvider: &protocol.Or_ServerCapabilities_callHierarchyProvider{Value: true},
Robert Findleyb15dac22022-08-30 14:40:12 -0400151 CodeActionProvider: codeActionProvider,
Suzy Mueller15782442022-09-19 14:13:01 -0400152 CodeLensProvider: &protocol.CodeLensOptions{}, // must be non-nil to enable the code lens capability
Peter Weinberger49420522023-03-01 14:46:20 -0500153 CompletionProvider: &protocol.CompletionOptions{
Robert Findleyb15dac22022-08-30 14:40:12 -0400154 TriggerCharacters: []string{"."},
155 },
Peter Weinbergere85b5332023-02-18 15:25:18 -0500156 DefinitionProvider: &protocol.Or_ServerCapabilities_definitionProvider{Value: true},
157 TypeDefinitionProvider: &protocol.Or_ServerCapabilities_typeDefinitionProvider{Value: true},
158 ImplementationProvider: &protocol.Or_ServerCapabilities_implementationProvider{Value: true},
159 DocumentFormattingProvider: &protocol.Or_ServerCapabilities_documentFormattingProvider{Value: true},
160 DocumentSymbolProvider: &protocol.Or_ServerCapabilities_documentSymbolProvider{Value: true},
161 WorkspaceSymbolProvider: &protocol.Or_ServerCapabilities_workspaceSymbolProvider{Value: true},
Peter Weinberger49420522023-03-01 14:46:20 -0500162 ExecuteCommandProvider: &protocol.ExecuteCommandOptions{
Alan Donovan147b88d2023-11-29 10:03:49 -0500163 Commands: protocol.NonNilSlice(options.SupportedCommands),
Robert Findleyb15dac22022-08-30 14:40:12 -0400164 },
Peter Weinbergere85b5332023-02-18 15:25:18 -0500165 FoldingRangeProvider: &protocol.Or_ServerCapabilities_foldingRangeProvider{Value: true},
166 HoverProvider: &protocol.Or_ServerCapabilities_hoverProvider{Value: true},
167 DocumentHighlightProvider: &protocol.Or_ServerCapabilities_documentHighlightProvider{Value: true},
Peter Weinberger49420522023-03-01 14:46:20 -0500168 DocumentLinkProvider: &protocol.DocumentLinkOptions{},
Robert Findleyb15dac22022-08-30 14:40:12 -0400169 InlayHintProvider: protocol.InlayHintOptions{},
Peter Weinbergere85b5332023-02-18 15:25:18 -0500170 ReferencesProvider: &protocol.Or_ServerCapabilities_referencesProvider{Value: true},
Robert Findleyb15dac22022-08-30 14:40:12 -0400171 RenameProvider: renameOpts,
Peter Weinbergere85b5332023-02-18 15:25:18 -0500172 SelectionRangeProvider: &protocol.Or_ServerCapabilities_selectionRangeProvider{Value: true},
Peter Weinbergerb5d65e02023-01-14 15:58:41 -0500173 SemanticTokensProvider: protocol.SemanticTokensOptions{
Peter Weinbergere85b5332023-02-18 15:25:18 -0500174 Range: &protocol.Or_SemanticTokensOptions_range{Value: true},
175 Full: &protocol.Or_SemanticTokensOptions_full{Value: true},
Peter Weinbergerb5d65e02023-01-14 15:58:41 -0500176 Legend: protocol.SemanticTokensLegend{
Alan Donovan147b88d2023-11-29 10:03:49 -0500177 TokenTypes: protocol.NonNilSlice(options.SemanticTypes),
178 TokenModifiers: protocol.NonNilSlice(options.SemanticMods),
Peter Weinbergerb5d65e02023-01-14 15:58:41 -0500179 },
180 },
Peter Weinberger49420522023-03-01 14:46:20 -0500181 SignatureHelpProvider: &protocol.SignatureHelpOptions{
Robert Findleyb15dac22022-08-30 14:40:12 -0400182 TriggerCharacters: []string{"(", ","},
183 },
184 TextDocumentSync: &protocol.TextDocumentSyncOptions{
185 Change: protocol.Incremental,
186 OpenClose: true,
Peter Weinberger49420522023-03-01 14:46:20 -0500187 Save: &protocol.SaveOptions{
Robert Findleyb15dac22022-08-30 14:40:12 -0400188 IncludeText: false,
189 },
190 },
Peter Weinberger49420522023-03-01 14:46:20 -0500191 Workspace: &protocol.Workspace6Gn{
192 WorkspaceFolders: &protocol.WorkspaceFolders5Gn{
Robert Findleyb15dac22022-08-30 14:40:12 -0400193 Supported: true,
194 ChangeNotifications: "workspace/didChangeWorkspaceFolders",
195 },
196 },
197 },
Peter Weinberger49420522023-03-01 14:46:20 -0500198 ServerInfo: &protocol.PServerInfoMsg_initialize{
Robert Findleyb15dac22022-08-30 14:40:12 -0400199 Name: "gopls",
200 Version: string(goplsVersion),
201 },
202 }, nil
203}
204
Alan Donovan25294812023-11-19 13:24:36 -0500205func (s *server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
Robert Findleyf7963612023-03-28 21:34:10 -0400206 ctx, done := event.Start(ctx, "lsp.Server.initialized")
207 defer done()
208
Robert Findleyb15dac22022-08-30 14:40:12 -0400209 s.stateMu.Lock()
210 if s.state >= serverInitialized {
211 defer s.stateMu.Unlock()
212 return fmt.Errorf("%w: initialized called while server in %v state", jsonrpc2.ErrInvalidRequest, s.state)
213 }
214 s.state = serverInitialized
215 s.stateMu.Unlock()
216
217 for _, not := range s.notifications {
218 s.client.ShowMessage(ctx, not)
219 }
220 s.notifications = nil
221
Alan Donovan73e70de2023-11-09 13:07:56 -0500222 s.addFolders(ctx, s.pendingFolders)
223
Robert Findleyb15dac22022-08-30 14:40:12 -0400224 s.pendingFolders = nil
Robert Findley20c1ee72022-09-28 13:10:58 -0400225 s.checkViewGoVersions()
Robert Findleyb15dac22022-08-30 14:40:12 -0400226
227 var registrations []protocol.Registration
Rob Findleyfda3fe32023-10-09 09:33:23 -0400228 options := s.Options()
Robert Findleyb15dac22022-08-30 14:40:12 -0400229 if options.ConfigurationSupported && options.DynamicConfigurationSupported {
230 registrations = append(registrations, protocol.Registration{
231 ID: "workspace/didChangeConfiguration",
232 Method: "workspace/didChangeConfiguration",
233 })
234 }
Robert Findleyb15dac22022-08-30 14:40:12 -0400235 if len(registrations) > 0 {
236 if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
237 Registrations: registrations,
238 }); err != nil {
239 return err
240 }
241 }
Rob Findleya3c6fd82023-09-12 13:08:54 -0400242
243 // Ask (maybe) about enabling telemetry. Do this asynchronously, as it's OK
244 // for users to ignore or dismiss the question.
Hana (Hyang-Ah) Kim64beb952023-09-22 09:19:20 -0400245 go s.maybePromptForTelemetry(ctx, options.TelemetryPrompt)
Rob Findleya3c6fd82023-09-12 13:08:54 -0400246
Robert Findleyb15dac22022-08-30 14:40:12 -0400247 return nil
248}
249
Robert Findley20c1ee72022-09-28 13:10:58 -0400250// checkViewGoVersions checks whether any Go version used by a view is too old,
251// raising a showMessage notification if so.
252//
253// It should be called after views change.
Alan Donovan79546892023-11-13 16:59:44 -0500254func (s *server) checkViewGoVersions() {
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400255 oldestVersion, fromBuild := go1Point(), true
Robert Findley20c1ee72022-09-28 13:10:58 -0400256 for _, view := range s.session.Views() {
257 viewVersion := view.GoVersion()
258 if oldestVersion == -1 || viewVersion < oldestVersion {
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400259 oldestVersion, fromBuild = viewVersion, false
Robert Findley20c1ee72022-09-28 13:10:58 -0400260 }
Hana (Hyang-Ah) Kim33da5c02023-08-03 18:26:07 -0400261 telemetry.RecordViewGoVersion(viewVersion)
Robert Findley20c1ee72022-09-28 13:10:58 -0400262 }
263
Alan Donovancae49bd2023-11-13 18:08:16 -0500264 if msg, isError := goversion.Message(oldestVersion, fromBuild); msg != "" {
265 mType := protocol.Warning
266 if isError {
267 mType = protocol.Error
268 }
Robert Findley20c1ee72022-09-28 13:10:58 -0400269 s.eventuallyShowMessage(context.Background(), &protocol.ShowMessageParams{
Robert Findley42cb7be2022-10-26 18:57:08 -0400270 Type: mType,
Robert Findley20c1ee72022-09-28 13:10:58 -0400271 Message: msg,
272 })
273 }
274}
275
Rob Findley5ff5cbb2023-05-22 13:24:35 -0400276// go1Point returns the x in Go 1.x. If an error occurs extracting the go
277// version, it returns -1.
278//
279// Copied from the testenv package.
280func go1Point() int {
281 for i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- {
282 var version int
283 if _, err := fmt.Sscanf(build.Default.ReleaseTags[i], "go1.%d", &version); err != nil {
284 continue
285 }
286 return version
287 }
288 return -1
289}
290
Alan Donovan73e70de2023-11-09 13:07:56 -0500291// addFolders adds the specified list of "folders" (that's Windows for
292// directories) to the session. It does not return an error, though it
293// may report an error to the client over LSP if one or more folders
294// had problems.
Alan Donovan73e70de2023-11-09 13:07:56 -0500295func (s *server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) {
Robert Findleyb15dac22022-08-30 14:40:12 -0400296 originalViews := len(s.session.Views())
Alan Donovanc04fbc02023-11-28 12:50:06 -0500297 viewErrors := make(map[protocol.URI]error)
Robert Findleyb15dac22022-08-30 14:40:12 -0400298
Alan Donovan32e1cb72022-10-27 12:24:51 -0400299 var ndiagnose sync.WaitGroup // number of unfinished diagnose calls
Rob Findleyfb4bd112023-09-06 15:44:41 -0400300 if s.Options().VerboseWorkDoneProgress {
Robert Findleyb15dac22022-08-30 14:40:12 -0400301 work := s.progress.Start(ctx, DiagnosticWorkTitle(FromInitialWorkspaceLoad), "Calculating diagnostics for initial workspace load...", nil, nil)
302 defer func() {
303 go func() {
Alan Donovan32e1cb72022-10-27 12:24:51 -0400304 ndiagnose.Wait()
Robert Findleyb15dac22022-08-30 14:40:12 -0400305 work.End(ctx, "Done.")
306 }()
307 }()
308 }
309 // Only one view gets to have a workspace.
Alan Donovan32e1cb72022-10-27 12:24:51 -0400310 var nsnapshots sync.WaitGroup // number of unfinished snapshot initializations
Robert Findleyb15dac22022-08-30 14:40:12 -0400311 for _, folder := range folders {
Alan Donovaneee280c2023-11-18 14:43:57 -0500312 uri, err := protocol.ParseDocumentURI(folder.URI)
313 if err != nil {
Alan Donovanc04fbc02023-11-28 12:50:06 -0500314 viewErrors[folder.URI] = fmt.Errorf("invalid folder URI: %v", err)
Robert Findleyb15dac22022-08-30 14:40:12 -0400315 continue
316 }
317 work := s.progress.Start(ctx, "Setting up workspace", "Loading packages...", nil, nil)
318 snapshot, release, err := s.addView(ctx, folder.Name, uri)
319 if err != nil {
Rob Findleye7d61d92023-11-20 15:54:11 -0500320 if err == cache.ErrViewExists {
Robert Findleyb15dac22022-08-30 14:40:12 -0400321 continue
322 }
Alan Donovanc04fbc02023-11-28 12:50:06 -0500323 viewErrors[folder.URI] = err
Robert Findleyb15dac22022-08-30 14:40:12 -0400324 work.End(ctx, fmt.Sprintf("Error loading packages: %s", err))
325 continue
326 }
327 // Inv: release() must be called once.
328
Alan Donovan32e1cb72022-10-27 12:24:51 -0400329 // Initialize snapshot asynchronously.
330 initialized := make(chan struct{})
331 nsnapshots.Add(1)
332 go func() {
333 snapshot.AwaitInitialized(ctx)
334 work.End(ctx, "Finished loading packages.")
335 nsnapshots.Done()
336 close(initialized) // signal
337 }()
338
339 // Diagnose the newly created view asynchronously.
340 ndiagnose.Add(1)
Robert Findleyb15dac22022-08-30 14:40:12 -0400341 go func() {
Rob Findleye286d222023-04-20 17:21:15 -0400342 s.diagnoseSnapshot(snapshot, nil, false, 0)
Alan Donovan32e1cb72022-10-27 12:24:51 -0400343 <-initialized
Robert Findleyb15dac22022-08-30 14:40:12 -0400344 release()
Alan Donovan32e1cb72022-10-27 12:24:51 -0400345 ndiagnose.Done()
Robert Findleyb15dac22022-08-30 14:40:12 -0400346 }()
347 }
348
Alan Donovan32e1cb72022-10-27 12:24:51 -0400349 // Wait for snapshots to be initialized so that all files are known.
350 // (We don't need to wait for diagnosis to finish.)
351 nsnapshots.Wait()
352
Robert Findleyb15dac22022-08-30 14:40:12 -0400353 // Register for file watching notifications, if they are supported.
Robert Findleyb15dac22022-08-30 14:40:12 -0400354 if err := s.updateWatchedDirectories(ctx); err != nil {
355 event.Error(ctx, "failed to register for file watching notifications", err)
356 }
357
Alan Donovan73e70de2023-11-09 13:07:56 -0500358 // Report any errors using the protocol.
Robert Findleyb15dac22022-08-30 14:40:12 -0400359 if len(viewErrors) > 0 {
360 errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(folders), len(s.session.Views())-originalViews)
361 for uri, err := range viewErrors {
362 errMsg += fmt.Sprintf("failed to load view for %s: %v\n", uri, err)
363 }
Alan Donovan73e70de2023-11-09 13:07:56 -0500364 showMessage(ctx, s.client, protocol.Error, errMsg)
Robert Findleyb15dac22022-08-30 14:40:12 -0400365 }
Robert Findleyb15dac22022-08-30 14:40:12 -0400366}
367
368// updateWatchedDirectories compares the current set of directories to watch
369// with the previously registered set of directories. If the set of directories
370// has changed, we unregister and re-register for file watching notifications.
371// updatedSnapshots is the set of snapshots that have been updated.
Alan Donovan79546892023-11-13 16:59:44 -0500372func (s *server) updateWatchedDirectories(ctx context.Context) error {
Robert Findleyb15dac22022-08-30 14:40:12 -0400373 patterns := s.session.FileWatchingGlobPatterns(ctx)
374
375 s.watchedGlobPatternsMu.Lock()
376 defer s.watchedGlobPatternsMu.Unlock()
377
378 // Nothing to do if the set of workspace directories is unchanged.
379 if equalURISet(s.watchedGlobPatterns, patterns) {
380 return nil
381 }
382
383 // If the set of directories to watch has changed, register the updates and
384 // unregister the previously watched directories. This ordering avoids a
385 // period where no files are being watched. Still, if a user makes on-disk
386 // changes before these updates are complete, we may miss them for the new
387 // directories.
388 prevID := s.watchRegistrationCount - 1
389 if err := s.registerWatchedDirectoriesLocked(ctx, patterns); err != nil {
390 return err
391 }
392 if prevID >= 0 {
393 return s.client.UnregisterCapability(ctx, &protocol.UnregistrationParams{
394 Unregisterations: []protocol.Unregistration{{
395 ID: watchedFilesCapabilityID(prevID),
396 Method: "workspace/didChangeWatchedFiles",
397 }},
398 })
399 }
400 return nil
401}
402
403func watchedFilesCapabilityID(id int) string {
404 return fmt.Sprintf("workspace/didChangeWatchedFiles-%d", id)
405}
406
407func equalURISet(m1, m2 map[string]struct{}) bool {
408 if len(m1) != len(m2) {
409 return false
410 }
411 for k := range m1 {
412 _, ok := m2[k]
413 if !ok {
414 return false
415 }
416 }
417 return true
418}
419
420// registerWatchedDirectoriesLocked sends the workspace/didChangeWatchedFiles
421// registrations to the client and updates s.watchedDirectories.
Alan Donovan2ec42992023-05-18 13:45:43 -0400422// The caller must not subsequently mutate patterns.
Alan Donovan79546892023-11-13 16:59:44 -0500423func (s *server) registerWatchedDirectoriesLocked(ctx context.Context, patterns map[string]struct{}) error {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400424 if !s.Options().DynamicWatchedFilesSupported {
Robert Findleyb15dac22022-08-30 14:40:12 -0400425 return nil
426 }
Alan Donovan2ec42992023-05-18 13:45:43 -0400427 s.watchedGlobPatterns = patterns
428 watchers := make([]protocol.FileSystemWatcher, 0, len(patterns)) // must be a slice
Peter Weinberger49420522023-03-01 14:46:20 -0500429 val := protocol.WatchChange | protocol.WatchDelete | protocol.WatchCreate
Robert Findleyb15dac22022-08-30 14:40:12 -0400430 for pattern := range patterns {
431 watchers = append(watchers, protocol.FileSystemWatcher{
432 GlobPattern: pattern,
Peter Weinberger49420522023-03-01 14:46:20 -0500433 Kind: &val,
Robert Findleyb15dac22022-08-30 14:40:12 -0400434 })
435 }
436
437 if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
438 Registrations: []protocol.Registration{{
439 ID: watchedFilesCapabilityID(s.watchRegistrationCount),
440 Method: "workspace/didChangeWatchedFiles",
441 RegisterOptions: protocol.DidChangeWatchedFilesRegistrationOptions{
442 Watchers: watchers,
443 },
444 }},
445 }); err != nil {
446 return err
447 }
448 s.watchRegistrationCount++
Robert Findleyb15dac22022-08-30 14:40:12 -0400449 return nil
450}
451
Rob Findleyfb4bd112023-09-06 15:44:41 -0400452// Options returns the current server options.
453//
454// The caller must not modify the result.
Rob Findley42d97792023-11-17 17:45:31 -0500455func (s *server) Options() *settings.Options {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400456 s.optionsMu.Lock()
457 defer s.optionsMu.Unlock()
458 return s.options
459}
460
461// SetOptions sets the current server options.
462//
463// The caller must not subsequently modify the options.
Rob Findley42d97792023-11-17 17:45:31 -0500464func (s *server) SetOptions(opts *settings.Options) {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400465 s.optionsMu.Lock()
466 defer s.optionsMu.Unlock()
467 s.options = opts
468}
469
Rob Findley42d97792023-11-17 17:45:31 -0500470func (s *server) fetchFolderOptions(ctx context.Context, folder protocol.DocumentURI) (*settings.Options, error) {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400471 if opts := s.Options(); !opts.ConfigurationSupported {
472 return opts, nil
Robert Findleyb15dac22022-08-30 14:40:12 -0400473 }
474 configs, err := s.client.Configuration(ctx, &protocol.ParamConfiguration{
Peter Weinberger9250e222022-08-16 11:12:59 -0400475 Items: []protocol.ConfigurationItem{{
476 ScopeURI: string(folder),
477 Section: "gopls",
478 }},
479 },
480 )
Robert Findleyb15dac22022-08-30 14:40:12 -0400481 if err != nil {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400482 return nil, fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err)
Robert Findleyb15dac22022-08-30 14:40:12 -0400483 }
Rob Findleyfb4bd112023-09-06 15:44:41 -0400484
485 folderOpts := s.Options().Clone()
Robert Findleyb15dac22022-08-30 14:40:12 -0400486 for _, config := range configs {
Rob Findley42d97792023-11-17 17:45:31 -0500487 if err := s.handleOptionResults(ctx, settings.SetOptions(folderOpts, config)); err != nil {
Rob Findleyfb4bd112023-09-06 15:44:41 -0400488 return nil, err
Robert Findleyb15dac22022-08-30 14:40:12 -0400489 }
490 }
Rob Findleyfb4bd112023-09-06 15:44:41 -0400491 return folderOpts, nil
Robert Findleyb15dac22022-08-30 14:40:12 -0400492}
493
Alan Donovan79546892023-11-13 16:59:44 -0500494func (s *server) eventuallyShowMessage(ctx context.Context, msg *protocol.ShowMessageParams) error {
Robert Findleyb15dac22022-08-30 14:40:12 -0400495 s.stateMu.Lock()
496 defer s.stateMu.Unlock()
497 if s.state == serverInitialized {
498 return s.client.ShowMessage(ctx, msg)
499 }
500 s.notifications = append(s.notifications, msg)
501 return nil
502}
503
Rob Findley42d97792023-11-17 17:45:31 -0500504func (s *server) handleOptionResults(ctx context.Context, results settings.OptionResults) error {
Robert Findley89b43352022-10-07 13:43:27 -0400505 var warnings, errors []string
Robert Findleyb15dac22022-08-30 14:40:12 -0400506 for _, result := range results {
Robert Findleyb15dac22022-08-30 14:40:12 -0400507 switch result.Error.(type) {
508 case nil:
509 // nothing to do
Rob Findley42d97792023-11-17 17:45:31 -0500510 case *settings.SoftError:
Robert Findley89b43352022-10-07 13:43:27 -0400511 warnings = append(warnings, result.Error.Error())
Robert Findleyb15dac22022-08-30 14:40:12 -0400512 default:
Robert Findley89b43352022-10-07 13:43:27 -0400513 errors = append(errors, result.Error.Error())
Robert Findleyb15dac22022-08-30 14:40:12 -0400514 }
515 }
Robert Findley89b43352022-10-07 13:43:27 -0400516
517 // Sort messages, but put errors first.
518 //
519 // Having stable content for the message allows clients to de-duplicate. This
520 // matters because we may send duplicate warnings for clients that support
521 // dynamic configuration: one for the initial settings, and then more for the
Rob Findley42d97792023-11-17 17:45:31 -0500522 // individual viewsettings.
Robert Findley89b43352022-10-07 13:43:27 -0400523 var msgs []string
524 msgType := protocol.Warning
525 if len(errors) > 0 {
526 msgType = protocol.Error
527 sort.Strings(errors)
528 msgs = append(msgs, errors...)
529 }
530 if len(warnings) > 0 {
531 sort.Strings(warnings)
532 msgs = append(msgs, warnings...)
533 }
534
535 if len(msgs) > 0 {
536 // Settings
537 combined := "Invalid settings: " + strings.Join(msgs, "; ")
538 params := &protocol.ShowMessageParams{
539 Type: msgType,
540 Message: combined,
541 }
542 return s.eventuallyShowMessage(ctx, params)
543 }
544
Robert Findleyb15dac22022-08-30 14:40:12 -0400545 return nil
546}
547
548// beginFileRequest checks preconditions for a file-oriented request and routes
549// it to a snapshot.
550// We don't want to return errors for benign conditions like wrong file type,
551// so callers should do if !ok { return err } rather than if err != nil.
552// The returned cleanup function is non-nil even in case of false/error result.
Alan Donovaneee280c2023-11-18 14:43:57 -0500553func (s *server) beginFileRequest(ctx context.Context, uri protocol.DocumentURI, expectKind file.Kind) (*cache.Snapshot, file.Handle, bool, func(), error) {
Robert Findleyb15dac22022-08-30 14:40:12 -0400554 view, err := s.session.ViewOf(uri)
555 if err != nil {
556 return nil, nil, false, func() {}, err
557 }
Robert Findleyc4c6aa62023-01-19 20:24:55 -0500558 snapshot, release, err := view.Snapshot()
559 if err != nil {
560 return nil, nil, false, func() {}, err
561 }
Alan Donovan36ed0b12023-03-13 14:20:23 -0400562 fh, err := snapshot.ReadFile(ctx, uri)
Robert Findleyb15dac22022-08-30 14:40:12 -0400563 if err != nil {
564 release()
565 return nil, nil, false, func() {}, err
566 }
Rob Findleyc8234882023-11-17 16:14:35 -0500567 if expectKind != file.UnknownKind && snapshot.FileKind(fh) != expectKind {
Robert Findleyb15dac22022-08-30 14:40:12 -0400568 // Wrong kind of file. Nothing to do.
569 release()
570 return nil, nil, false, func() {}, nil
571 }
572 return snapshot, fh, true, release, nil
573}
574
575// shutdown implements the 'shutdown' LSP handler. It releases resources
576// associated with the server and waits for all ongoing work to complete.
Alan Donovan25294812023-11-19 13:24:36 -0500577func (s *server) Shutdown(ctx context.Context) error {
Robert Findleyf7963612023-03-28 21:34:10 -0400578 ctx, done := event.Start(ctx, "lsp.Server.shutdown")
579 defer done()
580
Robert Findleyb15dac22022-08-30 14:40:12 -0400581 s.stateMu.Lock()
582 defer s.stateMu.Unlock()
583 if s.state < serverInitialized {
584 event.Log(ctx, "server shutdown without initialization")
585 }
586 if s.state != serverShutDown {
587 // drop all the active views
588 s.session.Shutdown(ctx)
589 s.state = serverShutDown
590 if s.tempDir != "" {
591 if err := os.RemoveAll(s.tempDir); err != nil {
592 event.Error(ctx, "removing temp dir", err)
593 }
594 }
595 }
596 return nil
597}
598
Alan Donovan25294812023-11-19 13:24:36 -0500599func (s *server) Exit(ctx context.Context) error {
Robert Findleyf7963612023-03-28 21:34:10 -0400600 ctx, done := event.Start(ctx, "lsp.Server.exit")
601 defer done()
602
Robert Findleyb15dac22022-08-30 14:40:12 -0400603 s.stateMu.Lock()
604 defer s.stateMu.Unlock()
605
606 s.client.Close()
607
608 if s.state != serverShutDown {
609 // TODO: We should be able to do better than this.
610 os.Exit(1)
611 }
Rob Findleye286d222023-04-20 17:21:15 -0400612 // We don't terminate the process on a normal exit, we just allow it to
Robert Findleyb15dac22022-08-30 14:40:12 -0400613 // close naturally if needed after the connection is closed.
614 return nil
615}