3
3
4
4
import * as path from 'path' ;
5
5
import { inject , injectable } from 'inversify' ;
6
- import { ConfigurationChangeEvent , Uri } from 'vscode' ;
6
+ import { ConfigurationChangeEvent , Uri , WorkspaceFoldersChangeEvent } from 'vscode' ;
7
7
import { LanguageServerChangeHandler } from '../activation/common/languageServerChangeHandler' ;
8
8
import {
9
9
IExtensionActivationService ,
@@ -50,7 +50,9 @@ export class LanguageServerWatcher
50
50
51
51
private workspaceInterpreters : Map < string , PythonEnvironment | undefined > ;
52
52
53
- // In a multiroot workspace scenario we will have one language server per folder.
53
+ // In a multiroot workspace scenario we may have multiple language servers running:
54
+ // When using Jedi, there will be one language server per workspace folder.
55
+ // When using Pylance, there will only be one language server for the project.
54
56
private workspaceLanguageServers : Map < string , ILanguageServerExtensionManager | undefined > ;
55
57
56
58
private languageServerChangeHandler : LanguageServerChangeHandler ;
@@ -77,6 +79,10 @@ export class LanguageServerWatcher
77
79
78
80
disposables . push ( this . workspaceService . onDidChangeConfiguration ( this . onDidChangeConfiguration . bind ( this ) ) ) ;
79
81
82
+ disposables . push (
83
+ this . workspaceService . onDidChangeWorkspaceFolders ( this . onDidChangeWorkspaceFolders . bind ( this ) ) ,
84
+ ) ;
85
+
80
86
if ( this . workspaceService . isTrusted ) {
81
87
disposables . push ( this . interpreterPathService . onDidChange ( this . onDidChangeInterpreter . bind ( this ) ) ) ;
82
88
}
@@ -113,7 +119,7 @@ export class LanguageServerWatcher
113
119
languageServerType : LanguageServerType ,
114
120
resource ?: Resource ,
115
121
) : Promise < ILanguageServerExtensionManager > {
116
- const lsResource = this . getWorkspaceKey ( resource ) ;
122
+ const lsResource = this . getWorkspaceUri ( resource ) ;
117
123
const currentInterpreter = this . workspaceInterpreters . get ( lsResource . fsPath ) ;
118
124
const interpreter = await this . interpreterService ?. getActiveInterpreter ( resource ) ;
119
125
@@ -142,6 +148,15 @@ export class LanguageServerWatcher
142
148
serverType = LanguageServerType . None ;
143
149
}
144
150
151
+ // If the language server type is Pylance or None,
152
+ // We only need to instantiate the language server once, even in multiroot workspace scenarios,
153
+ // so we only need one language server extension manager.
154
+ const key = this . getWorkspaceKey ( resource , serverType ) ;
155
+ const languageServer = this . workspaceLanguageServers . get ( key ) ;
156
+ if ( ( serverType === LanguageServerType . Node || serverType === LanguageServerType . None ) && languageServer ) {
157
+ return languageServer ;
158
+ }
159
+
145
160
// Instantiate the language server extension manager.
146
161
const languageServerExtensionManager = this . createLanguageServer ( serverType ) ;
147
162
@@ -156,16 +171,16 @@ export class LanguageServerWatcher
156
171
await languageServerExtensionManager . languageServerNotAvailable ( ) ;
157
172
}
158
173
159
- this . workspaceLanguageServers . set ( lsResource . fsPath , languageServerExtensionManager ) ;
174
+ this . workspaceLanguageServers . set ( key , languageServerExtensionManager ) ;
160
175
161
176
return languageServerExtensionManager ;
162
177
}
163
178
164
179
// ILanguageServerCache
165
180
166
181
public async get ( resource ?: Resource ) : Promise < ILanguageServer > {
167
- const lsResource = this . getWorkspaceKey ( resource ) ;
168
- let languageServerExtensionManager = this . workspaceLanguageServers . get ( lsResource . fsPath ) ;
182
+ const key = this . getWorkspaceKey ( resource , this . languageServerType ) ;
183
+ let languageServerExtensionManager = this . workspaceLanguageServers . get ( key ) ;
169
184
170
185
if ( ! languageServerExtensionManager ) {
171
186
languageServerExtensionManager = await this . startAndGetLanguageServer ( this . languageServerType , resource ) ;
@@ -177,13 +192,13 @@ export class LanguageServerWatcher
177
192
// Private methods
178
193
179
194
private stopLanguageServer ( resource ?: Resource ) : void {
180
- const lsResource = this . getWorkspaceKey ( resource ) ;
181
- const languageServerExtensionManager = this . workspaceLanguageServers . get ( lsResource . fsPath ) ;
195
+ const key = this . getWorkspaceKey ( resource , this . languageServerType ) ;
196
+ const languageServerExtensionManager = this . workspaceLanguageServers . get ( key ) ;
182
197
183
198
if ( languageServerExtensionManager ) {
184
199
languageServerExtensionManager . stopLanguageServer ( ) ;
185
200
languageServerExtensionManager . dispose ( ) ;
186
- this . workspaceLanguageServers . delete ( lsResource . fsPath ) ;
201
+ this . workspaceLanguageServers . delete ( key ) ;
187
202
}
188
203
}
189
204
@@ -228,11 +243,11 @@ export class LanguageServerWatcher
228
243
}
229
244
230
245
private async refreshLanguageServer ( resource ?: Resource ) : Promise < void > {
231
- const lsResource = this . getWorkspaceKey ( resource ) ;
246
+ const lsResource = this . getWorkspaceUri ( resource ) ;
232
247
const languageServerType = this . configurationService . getSettings ( lsResource ) . languageServer ;
233
248
234
249
if ( languageServerType !== this . languageServerType ) {
235
- this . stopLanguageServer ( lsResource ) ;
250
+ this . stopLanguageServer ( resource ) ;
236
251
await this . startLanguageServer ( languageServerType , lsResource ) ;
237
252
}
238
253
}
@@ -267,8 +282,19 @@ export class LanguageServerWatcher
267
282
}
268
283
}
269
284
270
- // Get the workspace key for the given resource, in order to query this.workspaceInterpreters and this.workspaceLanguageServers.
271
- private getWorkspaceKey ( resource ?: Resource ) : Uri {
285
+ // Watch for workspace folder changes.
286
+ private async onDidChangeWorkspaceFolders ( event : WorkspaceFoldersChangeEvent ) : Promise < void > {
287
+ // Since Jedi is the only language server type where we instantiate multiple language servers,
288
+ // Make sure to dispose of them only in that scenario.
289
+ if ( event . removed . length && this . languageServerType === LanguageServerType . Jedi ) {
290
+ event . removed . forEach ( ( workspace ) => {
291
+ this . stopLanguageServer ( workspace . uri ) ;
292
+ } ) ;
293
+ }
294
+ }
295
+
296
+ // Get the workspace Uri for the given resource, in order to query this.workspaceInterpreters and this.workspaceLanguageServers.
297
+ private getWorkspaceUri ( resource ?: Resource ) : Uri {
272
298
let uri ;
273
299
274
300
if ( resource ) {
@@ -279,6 +305,19 @@ export class LanguageServerWatcher
279
305
280
306
return uri ?? Uri . parse ( 'default' ) ;
281
307
}
308
+
309
+ // Get the key used to identify which language server extension manager is associated to which workspace.
310
+ // When using Pylance or having no LS enabled, we return a static key since there should only be one LS extension manager for these LS types.
311
+ private getWorkspaceKey ( resource : Resource | undefined , languageServerType : LanguageServerType ) : string {
312
+ switch ( languageServerType ) {
313
+ case LanguageServerType . Node :
314
+ return 'Pylance' ;
315
+ case LanguageServerType . None :
316
+ return 'None' ;
317
+ default :
318
+ return this . getWorkspaceUri ( resource ) . fsPath ;
319
+ }
320
+ }
282
321
}
283
322
284
323
function logStartup ( languageServerType : LanguageServerType , resource : Uri ) : void {
@@ -290,10 +329,10 @@ function logStartup(languageServerType: LanguageServerType, resource: Uri): void
290
329
outputLine = LanguageService . startingJedi ( ) . format ( basename ) ;
291
330
break ;
292
331
case LanguageServerType . Node :
293
- outputLine = LanguageService . startingPylance ( ) . format ( basename ) ;
332
+ outputLine = LanguageService . startingPylance ( ) ;
294
333
break ;
295
334
case LanguageServerType . None :
296
- outputLine = LanguageService . startingNone ( ) . format ( basename ) ;
335
+ outputLine = LanguageService . startingNone ( ) ;
297
336
break ;
298
337
default :
299
338
throw new Error ( `Unknown language server type: ${ languageServerType } ` ) ;
0 commit comments