cuelang.org/go@v0.13.0/internal/golangorgx/gopls/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 "cuelang.org/go/internal/golangorgx/gopls/cache" 16 "cuelang.org/go/internal/golangorgx/gopls/progress" 17 "cuelang.org/go/internal/golangorgx/gopls/protocol" 18 "cuelang.org/go/internal/golangorgx/gopls/settings" 19 "cuelang.org/go/internal/golangorgx/tools/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 // 86 // TODO(myitcv): it doesn't feel clean having this state at the "same level" 87 // as other server state. This field is only relevant for the time between 88 // the call to Initialize and notification of Initialized. 89 pendingFolders []protocol.WorkspaceFolder 90 91 // watchedGlobPatterns is the set of glob patterns that we have requested 92 // the client watch on disk. It will be updated as the set of directories 93 // that the server should watch changes. 94 // The map field may be reassigned but the map is immutable. 95 watchedGlobPatternsMu sync.Mutex 96 watchedGlobPatterns map[protocol.RelativePattern]unit 97 watchRegistrationCount int 98 99 diagnosticsMu sync.Mutex 100 diagnostics map[protocol.DocumentURI]*fileDiagnostics 101 102 // diagnosticsSema limits the concurrency of diagnostics runs, which can be 103 // expensive. 104 diagnosticsSema chan unit 105 106 progress *progress.Tracker 107 108 // When the workspace fails to load, we show its status through a progress 109 // report with an error message. 110 criticalErrorStatusMu sync.Mutex 111 criticalErrorStatus *progress.WorkDone 112 113 // Track an ongoing CPU profile created with the StartProfile command and 114 // terminated with the StopProfile command. 115 ongoingProfileMu sync.Mutex 116 ongoingProfile *os.File // if non-nil, an ongoing profile is writing to this file 117 118 // Track most recently requested options. 119 optionsMu sync.Mutex 120 options *settings.Options 121 122 // # Modification tracking and diagnostics 123 // 124 // For the purpose of tracking diagnostics, we need a monotonically 125 // increasing clock. Each time a change occurs on the server, this clock is 126 // incremented and the previous diagnostics pass is cancelled. When the 127 // changed is processed, the Session (via DidModifyFiles) determines which 128 // Views are affected by the change and these views are added to the 129 // viewsToDiagnose set. Then the server calls diagnoseChangedViews 130 // in a separate goroutine. Any Views that successfully complete their 131 // diagnostics are removed from the viewsToDiagnose set, provided they haven't 132 // been subsequently marked for re-diagnosis (as determined by the latest 133 // modificationID referenced by viewsToDiagnose). 134 // 135 // In this way, we enforce eventual completeness of the diagnostic set: any 136 // views requiring diagnosis are diagnosed, though possibly at a later point 137 // in time. Notably, the logic in Session.DidModifyFiles to determines if a 138 // view needs diagnosis considers whether any packages in the view were 139 // invalidated. Consider the following sequence of snapshots for a given view 140 // V: 141 // 142 // C1 C2 143 // S1 -> S2 -> S3 144 // 145 // In this case, suppose that S1 was fully type checked, and then two changes 146 // C1 and C2 occur in rapid succession, to a file in their package graph but 147 // perhaps not enclosed by V's root. In this case, the logic of 148 // DidModifyFiles will detect that V needs to be reloaded following C1. In 149 // order for our eventual consistency to be sound, we need to avoid the race 150 // where S2 is being diagnosed, C2 arrives, and S3 is not detected as needing 151 // diagnosis because the relevant package has not yet been computed in S2. To 152 // achieve this, we only remove V from viewsToDiagnose if the diagnosis of S2 153 // completes before C2 is processed, which we can confirm by checking 154 // S2.BackgroundContext(). 155 modificationMu sync.Mutex 156 cancelPrevDiagnostics func() 157 viewsToDiagnose map[*cache.View]uint64 // View -> modification at which it last required diagnosis 158 lastModificationID uint64 // incrementing clock 159 } 160 161 func (s *server) WorkDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error { 162 ctx, done := event.Start(ctx, "lsp.Server.workDoneProgressCancel") 163 defer done() 164 165 return s.progress.Cancel(params.Token) 166 }