@@ -15,6 +15,7 @@ import {
15
15
ExecutableOptions ,
16
16
LanguageClient ,
17
17
LanguageClientOptions ,
18
+ Logger ,
18
19
RevealOutputChannelOn ,
19
20
ServerOptions ,
20
21
TransportKind ,
@@ -23,7 +24,7 @@ import { CommandNames } from './commands/constants';
23
24
import { ImportIdentifier } from './commands/importIdentifier' ;
24
25
import { DocsBrowser } from './docsBrowser' ;
25
26
import { downloadHaskellLanguageServer } from './hlsBinaries' ;
26
- import { executableExists } from './utils' ;
27
+ import { executableExists , ExtensionLogger } from './utils' ;
27
28
28
29
// The current map of documents & folders to language servers.
29
30
// 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) {
45
46
for ( const folder of event . removed ) {
46
47
const client = clients . get ( folder . uri . toString ( ) ) ;
47
48
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' ) ;
49
53
client . stop ( ) ;
50
54
}
51
55
}
@@ -54,12 +58,35 @@ export async function activate(context: ExtensionContext) {
54
58
// Register editor commands for HIE, but only register the commands once at activation.
55
59
const restartCmd = commands . registerCommand ( CommandNames . RestartServerCommandName , async ( ) => {
56
60
for ( const langClient of clients . values ( ) ) {
61
+ langClient ?. info ( 'Stopping the server' ) ;
57
62
await langClient ?. stop ( ) ;
63
+ langClient ?. info ( 'Starting the server' ) ;
58
64
langClient ?. start ( ) ;
59
65
}
60
66
} ) ;
67
+
61
68
context . subscriptions . push ( restartCmd ) ;
62
69
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
+
63
90
context . subscriptions . push ( ImportIdentifier . registerCommand ( ) ) ;
64
91
65
92
// Set up the documentation browser.
@@ -70,30 +97,31 @@ export async function activate(context: ExtensionContext) {
70
97
context . subscriptions . push ( openOnHackageDisposable ) ;
71
98
}
72
99
73
- function findManualExecutable ( uri : Uri , folder ?: WorkspaceFolder ) : string | null {
100
+ function findManualExecutable ( logger : Logger , uri : Uri , folder ?: WorkspaceFolder ) : string | null {
74
101
let exePath = workspace . getConfiguration ( 'haskell' , uri ) . serverExecutablePath ;
75
102
if ( exePath === '' ) {
76
103
return null ;
77
104
}
78
-
105
+ logger . info ( `Trying to find the server executable in: ${ exePath } ` ) ;
79
106
// Substitute path variables with their corresponding locations.
80
107
exePath = exePath . replace ( '${HOME}' , os . homedir ) . replace ( '${home}' , os . homedir ) . replace ( / ^ ~ / , os . homedir ) ;
81
108
if ( folder ) {
82
109
exePath = exePath . replace ( '${workspaceFolder}' , folder . uri . path ) . replace ( '${workspaceRoot}' , folder . uri . path ) ;
83
110
}
84
-
111
+ logger . info ( `Location after path variables subsitution: ${ exePath } ` ) ;
85
112
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` ) ;
87
114
}
88
115
return exePath ;
89
116
}
90
117
91
118
/** 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 {
93
120
const exes : string [ ] = [ 'haskell-language-server-wrapper' , 'haskell-language-server' ] ;
94
-
121
+ logger . info ( `Searching for server executables ${ exes . join ( ',' ) } in $PATH` ) ;
95
122
for ( const exe of exes ) {
96
123
if ( executableExists ( exe ) ) {
124
+ logger . info ( `Found server executable in $PATH: ${ exe } ` ) ;
97
125
return exe ;
98
126
}
99
127
}
@@ -120,6 +148,9 @@ async function activeServer(context: ExtensionContext, document: TextDocument) {
120
148
121
149
async function activateServerForFolder ( context : ExtensionContext , uri : Uri , folder ?: WorkspaceFolder ) {
122
150
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 ) ;
123
154
124
155
// If the client already has an LSP server for this uri/folder, then don't start a new one.
125
156
if ( clients . has ( clientsKey ) ) {
@@ -129,21 +160,25 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
129
160
clients . set ( clientsKey , null ) ;
130
161
131
162
const logLevel = workspace . getConfiguration ( 'haskell' , uri ) . trace . server ;
163
+ const clientLogLevel = workspace . getConfiguration ( 'haskell' , uri ) . trace . client ;
132
164
const logFile = workspace . getConfiguration ( 'haskell' , uri ) . logFile ;
133
165
166
+ const logger : Logger = new ExtensionLogger ( 'client' , clientLogLevel , outputChannel ) ;
167
+
134
168
let serverExecutable ;
135
169
try {
136
170
// 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 ) ;
138
172
if ( serverExecutable === null ) {
139
173
// 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 ) ;
141
175
if ( ! serverExecutable ) {
142
176
return ;
143
177
}
144
178
}
145
179
} catch ( e ) {
146
180
if ( e instanceof Error ) {
181
+ logger . error ( `Error getting the server executable: ${ e . message } ` ) ;
147
182
window . showErrorMessage ( e . message ) ;
148
183
}
149
184
return ;
@@ -162,6 +197,12 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
162
197
// If we're operating on a standalone file (i.e. not in a folder) then we need
163
198
// to launch the server in a reasonable current directory. Otherwise the cradle
164
199
// 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
+
165
206
const exeOptions : ExecutableOptions = {
166
207
cwd : folder ? undefined : path . dirname ( uri . fsPath ) ,
167
208
} ;
@@ -173,15 +214,14 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
173
214
debug : { command : serverExecutable , transport : TransportKind . stdio , args, options : exeOptions } ,
174
215
} ;
175
216
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
+ }
183
222
184
223
const pat = folder ? `${ folder . uri . fsPath } /**/*` : '**/*' ;
224
+ logger . info ( `document selector patten: ${ pat } ` ) ;
185
225
const clientOptions : LanguageClientOptions = {
186
226
// Use the document selector to only notify the LSP on files inside the folder
187
227
// path for the specific workspace.
@@ -213,6 +253,7 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
213
253
langClient . registerProposedFeatures ( ) ;
214
254
215
255
// Finally start the client and add it to the list of clients.
256
+ logger . info ( 'Starting language server' ) ;
216
257
langClient . start ( ) ;
217
258
clients . set ( clientsKey , langClient ) ;
218
259
}
0 commit comments