@@ -25,17 +25,22 @@ import { DocsBrowser } from './docsBrowser';
25
25
import { downloadHaskellLanguageServer } from './hlsBinaries' ;
26
26
import { executableExists } from './utils' ;
27
27
28
- const clients : Map < string , LanguageClient > = new Map ( ) ;
28
+ // The current map of documents & folders to language servers.
29
+ // It may be null to indicate that we are in the process of launching a server,
30
+ // in which case don't try to launch another one for that uri
31
+ const clients : Map < string , LanguageClient | null > = new Map ( ) ;
29
32
30
33
// This is the entrypoint to our extension
31
34
export async function activate ( context : ExtensionContext ) {
32
- // Register HIE to check every time a text document gets opened, to
33
- // support multi-root workspaces.
34
-
35
- workspace . onDidOpenTextDocument ( async ( document : TextDocument ) => await activateHie ( context , document ) ) ;
36
- workspace . textDocuments . forEach ( async ( document : TextDocument ) => await activateHie ( context , document ) ) ;
37
-
38
- // Stop HIE from any workspace folders that are removed.
35
+ // (Possibly) launch the language server every time a document is opened, so
36
+ // it works across multiple workspace folders. Eventually, haskell-lsp should
37
+ // just support
38
+ // https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#workspace_workspaceFolders
39
+ // and then we can just launch one server
40
+ workspace . onDidOpenTextDocument ( async ( document : TextDocument ) => await activeServer ( context , document ) ) ;
41
+ workspace . textDocuments . forEach ( async ( document : TextDocument ) => await activeServer ( context , document ) ) ;
42
+
43
+ // Stop the server from any workspace folders that are removed.
39
44
workspace . onDidChangeWorkspaceFolders ( ( event ) => {
40
45
for ( const folder of event . removed ) {
41
46
const client = clients . get ( folder . uri . toString ( ) ) ;
@@ -49,8 +54,8 @@ export async function activate(context: ExtensionContext) {
49
54
// Register editor commands for HIE, but only register the commands once at activation.
50
55
const restartCmd = commands . registerCommand ( CommandNames . RestartHieCommandName , async ( ) => {
51
56
for ( const langClient of clients . values ( ) ) {
52
- await langClient . stop ( ) ;
53
- langClient . start ( ) ;
57
+ await langClient ? .stop ( ) ;
58
+ langClient ? .start ( ) ;
54
59
}
55
60
} ) ;
56
61
context . subscriptions . push ( restartCmd ) ;
@@ -107,7 +112,7 @@ function findLocalServer(context: ExtensionContext, uri: Uri, folder?: Workspace
107
112
return null ;
108
113
}
109
114
110
- async function activateHie ( context : ExtensionContext , document : TextDocument ) {
115
+ async function activeServer ( context : ExtensionContext , document : TextDocument ) {
111
116
// We are only interested in Haskell files.
112
117
if (
113
118
( document . languageId !== 'haskell' &&
@@ -121,15 +126,20 @@ async function activateHie(context: ExtensionContext, document: TextDocument) {
121
126
const uri = document . uri ;
122
127
const folder = workspace . getWorkspaceFolder ( uri ) ;
123
128
124
- // If the client already has an LSP server for this folder, then don't start a new one.
125
- if ( folder && clients . has ( folder . uri . toString ( ) ) ) {
129
+ activateServerForFolder ( context , uri , folder ) ;
130
+ }
131
+
132
+ async function activateServerForFolder ( context : ExtensionContext , uri : Uri , folder ?: WorkspaceFolder ) {
133
+ const clientsKey = folder ? folder . uri . toString ( ) : uri . toString ( ) ;
134
+
135
+ // If the client already has an LSP server for this uri/folder, then don't start a new one.
136
+ if ( clients . has ( clientsKey ) ) {
126
137
return ;
127
138
}
128
- activateHieNoCheck ( context , uri , folder ) ;
129
- }
139
+ // Set the key to null to prevent multiple servers being launched at once
140
+ clients . set ( clientsKey , null ) ;
130
141
131
- async function activateHieNoCheck ( context : ExtensionContext , uri : Uri , folder ?: WorkspaceFolder ) {
132
- // Stop right here, if HIE is disabled in the resource/workspace folder.
142
+ // Stop right here, if Haskell is disabled in the resource/workspace folder.
133
143
const enable = workspace . getConfiguration ( 'haskell' , uri ) . enable ;
134
144
if ( ! enable ) {
135
145
return ;
@@ -223,35 +233,27 @@ async function activateHieNoCheck(context: ExtensionContext, uri: Uri, folder?:
223
233
} ;
224
234
225
235
// Create the LSP client.
226
- const langClient = new LanguageClient ( langName , langName , serverOptions , clientOptions , true ) ;
236
+ const langClient = new LanguageClient ( langName , langName , serverOptions , clientOptions ) ;
227
237
228
238
// Register ClientCapabilities for stuff like window/progress
229
239
langClient . registerProposedFeatures ( ) ;
230
240
231
- // If the client already has an LSP server, then don't start a new one.
232
- // We check this again, as there may be multiple parallel requests.
233
- if ( folder && clients . has ( folder . uri . toString ( ) ) ) {
234
- return ;
235
- }
236
-
237
241
// Finally start the client and add it to the list of clients.
238
242
langClient . start ( ) ;
239
- if ( folder ) {
240
- clients . set ( folder . uri . toString ( ) , langClient ) ;
241
- } else {
242
- clients . set ( uri . toString ( ) , langClient ) ;
243
- }
243
+ clients . set ( clientsKey , langClient ) ;
244
244
}
245
245
246
246
/*
247
247
* Deactivate each of the LSP servers.
248
248
*/
249
- export function deactivate ( ) : Thenable < void > {
249
+ export async function deactivate ( ) {
250
250
const promises : Array < Thenable < void > > = [ ] ;
251
251
for ( const client of clients . values ( ) ) {
252
- promises . push ( client . stop ( ) ) ;
252
+ if ( client ) {
253
+ promises . push ( client . stop ( ) ) ;
254
+ }
253
255
}
254
- return Promise . all ( promises ) . then ( ( ) => undefined ) ;
256
+ await Promise . all ( promises ) ;
255
257
}
256
258
257
259
function showNotInstalledErrorMessage ( uri : Uri ) {
0 commit comments