@@ -25,17 +25,22 @@ import { DocsBrowser } from './docsBrowser';
2525import { downloadHaskellLanguageServer } from './hlsBinaries' ;
2626import { executableExists } from './utils' ;
2727
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 ( ) ;
2932
3033// This is the entrypoint to our extension
3134export 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.
3944 workspace . onDidChangeWorkspaceFolders ( ( event ) => {
4045 for ( const folder of event . removed ) {
4146 const client = clients . get ( folder . uri . toString ( ) ) ;
@@ -49,8 +54,8 @@ export async function activate(context: ExtensionContext) {
4954 // Register editor commands for HIE, but only register the commands once at activation.
5055 const restartCmd = commands . registerCommand ( CommandNames . RestartHieCommandName , async ( ) => {
5156 for ( const langClient of clients . values ( ) ) {
52- await langClient . stop ( ) ;
53- langClient . start ( ) ;
57+ await langClient ? .stop ( ) ;
58+ langClient ? .start ( ) ;
5459 }
5560 } ) ;
5661 context . subscriptions . push ( restartCmd ) ;
@@ -107,7 +112,7 @@ function findLocalServer(context: ExtensionContext, uri: Uri, folder?: Workspace
107112 return null ;
108113}
109114
110- async function activateHie ( context : ExtensionContext , document : TextDocument ) {
115+ async function activeServer ( context : ExtensionContext , document : TextDocument ) {
111116 // We are only interested in Haskell files.
112117 if (
113118 ( document . languageId !== 'haskell' &&
@@ -121,15 +126,20 @@ async function activateHie(context: ExtensionContext, document: TextDocument) {
121126 const uri = document . uri ;
122127 const folder = workspace . getWorkspaceFolder ( uri ) ;
123128
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 ) ) {
126137 return ;
127138 }
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 ) ;
130141
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.
133143 const enable = workspace . getConfiguration ( 'haskell' , uri ) . enable ;
134144 if ( ! enable ) {
135145 return ;
@@ -223,35 +233,27 @@ async function activateHieNoCheck(context: ExtensionContext, uri: Uri, folder?:
223233 } ;
224234
225235 // Create the LSP client.
226- const langClient = new LanguageClient ( langName , langName , serverOptions , clientOptions , true ) ;
236+ const langClient = new LanguageClient ( langName , langName , serverOptions , clientOptions ) ;
227237
228238 // Register ClientCapabilities for stuff like window/progress
229239 langClient . registerProposedFeatures ( ) ;
230240
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-
237241 // Finally start the client and add it to the list of clients.
238242 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 ) ;
244244}
245245
246246/*
247247 * Deactivate each of the LSP servers.
248248 */
249- export function deactivate ( ) : Thenable < void > {
249+ export async function deactivate ( ) {
250250 const promises : Array < Thenable < void > > = [ ] ;
251251 for ( const client of clients . values ( ) ) {
252- promises . push ( client . stop ( ) ) ;
252+ if ( client ) {
253+ promises . push ( client . stop ( ) ) ;
254+ }
253255 }
254- return Promise . all ( promises ) . then ( ( ) => undefined ) ;
256+ await Promise . all ( promises ) ;
255257}
256258
257259function showNotInstalledErrorMessage ( uri : Uri ) {
0 commit comments