@@ -15,6 +15,7 @@ import {
1515 ExecutableOptions ,
1616 LanguageClient ,
1717 LanguageClientOptions ,
18+ Logger ,
1819 RevealOutputChannelOn ,
1920 ServerOptions ,
2021 TransportKind ,
@@ -23,7 +24,7 @@ import { CommandNames } from './commands/constants';
2324import { ImportIdentifier } from './commands/importIdentifier' ;
2425import { DocsBrowser } from './docsBrowser' ;
2526import { downloadHaskellLanguageServer } from './hlsBinaries' ;
26- import { executableExists } from './utils' ;
27+ import { executableExists , ExtensionLogger } from './utils' ;
2728
2829// The current map of documents & folders to language servers.
2930// It may be null to indicate that we are in the process of launching a server,
@@ -45,7 +46,10 @@ export async function activate(context: ExtensionContext) {
4546 for ( const folder of event . removed ) {
4647 const client = clients . get ( folder . uri . toString ( ) ) ;
4748 if ( client ) {
48- clients . delete ( folder . uri . toString ( ) ) ;
49+ const uri = folder . uri . toString ( ) ;
50+ client . info ( `Deleting folder for clients: ${ uri } ` ) ;
51+ clients . delete ( uri ) ;
52+ client . info ( 'Stopping the server' ) ;
4953 client . stop ( ) ;
5054 }
5155 }
@@ -54,12 +58,35 @@ export async function activate(context: ExtensionContext) {
5458 // Register editor commands for HIE, but only register the commands once at activation.
5559 const restartCmd = commands . registerCommand ( CommandNames . RestartServerCommandName , async ( ) => {
5660 for ( const langClient of clients . values ( ) ) {
61+ langClient ?. info ( 'Stopping the server' ) ;
5762 await langClient ?. stop ( ) ;
63+ langClient ?. info ( 'Starting the server' ) ;
5864 langClient ?. start ( ) ;
5965 }
6066 } ) ;
67+
6168 context . subscriptions . push ( restartCmd ) ;
6269
70+ const stopCmd = commands . registerCommand ( CommandNames . StopServerCommandName , async ( ) => {
71+ for ( const langClient of clients . values ( ) ) {
72+ langClient ?. info ( 'Stopping the server' ) ;
73+ await langClient ?. stop ( ) ;
74+ langClient ?. info ( 'Server stopped' ) ;
75+ }
76+ } ) ;
77+
78+ context . subscriptions . push ( stopCmd ) ;
79+
80+ const startCmd = commands . registerCommand ( CommandNames . StartServerCommandName , async ( ) => {
81+ for ( const langClient of clients . values ( ) ) {
82+ langClient ?. info ( 'Starting the server' ) ;
83+ langClient ?. start ( ) ;
84+ langClient ?. info ( 'Server started' ) ;
85+ }
86+ } ) ;
87+
88+ context . subscriptions . push ( startCmd ) ;
89+
6390 context . subscriptions . push ( ImportIdentifier . registerCommand ( ) ) ;
6491
6592 // Set up the documentation browser.
@@ -70,30 +97,31 @@ export async function activate(context: ExtensionContext) {
7097 context . subscriptions . push ( openOnHackageDisposable ) ;
7198}
7299
73- function findManualExecutable ( uri : Uri , folder ?: WorkspaceFolder ) : string | null {
100+ function findManualExecutable ( logger : Logger , uri : Uri , folder ?: WorkspaceFolder ) : string | null {
74101 let exePath = workspace . getConfiguration ( 'haskell' , uri ) . serverExecutablePath ;
75102 if ( exePath === '' ) {
76103 return null ;
77104 }
78-
105+ logger . info ( `Trying to find the server executable in: ${ exePath } ` ) ;
79106 // Substitute path variables with their corresponding locations.
80107 exePath = exePath . replace ( '${HOME}' , os . homedir ) . replace ( '${home}' , os . homedir ) . replace ( / ^ ~ / , os . homedir ) ;
81108 if ( folder ) {
82109 exePath = exePath . replace ( '${workspaceFolder}' , folder . uri . path ) . replace ( '${workspaceRoot}' , folder . uri . path ) ;
83110 }
84-
111+ logger . info ( `Location after path variables subsitution: ${ exePath } ` ) ;
85112 if ( ! executableExists ( exePath ) ) {
86- throw new Error ( `serverExecutablePath is set to ${ exePath } but it doesn't exist and is not on the PATH` ) ;
113+ throw new Error ( `serverExecutablePath is set to ${ exePath } but it doesn't exist and it is not on the PATH` ) ;
87114 }
88115 return exePath ;
89116}
90117
91118/** Searches the PATH for whatever is set in serverVariant */
92- function findLocalServer ( context : ExtensionContext , uri : Uri , folder ?: WorkspaceFolder ) : string | null {
119+ function findLocalServer ( context : ExtensionContext , logger : Logger , uri : Uri , folder ?: WorkspaceFolder ) : string | null {
93120 const exes : string [ ] = [ 'haskell-language-server-wrapper' , 'haskell-language-server' ] ;
94-
121+ logger . info ( `Searching for server executables ${ exes . join ( ',' ) } in $PATH` ) ;
95122 for ( const exe of exes ) {
96123 if ( executableExists ( exe ) ) {
124+ logger . info ( `Found server executable in $PATH: ${ exe } ` ) ;
97125 return exe ;
98126 }
99127 }
@@ -120,6 +148,9 @@ async function activeServer(context: ExtensionContext, document: TextDocument) {
120148
121149async function activateServerForFolder ( context : ExtensionContext , uri : Uri , folder ?: WorkspaceFolder ) {
122150 const clientsKey = folder ? folder . uri . toString ( ) : uri . toString ( ) ;
151+ // Set a unique name per workspace folder (useful for multi-root workspaces).
152+ const langName = 'Haskell' + ( folder ? ` (${ folder . name } )` : '' ) ;
153+ const outputChannel : OutputChannel = window . createOutputChannel ( langName ) ;
123154
124155 // If the client already has an LSP server for this uri/folder, then don't start a new one.
125156 if ( clients . has ( clientsKey ) ) {
@@ -129,21 +160,25 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
129160 clients . set ( clientsKey , null ) ;
130161
131162 const logLevel = workspace . getConfiguration ( 'haskell' , uri ) . trace . server ;
163+ const clientLogLevel = workspace . getConfiguration ( 'haskell' , uri ) . trace . client ;
132164 const logFile = workspace . getConfiguration ( 'haskell' , uri ) . logFile ;
133165
166+ const logger : Logger = new ExtensionLogger ( 'client' , clientLogLevel , outputChannel ) ;
167+
134168 let serverExecutable ;
135169 try {
136170 // Try and find local installations first
137- serverExecutable = findManualExecutable ( uri , folder ) ?? findLocalServer ( context , uri , folder ) ;
171+ serverExecutable = findManualExecutable ( logger , uri , folder ) ?? findLocalServer ( context , logger , uri , folder ) ;
138172 if ( serverExecutable === null ) {
139173 // If not, then try to download haskell-language-server binaries if it's selected
140- serverExecutable = await downloadHaskellLanguageServer ( context , uri , folder ) ;
174+ serverExecutable = await downloadHaskellLanguageServer ( context , logger , uri , folder ) ;
141175 if ( ! serverExecutable ) {
142176 return ;
143177 }
144178 }
145179 } catch ( e ) {
146180 if ( e instanceof Error ) {
181+ logger . error ( `Error getting the server executable: ${ e . message } ` ) ;
147182 window . showErrorMessage ( e . message ) ;
148183 }
149184 return ;
@@ -162,6 +197,12 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
162197 // If we're operating on a standalone file (i.e. not in a folder) then we need
163198 // to launch the server in a reasonable current directory. Otherwise the cradle
164199 // guessing logic in hie-bios will be wrong!
200+ if ( folder ) {
201+ logger . info ( `Activating the language server in the workspace folder: ${ folder ?. uri . fsPath } ` ) ;
202+ } else {
203+ logger . info ( `Activating the language server in the parent dir of the file: ${ uri . fsPath } ` ) ;
204+ }
205+
165206 const exeOptions : ExecutableOptions = {
166207 cwd : folder ? undefined : path . dirname ( uri . fsPath ) ,
167208 } ;
@@ -173,15 +214,14 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
173214 debug : { command : serverExecutable , transport : TransportKind . stdio , args, options : exeOptions } ,
174215 } ;
175216
176- // Set a unique name per workspace folder (useful for multi-root workspaces).
177- const langName = 'Haskell' + ( folder ? ` (${ folder . name } )` : '' ) ;
178- const outputChannel : OutputChannel = window . createOutputChannel ( langName ) ;
179- outputChannel . appendLine ( '[client] run command: "' + serverExecutable + ' ' + args . join ( ' ' ) + '"' ) ;
180- outputChannel . appendLine ( '[client] debug command: "' + serverExecutable + ' ' + args . join ( ' ' ) + '"' ) ;
181-
182- outputChannel . appendLine ( `[client] server cwd: ${ exeOptions . cwd } ` ) ;
217+ logger . info ( `run command: ${ serverExecutable } ${ args . join ( ' ' ) } ` ) ;
218+ logger . info ( `debug command: ${ serverExecutable } ${ args . join ( ' ' ) } ` ) ;
219+ if ( exeOptions . cwd ) {
220+ logger . info ( `server cwd: ${ exeOptions . cwd } ` ) ;
221+ }
183222
184223 const pat = folder ? `${ folder . uri . fsPath } /**/*` : '**/*' ;
224+ logger . info ( `document selector patten: ${ pat } ` ) ;
185225 const clientOptions : LanguageClientOptions = {
186226 // Use the document selector to only notify the LSP on files inside the folder
187227 // path for the specific workspace.
@@ -213,6 +253,7 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
213253 langClient . registerProposedFeatures ( ) ;
214254
215255 // Finally start the client and add it to the list of clients.
256+ logger . info ( 'Starting language server' ) ;
216257 langClient . start ( ) ;
217258 clients . set ( clientsKey , langClient ) ;
218259}
0 commit comments