@@ -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 ,
@@ -124,6 +130,7 @@ export async function findHaskellLanguageServer(
124
130
// first extension initialization
125
131
manageHLS = await promptUserForManagingHls ( context , manageHLS ) ;
126
132
133
+ // based on the user-decision
127
134
if ( manageHLS === 'PATH' ) {
128
135
const exe = findHlsInPath ( logger ) ;
129
136
return {
@@ -176,21 +183,21 @@ export async function findHaskellLanguageServer(
176
183
// download popups
177
184
const promptBeforeDownloads = workspace . getConfiguration ( 'haskell' ) . get ( 'promptBeforeDownloads' ) as boolean ;
178
185
if ( promptBeforeDownloads ) {
179
- const hlsInstalled = latestHLS ? await toolInstalled ( ghcup , 'hls' , latestHLS ) : undefined ;
180
- const cabalInstalled = latestCabal ? await toolInstalled ( ghcup , 'cabal' , latestCabal ) : undefined ;
181
- const stackInstalled = latestStack ? await toolInstalled ( ghcup , 'stack' , latestStack ) : undefined ;
186
+ const hlsInstalled = latestHLS ? await installationStatusOfGhcupTool ( ghcup , 'hls' , latestHLS ) : undefined ;
187
+ const cabalInstalled = latestCabal ? await installationStatusOfGhcupTool ( ghcup , 'cabal' , latestCabal ) : undefined ;
188
+ const stackInstalled = latestStack ? await installationStatusOfGhcupTool ( ghcup , 'stack' , latestStack ) : undefined ;
182
189
const ghcInstalled = executableExists ( 'ghc' )
183
- ? new InstalledTool (
190
+ ? new ToolStatus (
184
191
'ghc' ,
185
192
await callAsync ( `ghc${ exeExt } ` , [ '--numeric-version' ] , logger , undefined , undefined , false ) ,
186
193
)
187
194
: // if recGHC is null, that means user disabled automatic handling,
188
195
recGHC !== null
189
- ? await toolInstalled ( ghcup , 'ghc' , recGHC )
196
+ ? await installationStatusOfGhcupTool ( ghcup , 'ghc' , recGHC )
190
197
: undefined ;
191
- const toInstall : InstalledTool [ ] = [ hlsInstalled , cabalInstalled , stackInstalled , ghcInstalled ] . filter (
198
+ const toInstall : ToolStatus [ ] = [ hlsInstalled , cabalInstalled , stackInstalled , ghcInstalled ] . filter (
192
199
( tool ) => tool && ! tool . installed ,
193
- ) as InstalledTool [ ] ;
200
+ ) as ToolStatus [ ] ;
194
201
if ( toInstall . length > 0 ) {
195
202
const decision = await window . showInformationMessage (
196
203
`Need to download ${ toInstall . map ( ( t ) => t . nameWithVersion ) . join ( ', ' ) } , continue?` ,
@@ -258,11 +265,11 @@ export async function findHaskellLanguageServer(
258
265
259
266
// more download popups
260
267
if ( promptBeforeDownloads ) {
261
- const hlsInstalled = projectHls ? await toolInstalled ( ghcup , 'hls' , projectHls ) : undefined ;
262
- const ghcInstalled = projectGhc ? await toolInstalled ( ghcup , 'ghc' , projectGhc ) : undefined ;
263
- const toInstall : InstalledTool [ ] = [ hlsInstalled , ghcInstalled ] . filter (
268
+ const hlsInstalled = projectHls ? await installationStatusOfGhcupTool ( ghcup , 'hls' , projectHls ) : undefined ;
269
+ const ghcInstalled = projectGhc ? await installationStatusOfGhcupTool ( ghcup , 'ghc' , projectGhc ) : undefined ;
270
+ const toInstall : ToolStatus [ ] = [ hlsInstalled , ghcInstalled ] . filter (
264
271
( tool ) => tool && ! tool . installed ,
265
- ) as InstalledTool [ ] ;
272
+ ) as ToolStatus [ ] ;
266
273
if ( toInstall . length > 0 ) {
267
274
const decision = await window . showInformationMessage (
268
275
`Need to download ${ toInstall . map ( ( t ) => t . nameWithVersion ) . join ( ', ' ) } , continue?` ,
@@ -511,18 +518,18 @@ async function findAvailableHlsBinariesFromGHCup(ghcup: GHCup): Promise<Map<stri
511
518
}
512
519
}
513
520
514
- async function toolInstalled ( ghcup : GHCup , tool : Tool , version : string ) : Promise < InstalledTool > {
521
+ async function installationStatusOfGhcupTool ( ghcup : GHCup , tool : Tool , version : string ) : Promise < ToolStatus > {
515
522
const b = await ghcup
516
523
. call ( [ 'whereis' , tool , version ] , undefined , false )
517
524
. then ( ( ) => true )
518
525
. catch ( ( ) => false ) ;
519
- return new InstalledTool ( tool , version , b ) ;
526
+ return new ToolStatus ( tool , version , b ) ;
520
527
}
521
528
522
529
/**
523
530
* Tracks the name, version and installation state of tools we need.
524
531
*/
525
- class InstalledTool {
532
+ class ToolStatus {
526
533
/**
527
534
* "\<name\>-\<version\>" of the installed Tool.
528
535
*/
0 commit comments