golang.org/x/tools/gopls@v0.15.3/internal/server/server.go (about) 1 // Copyright 2018 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 server defines gopls' implementation of the LSP server 6 // interface, [protocol.Server]. Call [New] to create an instance. 7 package server 8 9 import ( 10 "context" 11 "fmt" 12 "os" 13 "sync" 14 15 "golang.org/x/tools/gopls/internal/cache" 16 "golang.org/x/tools/gopls/internal/progress" 17 "golang.org/x/tools/gopls/internal/protocol" 18 "golang.org/x/tools/gopls/internal/settings" 19 "golang.org/x/tools/internal/event" 20 ) 21 22 // New creates an LSP server and binds it to handle incoming client 23 // messages on the supplied stream. 24 func New(session *cache.Session, client protocol.ClientCloser, options *settings.Options) protocol.Server { 25 const concurrentAnalyses = 1 26 // If this assignment fails to compile after a protocol 27 // upgrade, it means that one or more new methods need new 28 // stub declarations in unimplemented.go. 29 return &server{ 30 diagnostics: make(map[protocol.DocumentURI]*fileDiagnostics), 31 watchedGlobPatterns: nil, // empty 32 changedFiles: make(map[protocol.DocumentURI]unit), 33 session: session, 34 client: client, 35 diagnosticsSema: make(chan unit, concurrentAnalyses), 36 progress: progress.NewTracker(client), 37 options: options, 38 viewsToDiagnose: make(map[*cache.View]uint64), 39 } 40 } 41 42 type serverState int 43 44 const ( 45 serverCreated = serverState(iota) 46 serverInitializing // set once the server has received "initialize" request 47 serverInitialized // set once the server has received "initialized" request 48 serverShutDown 49 ) 50 51 func (s serverState) String() string { 52 switch s { 53 case serverCreated: 54 return "created" 55 case serverInitializing: 56 return "initializing" 57 case serverInitialized: 58 return "initialized" 59 case serverShutDown: 60 return "shutDown" 61 } 62 return fmt.Sprintf("(unknown state: %d)", int(s)) 63 } 64 65 // server implements the protocol.server interface. 66 type server struct { 67 client protocol.ClientCloser 68 69 stateMu sync.Mutex 70 state serverState 71 // notifications generated before serverInitialized 72 notifications []*protocol.ShowMessageParams 73 74 session *cache.Session 75 76 tempDir string 77 78 // changedFiles tracks files for which there has been a textDocument/didChange. 79 changedFilesMu sync.Mutex 80 changedFiles map[protocol.DocumentURI]unit 81 82 // folders is only valid between initialize and initialized, and holds the 83 // set of folders to build views for when we are ready. 84 // Each has a valid, non-empty 'file'-scheme URI. 85 pendingFolders []protocol.WorkspaceFolder 86 87 // watchedGlobPatterns is the set of glob patterns that we have requested 88 // the client watch on disk. It will be updated as the set of directories 89 // that the server should watch changes. 90 // The map field may be reassigned but the map is immutable. 91 watchedGlobPatternsMu sync.Mutex 92 watchedGlobPatterns map[protocol.RelativePattern]unit 93 watchRegistrationCount int 94 95 diagnosticsMu sync.Mutex 96 diagnostics map[protocol.DocumentURI]*fileDiagnostics 97 98 // diagnosticsSema limits the concurrency of diagnostics runs, which can be 99 // expensive. 100 diagnosticsSema chan unit 101 102 progress *progress.Tracker 103 104 // When the workspace fails to load, we show its status through a progress 105 // report with an error message. 106 criticalErrorStatusMu sync.Mutex 107 criticalErrorStatus *progress.WorkDone 108 109 // Track an ongoing CPU profile created with the StartProfile command and 110 // terminated with the StopProfile command. 111 ongoingProfileMu sync.Mutex 112 ongoingProfile *os.File // if non-nil, an ongoing profile is writing to this file 113 114 // Track most recently requested options. 115 optionsMu sync.Mutex 116 options *settings.Options 117 118 // # Modification tracking and diagnostics 119 // 120 // For the purpose of tracking diagnostics, we need a monotonically 121 // increasing clock. Each time a change occurs on the server, this clock is 122 // incremented and the previous diagnostics pass is cancelled. When the 123 // changed is processed, the Session (via DidModifyFiles) determines which 124 // Views are affected by the change and these views are added to the 125 // viewsToDiagnose set. Then the server calls diagnoseChangedViews 126 // in a separate goroutine. Any Views that successfully complete their 127 // diagnostics are removed from the viewsToDiagnose set, provided they haven't 128 // been subsequently marked for re-diagnosis (as determined by the latest 129 // modificationID referenced by viewsToDiagnose). 130 // 131 // In this way, we enforce eventual completeness of the diagnostic set: any 132 // views requiring diagnosis are diagnosed, though possibly at a later point 133 // in time. Notably, the logic in Session.DidModifyFiles to determines if a 134 // view needs diagnosis considers whether any packages in the view were 135 // invalidated. Consider the following sequence of snapshots for a given view 136 // V: 137 // 138 // C1 C2 139 // S1 -> S2 -> S3 140 // 141 // In this case, suppose that S1 was fully type checked, and then two changes 142 // C1 and C2 occur in rapid succession, to a file in their package graph but 143 // perhaps not enclosed by V's root. In this case, the logic of 144 // DidModifyFiles will detect that V needs to be reloaded following C1. In 145 // order for our eventual consistency to be sound, we need to avoid the race 146 // where S2 is being diagnosed, C2 arrives, and S3 is not detected as needing 147 // diagnosis because the relevant package has not yet been computed in S2. To 148 // achieve this, we only remove V from viewsToDiagnose if the diagnosis of S2 149 // completes before C2 is processed, which we can confirm by checking 150 // S2.BackgroundContext(). 151 modificationMu sync.Mutex 152 cancelPrevDiagnostics func() 153 viewsToDiagnose map[*cache.View]uint64 // View -> modification at which it last required diagnosis 154 lastModificationID uint64 // incrementing clock 155 } 156 157 func (s *server) WorkDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error { 158 ctx, done := event.Start(ctx, "lsp.Server.workDoneProgressCancel") 159 defer done() 160 161 return s.progress.Cancel(params.Token) 162 }