1
- import * as path from 'path' ;
2
- import {
3
- commands ,
4
- env ,
5
- ExtensionContext ,
6
- OutputChannel ,
7
- TextDocument ,
8
- Uri ,
9
- window ,
10
- workspace ,
11
- WorkspaceFolder ,
12
- } from 'vscode' ;
1
+ import { commands , env , ExtensionContext , TextDocument , Uri , window , workspace , WorkspaceFolder } from 'vscode' ;
13
2
import {
14
3
ExecutableOptions ,
15
4
LanguageClient ,
@@ -22,7 +11,8 @@ import { RestartServerCommandName, StartServerCommandName, StopServerCommandName
22
11
import * as DocsBrowser from './docsBrowser' ;
23
12
import { HlsError , MissingToolError , NoMatchingHls } from './errors' ;
24
13
import { callAsync , findHaskellLanguageServer , IEnvVars } from './hlsBinaries' ;
25
- import { addPathToProcessPath , comparePVP , expandHomeDir , ExtensionLogger } from './utils' ;
14
+ import { addPathToProcessPath , comparePVP } from './utils' ;
15
+ import { initConfig , initLoggerFromConfig , logConfig } from './config' ;
26
16
27
17
// The current map of documents & folders to language servers.
28
18
// It may be null to indicate that we are in the process of launching a server,
@@ -112,47 +102,25 @@ async function activeServer(context: ExtensionContext, document: TextDocument) {
112
102
113
103
async function activateServerForFolder ( context : ExtensionContext , uri : Uri , folder ?: WorkspaceFolder ) {
114
104
const clientsKey = folder ? folder . uri . toString ( ) : uri . toString ( ) ;
115
- // Set a unique name per workspace folder (useful for multi-root workspaces).
116
- const langName = 'Haskell' + ( folder ? ` (${ folder . name } )` : '' ) ;
117
-
118
105
// If the client already has an LSP server for this uri/folder, then don't start a new one.
119
106
if ( clients . has ( clientsKey ) ) {
120
107
return ;
121
108
}
122
-
123
- const currentWorkingDir = folder ? folder . uri . fsPath : path . dirname ( uri . fsPath ) ;
124
-
125
109
// Set the key to null to prevent multiple servers being launched at once
126
110
clients . set ( clientsKey , null ) ;
127
111
128
- const logLevel = workspace . getConfiguration ( 'haskell' , uri ) . trace . server ;
129
- const clientLogLevel = workspace . getConfiguration ( 'haskell' , uri ) . trace . client ;
130
- const logFile : string = workspace . getConfiguration ( 'haskell' , uri ) . logFile ;
112
+ const config = initConfig ( workspace . getConfiguration ( 'haskell' , uri ) , uri , folder ) ;
113
+ const logger : Logger = initLoggerFromConfig ( config ) ;
131
114
132
- const outputChannel : OutputChannel = window . createOutputChannel ( langName ) ;
133
-
134
- const logFilePath = logFile !== '' ? path . resolve ( currentWorkingDir , expandHomeDir ( logFile ) ) : undefined ;
135
- const logger : Logger = new ExtensionLogger ( 'client' , clientLogLevel , outputChannel , logFilePath ) ;
136
- if ( logFilePath ) {
137
- logger . info ( `Writing client log to file ${ logFilePath } ` ) ;
138
- }
139
- logger . log ( 'Environment variables:' ) ;
140
- Object . entries ( process . env ) . forEach ( ( [ key , value ] : [ string , string | undefined ] ) => {
141
- // only list environment variables that we actually care about.
142
- // this makes it safe for users to just paste the logs to whoever,
143
- // and avoids leaking secrets.
144
- if ( [ 'PATH' ] . includes ( key ) ) {
145
- logger . log ( ` ${ key } : ${ value } ` ) ;
146
- }
147
- } ) ;
115
+ logConfig ( logger , config ) ;
148
116
149
117
let serverExecutable : string ;
150
118
let addInternalServerPath : string | undefined ; // if we download HLS, add that bin dir to PATH
151
119
try {
152
120
[ serverExecutable , addInternalServerPath ] = await findHaskellLanguageServer (
153
121
context ,
154
122
logger ,
155
- currentWorkingDir ,
123
+ config . workingDir ,
156
124
folder ,
157
125
) ;
158
126
if ( ! serverExecutable ) {
@@ -190,31 +158,10 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
190
158
return ;
191
159
}
192
160
193
- let args : string [ ] = [ '--lsp' ] ;
194
-
195
- if ( logLevel === 'messages' ) {
196
- args = args . concat ( [ '-d' ] ) ;
197
- }
198
-
199
- if ( logFile !== '' ) {
200
- args = args . concat ( [ '-l' , logFile ] ) ;
201
- }
202
-
203
- const extraArgs : string = workspace . getConfiguration ( 'haskell' , uri ) . serverExtraArgs ;
204
- if ( extraArgs !== '' ) {
205
- args = args . concat ( extraArgs . split ( ' ' ) ) ;
206
- }
207
-
208
- const cabalFileSupport : 'automatic' | 'enable' | 'disable' = workspace . getConfiguration (
209
- 'haskell' ,
210
- uri ,
211
- ) . supportCabalFiles ;
212
- logger . info ( `Support for '.cabal' files: ${ cabalFileSupport } ` ) ;
213
-
214
161
// If we're operating on a standalone file (i.e. not in a folder) then we need
215
162
// to launch the server in a reasonable current directory. Otherwise the cradle
216
163
// guessing logic in hie-bios will be wrong!
217
- let cwdMsg = `Activating the language server in working dir: ${ currentWorkingDir } ` ;
164
+ let cwdMsg = `Activating the language server in working dir: ${ config . workingDir } ` ;
218
165
if ( folder ) {
219
166
cwdMsg += ' (the workspace folder)' ;
220
167
} else {
@@ -231,22 +178,19 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
231
178
} ;
232
179
}
233
180
const exeOptions : ExecutableOptions = {
234
- cwd : folder ? folder . uri . fsPath : path . dirname ( uri . fsPath ) ,
181
+ cwd : config . workingDir ,
235
182
env : { ...process . env , ...serverEnvironment } ,
236
183
} ;
237
184
238
- // We don't want empty strings in our args
239
- args = args . map ( ( x ) => x . trim ( ) ) . filter ( ( x ) => x !== '' ) ;
240
-
241
185
// For our intents and purposes, the server should be launched the same way in
242
186
// both debug and run mode.
243
187
const serverOptions : ServerOptions = {
244
- run : { command : serverExecutable , args, options : exeOptions } ,
245
- debug : { command : serverExecutable , args, options : exeOptions } ,
188
+ run : { command : serverExecutable , args : config . serverArgs , options : exeOptions } ,
189
+ debug : { command : serverExecutable , args : config . serverArgs , options : exeOptions } ,
246
190
} ;
247
191
248
- logger . info ( `run command: ${ serverExecutable } ${ args . join ( ' ' ) } ` ) ;
249
- logger . info ( `debug command: ${ serverExecutable } ${ args . join ( ' ' ) } ` ) ;
192
+ logger . info ( `run command: ${ serverExecutable } ${ config . serverArgs . join ( ' ' ) } ` ) ;
193
+ logger . info ( `debug command: ${ serverExecutable } ${ config . serverArgs . join ( ' ' ) } ` ) ;
250
194
if ( exeOptions . cwd ) {
251
195
logger . info ( `server cwd: ${ exeOptions . cwd } ` ) ;
252
196
}
@@ -268,13 +212,19 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
268
212
269
213
const documentSelector = [ ...haskellDocumentSelector ] ;
270
214
215
+ const cabalFileSupport : 'automatic' | 'enable' | 'disable' = workspace . getConfiguration (
216
+ 'haskell' ,
217
+ uri ,
218
+ ) . supportCabalFiles ;
219
+ logger . info ( `Support for '.cabal' files: ${ cabalFileSupport } ` ) ;
220
+
271
221
switch ( cabalFileSupport ) {
272
222
case 'automatic' :
273
223
const hlsVersion = await callAsync (
274
224
serverExecutable ,
275
225
[ '--numeric-version' ] ,
276
226
logger ,
277
- currentWorkingDir ,
227
+ config . workingDir ,
278
228
undefined /* this command is very fast, don't show anything */ ,
279
229
false ,
280
230
serverEnvironment ,
@@ -301,10 +251,10 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
301
251
// Synchronize the setting section 'haskell' to the server.
302
252
configurationSection : 'haskell' ,
303
253
} ,
304
- diagnosticCollectionName : langName ,
254
+ diagnosticCollectionName : config . langName ,
305
255
revealOutputChannelOn : RevealOutputChannelOn . Never ,
306
- outputChannel,
307
- outputChannelName : langName ,
256
+ outputChannel : config . outputChannel ,
257
+ outputChannelName : config . langName ,
308
258
middleware : {
309
259
provideHover : DocsBrowser . hoverLinksMiddlewareHook ,
310
260
provideCompletionItem : DocsBrowser . completionLinksMiddlewareHook ,
@@ -314,15 +264,15 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
314
264
} ;
315
265
316
266
// Create the LSP client.
317
- const langClient = new LanguageClient ( 'haskell' , langName , serverOptions , clientOptions ) ;
267
+ const langClient = new LanguageClient ( 'haskell' , config . langName , serverOptions , clientOptions ) ;
318
268
319
269
// Register ClientCapabilities for stuff like window/progress
320
270
langClient . registerProposedFeatures ( ) ;
321
271
322
272
// Finally start the client and add it to the list of clients.
323
273
logger . info ( 'Starting language server' ) ;
324
- langClient . start ( ) ;
325
274
clients . set ( clientsKey , langClient ) ;
275
+ await langClient . start ( ) ;
326
276
}
327
277
328
278
/*
0 commit comments