@@ -49,7 +49,11 @@ function findServerExecutable(logger: Logger, folder?: WorkspaceFolder): string
49
49
}
50
50
}
51
51
52
- /** Searches the PATH. Fails if nothing is found.
52
+ /**
53
+ * Searches the `PATH` for `haskell-language-server` or `haskell-language-server-wrapper` binary.
54
+ * Fails if nothing is found.
55
+ * @param logger Log all the stuff!
56
+ * @returns Location of the `haskell-language-server` or `haskell-language-server-wrapper` binary if found.
53
57
*/
54
58
function findHlsInPath ( logger : Logger ) : string {
55
59
// try PATH
@@ -87,17 +91,19 @@ export type HlsViaGhcup = {
87
91
} ;
88
92
89
93
/**
90
- * Downloads the latest haskell-language-server binaries via GHCup.
91
- * Makes sure that either `ghcup` is available locally, otherwise installs
92
- * it into an isolated location.
93
- * If we figure out the correct GHC version, but it isn't compatible with
94
- * the latest HLS executables, we download the latest compatible HLS binaries
95
- * as a fallback.
94
+ * Find and setup the Haskell Language Server.
95
+ *
96
+ * We support three ways of finding the HLS binary:
97
+ *
98
+ * 1. Let the user provide a location via `haskell.serverExecutablePath` option.
99
+ * 2. Find a `haskell-language-server` binary on the `$PATH` if the user wants to do that.
100
+ * 3. Use GHCup to install and locate HLS and other required tools, such as cabal, stack and ghc.
96
101
*
97
102
* @param context Context of the extension, required for metadata.
98
103
* @param logger Logger for progress updates.
99
104
* @param workingDir Working directory in VSCode.
100
- * @returns Path to haskell-language-server-wrapper
105
+ * @param folder Optional workspace folder. If given, will be preferred over {@link workingDir} for finding configuration entries.
106
+ * @returns Path to haskell-language-server, paired with additional data required for setting up.
101
107
*/
102
108
export async function findHaskellLanguageServer (
103
109
context : ExtensionContext ,
@@ -125,6 +131,7 @@ export async function findHaskellLanguageServer(
125
131
// first extension initialization
126
132
manageHLS = await promptUserForManagingHls ( context , manageHLS ) ;
127
133
134
+ // based on the user-decision
128
135
if ( manageHLS === 'PATH' ) {
129
136
const exe = findHlsInPath ( logger ) ;
130
137
return {
@@ -177,21 +184,21 @@ export async function findHaskellLanguageServer(
177
184
// download popups
178
185
const promptBeforeDownloads = workspace . getConfiguration ( 'haskell' ) . get ( 'promptBeforeDownloads' ) as boolean ;
179
186
if ( promptBeforeDownloads ) {
180
- const hlsInstalled = latestHLS ? await toolInstalled ( ghcup , 'hls' , latestHLS ) : undefined ;
181
- const cabalInstalled = latestCabal ? await toolInstalled ( ghcup , 'cabal' , latestCabal ) : undefined ;
182
- const stackInstalled = latestStack ? await toolInstalled ( ghcup , 'stack' , latestStack ) : undefined ;
187
+ const hlsInstalled = latestHLS ? await installationStatusOfGhcupTool ( ghcup , 'hls' , latestHLS ) : undefined ;
188
+ const cabalInstalled = latestCabal ? await installationStatusOfGhcupTool ( ghcup , 'cabal' , latestCabal ) : undefined ;
189
+ const stackInstalled = latestStack ? await installationStatusOfGhcupTool ( ghcup , 'stack' , latestStack ) : undefined ;
183
190
const ghcInstalled = executableExists ( 'ghc' )
184
- ? new InstalledTool (
191
+ ? new ToolStatus (
185
192
'ghc' ,
186
193
await callAsync ( `ghc${ exeExt } ` , [ '--numeric-version' ] , logger , undefined , undefined , false ) ,
187
194
)
188
195
: // if recGHC is null, that means user disabled automatic handling,
189
196
recGHC !== null
190
- ? await toolInstalled ( ghcup , 'ghc' , recGHC )
197
+ ? await installationStatusOfGhcupTool ( ghcup , 'ghc' , recGHC )
191
198
: undefined ;
192
- const toInstall : InstalledTool [ ] = [ hlsInstalled , cabalInstalled , stackInstalled , ghcInstalled ] . filter (
199
+ const toInstall : ToolStatus [ ] = [ hlsInstalled , cabalInstalled , stackInstalled , ghcInstalled ] . filter (
193
200
( tool ) => tool && ! tool . installed ,
194
- ) as InstalledTool [ ] ;
201
+ ) as ToolStatus [ ] ;
195
202
if ( toInstall . length > 0 ) {
196
203
const decision = await window . showInformationMessage (
197
204
`Need to download ${ toInstall . map ( ( t ) => t . nameWithVersion ) . join ( ', ' ) } , continue?` ,
@@ -259,11 +266,11 @@ export async function findHaskellLanguageServer(
259
266
260
267
// more download popups
261
268
if ( promptBeforeDownloads ) {
262
- const hlsInstalled = projectHls ? await toolInstalled ( ghcup , 'hls' , projectHls ) : undefined ;
263
- const ghcInstalled = projectGhc ? await toolInstalled ( ghcup , 'ghc' , projectGhc ) : undefined ;
264
- const toInstall : InstalledTool [ ] = [ hlsInstalled , ghcInstalled ] . filter (
269
+ const hlsInstalled = projectHls ? await installationStatusOfGhcupTool ( ghcup , 'hls' , projectHls ) : undefined ;
270
+ const ghcInstalled = projectGhc ? await installationStatusOfGhcupTool ( ghcup , 'ghc' , projectGhc ) : undefined ;
271
+ const toInstall : ToolStatus [ ] = [ hlsInstalled , ghcInstalled ] . filter (
265
272
( tool ) => tool && ! tool . installed ,
266
- ) as InstalledTool [ ] ;
273
+ ) as ToolStatus [ ] ;
267
274
if ( toInstall . length > 0 ) {
268
275
const decision = await window . showInformationMessage (
269
276
`Need to download ${ toInstall . map ( ( t ) => t . nameWithVersion ) . join ( ', ' ) } , continue?` ,
@@ -512,18 +519,18 @@ async function findAvailableHlsBinariesFromGHCup(ghcup: GHCup): Promise<Map<stri
512
519
}
513
520
}
514
521
515
- async function toolInstalled ( ghcup : GHCup , tool : Tool , version : string ) : Promise < InstalledTool > {
522
+ async function installationStatusOfGhcupTool ( ghcup : GHCup , tool : Tool , version : string ) : Promise < ToolStatus > {
516
523
const b = await ghcup
517
524
. call ( [ 'whereis' , tool , version ] , undefined , false )
518
525
. then ( ( ) => true )
519
526
. catch ( ( ) => false ) ;
520
- return new InstalledTool ( tool , version , b ) ;
527
+ return new ToolStatus ( tool , version , b ) ;
521
528
}
522
529
523
530
/**
524
531
* Tracks the name, version and installation state of tools we need.
525
532
*/
526
- class InstalledTool {
533
+ class ToolStatus {
527
534
/**
528
535
* "\<name\>-\<version\>" of the installed Tool.
529
536
*/
0 commit comments