@@ -17,15 +17,11 @@ import {
17
17
WorkspaceFolder ,
18
18
} from 'vscode' ;
19
19
import { Logger } from 'vscode-languageclient' ;
20
- import { executableExists , httpsGetSilently , resolvePathPlaceHolders } from './utils' ;
20
+ import { executableExists , httpsGetSilently , resolvePathPlaceHolders , IEnvVars , addPathToProcessPath , resolveServerEnvironmentPATH } from './utils' ;
21
+ export { IEnvVars }
21
22
22
23
export type ReleaseMetadata = Map < string , Map < string , Map < string , string [ ] > > > ;
23
24
24
- // Used for environment variables later on
25
- export interface IEnvVars {
26
- [ key : string ] : string ;
27
- }
28
-
29
25
type ManageHLS = 'GHCup' | 'PATH' ;
30
26
let manageHLS = workspace . getConfiguration ( 'haskell' ) . get ( 'manageHLS' ) as ManageHLS | null ;
31
27
@@ -103,9 +99,10 @@ async function callAsync(
103
99
reject : ( reason ?: any ) => void
104
100
) => void
105
101
) : Promise < string > {
106
- let newEnv : IEnvVars = workspace . getConfiguration ( 'haskell' ) . get ( 'serverEnvironment' ) || { } ;
107
- newEnv = Object . assign ( process . env , newEnv ) ;
108
- newEnv = Object . assign ( newEnv , ( envAdd || { } ) ) ;
102
+ let newEnv : IEnvVars = await resolveServerEnvironmentPATH ( workspace . getConfiguration ( 'haskell' ) . get ( 'serverEnvironment' ) || { } ) ;
103
+ newEnv = { ...process . env as IEnvVars , ...newEnv } ;
104
+ newEnv = { ...newEnv , ...( envAdd || { } ) } ;
105
+ logger . info ( `newEnv: ${ newEnv . PATH ! . split ( ':' ) } ` ) ;
109
106
return window . withProgress (
110
107
{
111
108
location : ProgressLocation . Notification ,
@@ -162,12 +159,12 @@ async function callAsync(
162
159
163
160
/** Gets serverExecutablePath and fails if it's not set.
164
161
*/
165
- function findServerExecutable ( context : ExtensionContext , logger : Logger , folder ?: WorkspaceFolder ) : string {
162
+ async function findServerExecutable ( context : ExtensionContext , logger : Logger , folder ?: WorkspaceFolder ) : Promise < string > {
166
163
let exePath = workspace . getConfiguration ( 'haskell' ) . get ( 'serverExecutablePath' ) as string ;
167
164
logger . info ( `Trying to find the server executable in: ${ exePath } ` ) ;
168
165
exePath = resolvePathPlaceHolders ( exePath , folder ) ;
169
166
logger . log ( `Location after path variables substitution: ${ exePath } ` ) ;
170
- if ( executableExists ( exePath ) ) {
167
+ if ( await executableExists ( exePath ) ) {
171
168
return exePath ;
172
169
} else {
173
170
const msg = `Could not find a HLS binary at ${ exePath } ! Consider installing HLS via ghcup or change "haskell.manageHLS" in your settings.` ;
@@ -178,13 +175,13 @@ function findServerExecutable(context: ExtensionContext, logger: Logger, folder?
178
175
179
176
/** Searches the PATH. Fails if nothing is found.
180
177
*/
181
- function findHLSinPATH ( context : ExtensionContext , logger : Logger , folder ?: WorkspaceFolder ) : string {
178
+ async function findHLSinPATH ( context : ExtensionContext , logger : Logger , folder ?: WorkspaceFolder ) : Promise < string > {
182
179
// try PATH
183
180
const exes : string [ ] = [ 'haskell-language-server-wrapper' , 'haskell-language-server' ] ;
184
181
logger . info ( `Searching for server executables ${ exes . join ( ',' ) } in $PATH` ) ;
185
182
logger . info ( `$PATH environment variable: ${ process . env . PATH } ` ) ;
186
183
for ( const exe of exes ) {
187
- if ( executableExists ( exe ) ) {
184
+ if ( await executableExists ( exe ) ) {
188
185
logger . info ( `Found server executable in $PATH: ${ exe } ` ) ;
189
186
return exe ;
190
187
}
@@ -248,7 +245,7 @@ export async function findHaskellLanguageServer(
248
245
return findHLSinPATH ( context , logger , folder ) ;
249
246
} else {
250
247
// we manage HLS, make sure ghcup is installed/available
251
- await getGHCup ( context , logger ) ;
248
+ await upgradeGHCup ( context , logger ) ;
252
249
253
250
// get a preliminary toolchain for finding the correct project GHC version (we need HLS and cabal/stack and ghc as fallback),
254
251
// later we may install a different toolchain that's more project-specific
@@ -319,8 +316,9 @@ async function callGHCup(
319
316
const metadataUrl = workspace . getConfiguration ( 'haskell' ) . metadataURL ;
320
317
321
318
if ( manageHLS === 'GHCup' ) {
319
+ const ghcup = await findGHCup ( context , logger ) ;
322
320
return await callAsync (
323
- ' ghcup' ,
321
+ ghcup ,
324
322
[ '--no-verbose' ] . concat ( metadataUrl ? [ '-s' , metadataUrl ] : [ ] ) . concat ( args ) ,
325
323
logger ,
326
324
undefined ,
@@ -382,7 +380,7 @@ export async function getProjectGHCVersion(toolchainBindir: string, workingDir:
382
380
383
381
const args = [ '--project-ghc-version' ] ;
384
382
385
- const newPath = addPathToProcessPath ( toolchainBindir ) ;
383
+ const newPath = await addPathToProcessPath ( toolchainBindir , logger ) ;
386
384
const environmentNew : IEnvVars = {
387
385
PATH : newPath ,
388
386
} ;
@@ -424,29 +422,28 @@ export async function getProjectGHCVersion(toolchainBindir: string, workingDir:
424
422
) ;
425
423
}
426
424
427
- /**
428
- * Downloads the latest ghcup binary.
429
- * Returns undefined if it can't find any for the given architecture/platform.
430
- */
431
- export async function getGHCup ( context : ExtensionContext , logger : Logger ) : Promise < string | undefined > {
432
- logger . info ( 'Checking for ghcup installation' ) ;
433
- const localGHCup = [ 'ghcup' ] . find ( executableExists ) ;
434
- if ( ! localGHCup ) {
435
- throw new MissingToolError ( 'ghcup' ) ;
436
- }
437
-
425
+ export async function upgradeGHCup ( context : ExtensionContext , logger : Logger ) : Promise < void > {
438
426
if ( manageHLS === 'GHCup' ) {
439
- logger . info ( `found ghcup at ${ localGHCup } ` ) ;
440
427
const upgrade = workspace . getConfiguration ( 'haskell' ) . get ( 'upgradeGHCup' ) as boolean ;
441
428
if ( upgrade ) {
442
429
await callGHCup ( context , logger , [ 'upgrade' ] , 'Upgrading ghcup' , true ) ;
443
430
}
444
- return localGHCup ;
445
431
} else {
446
432
throw new Error ( `Internal error: tried to call ghcup while haskell.manageHLS is set to ${ manageHLS } . Aborting!` ) ;
447
433
}
448
434
}
449
435
436
+ export async function findGHCup ( context : ExtensionContext , logger : Logger ) : Promise < string > {
437
+ logger . info ( 'Checking for ghcup installation' ) ;
438
+ const localGHCup = [ 'ghcup' ] . find ( executableExists ) ;
439
+ if ( ! localGHCup ) {
440
+ throw new MissingToolError ( 'ghcup' ) ;
441
+ } else {
442
+ logger . info ( `found ghcup at ${ localGHCup } ` ) ;
443
+ return localGHCup
444
+ }
445
+ }
446
+
450
447
/**
451
448
* Compare the PVP versions of two strings.
452
449
* Details: https://github.com/haskell/pvp/
@@ -496,13 +493,6 @@ export async function getStoragePath(context: ExtensionContext): Promise<string>
496
493
return storagePath ;
497
494
}
498
495
499
- export function addPathToProcessPath ( extraPath : string ) : string {
500
- const pathSep = process . platform === 'win32' ? ';' : ':' ;
501
- const PATH = process . env . PATH ! . split ( pathSep ) ;
502
- PATH . unshift ( extraPath ) ;
503
- return PATH . join ( pathSep ) ;
504
- }
505
-
506
496
// the tool might be installed or not
507
497
async function getLatestToolFromGHCup ( context : ExtensionContext , logger : Logger , tool : string ) : Promise < string > {
508
498
// these might be custom/stray/compiled, so we try first
0 commit comments