@@ -17,15 +17,11 @@ import {
1717 WorkspaceFolder ,
1818} from 'vscode' ;
1919import { Logger } from 'vscode-languageclient' ;
20- import { executableExists , httpsGetSilently , resolvePathPlaceHolders } from './utils' ;
20+ import { executableExists , httpsGetSilently , resolvePathPlaceHolders , IEnvVars , addPathToProcessPath , resolveServerEnvironmentPATH } from './utils' ;
21+ export { IEnvVars }
2122
2223export type ReleaseMetadata = Map < string , Map < string , Map < string , string [ ] > > > ;
2324
24- // Used for environment variables later on
25- export interface IEnvVars {
26- [ key : string ] : string ;
27- }
28-
2925type ManageHLS = 'GHCup' | 'PATH' ;
3026let manageHLS = workspace . getConfiguration ( 'haskell' ) . get ( 'manageHLS' ) as ManageHLS | null ;
3127
@@ -103,9 +99,10 @@ async function callAsync(
10399 reject : ( reason ?: any ) => void
104100 ) => void
105101) : 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 ( ':' ) } ` ) ;
109106 return window . withProgress (
110107 {
111108 location : ProgressLocation . Notification ,
@@ -162,12 +159,12 @@ async function callAsync(
162159
163160/** Gets serverExecutablePath and fails if it's not set.
164161 */
165- function findServerExecutable ( context : ExtensionContext , logger : Logger , folder ?: WorkspaceFolder ) : string {
162+ async function findServerExecutable ( context : ExtensionContext , logger : Logger , folder ?: WorkspaceFolder ) : Promise < string > {
166163 let exePath = workspace . getConfiguration ( 'haskell' ) . get ( 'serverExecutablePath' ) as string ;
167164 logger . info ( `Trying to find the server executable in: ${ exePath } ` ) ;
168165 exePath = resolvePathPlaceHolders ( exePath , folder ) ;
169166 logger . log ( `Location after path variables substitution: ${ exePath } ` ) ;
170- if ( executableExists ( exePath ) ) {
167+ if ( await executableExists ( exePath ) ) {
171168 return exePath ;
172169 } else {
173170 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?
178175
179176/** Searches the PATH. Fails if nothing is found.
180177 */
181- function findHLSinPATH ( context : ExtensionContext , logger : Logger , folder ?: WorkspaceFolder ) : string {
178+ async function findHLSinPATH ( context : ExtensionContext , logger : Logger , folder ?: WorkspaceFolder ) : Promise < string > {
182179 // try PATH
183180 const exes : string [ ] = [ 'haskell-language-server-wrapper' , 'haskell-language-server' ] ;
184181 logger . info ( `Searching for server executables ${ exes . join ( ',' ) } in $PATH` ) ;
185182 logger . info ( `$PATH environment variable: ${ process . env . PATH } ` ) ;
186183 for ( const exe of exes ) {
187- if ( executableExists ( exe ) ) {
184+ if ( await executableExists ( exe ) ) {
188185 logger . info ( `Found server executable in $PATH: ${ exe } ` ) ;
189186 return exe ;
190187 }
@@ -248,7 +245,7 @@ export async function findHaskellLanguageServer(
248245 return findHLSinPATH ( context , logger , folder ) ;
249246 } else {
250247 // we manage HLS, make sure ghcup is installed/available
251- await getGHCup ( context , logger ) ;
248+ await upgradeGHCup ( context , logger ) ;
252249
253250 // get a preliminary toolchain for finding the correct project GHC version (we need HLS and cabal/stack and ghc as fallback),
254251 // later we may install a different toolchain that's more project-specific
@@ -319,8 +316,9 @@ async function callGHCup(
319316 const metadataUrl = workspace . getConfiguration ( 'haskell' ) . metadataURL ;
320317
321318 if ( manageHLS === 'GHCup' ) {
319+ const ghcup = await findGHCup ( context , logger ) ;
322320 return await callAsync (
323- ' ghcup' ,
321+ ghcup ,
324322 [ '--no-verbose' ] . concat ( metadataUrl ? [ '-s' , metadataUrl ] : [ ] ) . concat ( args ) ,
325323 logger ,
326324 undefined ,
@@ -382,7 +380,7 @@ export async function getProjectGHCVersion(toolchainBindir: string, workingDir:
382380
383381 const args = [ '--project-ghc-version' ] ;
384382
385- const newPath = addPathToProcessPath ( toolchainBindir ) ;
383+ const newPath = await addPathToProcessPath ( toolchainBindir , logger ) ;
386384 const environmentNew : IEnvVars = {
387385 PATH : newPath ,
388386 } ;
@@ -424,29 +422,28 @@ export async function getProjectGHCVersion(toolchainBindir: string, workingDir:
424422 ) ;
425423}
426424
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 > {
438426 if ( manageHLS === 'GHCup' ) {
439- logger . info ( `found ghcup at ${ localGHCup } ` ) ;
440427 const upgrade = workspace . getConfiguration ( 'haskell' ) . get ( 'upgradeGHCup' ) as boolean ;
441428 if ( upgrade ) {
442429 await callGHCup ( context , logger , [ 'upgrade' ] , 'Upgrading ghcup' , true ) ;
443430 }
444- return localGHCup ;
445431 } else {
446432 throw new Error ( `Internal error: tried to call ghcup while haskell.manageHLS is set to ${ manageHLS } . Aborting!` ) ;
447433 }
448434}
449435
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+
450447/**
451448 * Compare the PVP versions of two strings.
452449 * Details: https://github.com/haskell/pvp/
@@ -496,13 +493,6 @@ export async function getStoragePath(context: ExtensionContext): Promise<string>
496493 return storagePath ;
497494}
498495
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-
506496// the tool might be installed or not
507497async function getLatestToolFromGHCup ( context : ExtensionContext , logger : Logger , tool : string ) : Promise < string > {
508498 // these might be custom/stray/compiled, so we try first
0 commit comments