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