@@ -6,7 +6,7 @@ import * as path from 'path';
6
6
import { match } from 'ts-pattern' ;
7
7
import * as url from 'url' ;
8
8
import { promisify } from 'util' ;
9
- import { ExtensionContext , ProgressLocation , Uri , window , workspace , WorkspaceFolder } from 'vscode' ;
9
+ import { ExtensionContext , ProgressLocation , Uri , window , workspace , WorkspaceFolder , ConfigurationTarget } from 'vscode' ;
10
10
import { Logger } from 'vscode-languageclient' ;
11
11
import { downloadFile , executableExists , httpsGetSilently , resolvePathPlaceHolders } from './utils' ;
12
12
@@ -17,6 +17,8 @@ export interface IEnvVars {
17
17
[ key : string ] : string ;
18
18
}
19
19
20
+ let systemGHCup = workspace . getConfiguration ( 'haskell' ) . get ( 'useSystemGHCup' ) as boolean | null ;
21
+
20
22
// On Windows the executable needs to be stored somewhere with an .exe extension
21
23
const exeExt = process . platform === 'win32' ? '.exe' : '' ;
22
24
@@ -189,7 +191,10 @@ export async function findHaskellLanguageServer(
189
191
logger : Logger ,
190
192
workingDir : string ,
191
193
folder ?: WorkspaceFolder
192
- ) : Promise < string > {
194
+ ) : Promise < [ string , string ] > {
195
+ // we manage HLS, make sure ghcup is installed/available
196
+ await getGHCup ( context , logger ) ;
197
+
193
198
logger . info ( 'Finding haskell-language-server' ) ;
194
199
195
200
const storagePath : string = await getStoragePath ( context ) ;
@@ -201,25 +206,23 @@ export async function findHaskellLanguageServer(
201
206
202
207
const manageHLS = workspace . getConfiguration ( 'haskell' ) . get ( 'manageHLS' ) as boolean ;
203
208
const wrapper = findHLSinPATH ( context , logger , folder ) ;
209
+ const [ installableHls , projectGhc ] = await getLatestSuitableHLS (
210
+ context ,
211
+ logger ,
212
+ workingDir ,
213
+ ( wrapper === null ) ? undefined : wrapper
214
+ ) ;
204
215
205
216
if ( ! manageHLS ) {
206
217
if ( ! wrapper ) {
207
218
const msg = 'Could not find a HLS binary! Consider installing HLS via ghcup or set "haskell.manageHLS" to true' ;
208
219
window . showErrorMessage ( msg ) ;
209
220
throw new Error ( msg ) ;
210
221
} else {
211
- return wrapper ;
222
+ return [ wrapper , projectGhc ] ;
212
223
}
213
224
}
214
- // we manage HLS, make sure ghcup is installed/available
215
- await getGHCup ( context , logger ) ;
216
225
217
- const installableHls = await getLatestSuitableHLS (
218
- context ,
219
- logger ,
220
- workingDir ,
221
- ( wrapper === null ) ? undefined : wrapper
222
- ) ;
223
226
const symHLSPath = path . join ( storagePath , 'hls' , installableHls ) ;
224
227
225
228
// check if the found existing wrapper is suitable
@@ -229,7 +232,7 @@ export async function findHaskellLanguageServer(
229
232
230
233
// is the currently set hls wrapper matching the required version?
231
234
if ( comparePVP ( setVersion , installableHls ) !== 0 ) {
232
- return wrapper ;
235
+ return [ wrapper , projectGhc ] ;
233
236
}
234
237
}
235
238
@@ -238,7 +241,7 @@ export async function findHaskellLanguageServer(
238
241
`Installing HLS ${ installableHls } ` ,
239
242
true
240
243
) ;
241
- return path . join ( symHLSPath , `haskell-language-server-wrapper${ exeExt } ` ) ;
244
+ return [ path . join ( symHLSPath , `haskell-language-server-wrapper${ exeExt } ` ) , projectGhc ] ;
242
245
}
243
246
244
247
async function callGHCup (
@@ -250,8 +253,7 @@ async function callGHCup(
250
253
) : Promise < string > {
251
254
252
255
const storagePath : string = await getStoragePath ( context ) ;
253
- const systemGHCup = workspace . getConfiguration ( 'haskell' ) . get ( 'useSystemGHCup' ) as boolean ;
254
- const ghcup = path . join ( storagePath , `ghcup${ exeExt } ` ) ;
256
+ const ghcup = ( systemGHCup === true ) ? `ghcup${ exeExt } ` : path . join ( storagePath , `ghcup${ exeExt } ` ) ;
255
257
if ( systemGHCup ) {
256
258
return await callAsync ( 'ghcup' , [ '--no-verbose' ] . concat ( args ) , storagePath , logger , title , cancellable ) ;
257
259
} else {
@@ -266,7 +268,7 @@ async function getLatestSuitableHLS(
266
268
logger : Logger ,
267
269
workingDir : string ,
268
270
wrapper ?: string
269
- ) : Promise < string > {
271
+ ) : Promise < [ string , string ] > {
270
272
const storagePath : string = await getStoragePath ( context ) ;
271
273
272
274
// get latest hls version
@@ -291,16 +293,16 @@ async function getLatestSuitableHLS(
291
293
projectGhc !== null ? await getLatestHLSforGHC ( context , storagePath , projectGhc , logger ) : null ;
292
294
const installableHls = latestMetadataHls !== null ? latestMetadataHls : latestHlsVersion ;
293
295
294
- return installableHls ;
296
+ return [ installableHls , projectGhc ] ;
295
297
}
296
298
297
299
// also serves as sanity check
298
300
export async function validateHLSToolchain (
299
301
wrapper : string ,
302
+ ghc : string ,
300
303
workingDir : string ,
301
304
logger : Logger
302
305
) : Promise < void > {
303
- const ghc = await getProjectGHCVersion ( wrapper , workingDir , logger ) ;
304
306
const wrapperDir = path . dirname ( wrapper ) ;
305
307
const hlsExe = path . join ( wrapperDir , `haskell-language-server-${ ghc } ${ exeExt } ` ) ;
306
308
const hlsVer = await callAsync ( wrapper , [ '--numeric-version' ] , workingDir , logger ) ;
@@ -358,13 +360,34 @@ export async function getProjectGHCVersion(
358
360
* Returns undefined if it can't find any for the given architecture/platform.
359
361
*/
360
362
export async function getGHCup ( context : ExtensionContext , logger : Logger ) : Promise < string | undefined > {
361
- const systemGHCup = workspace . getConfiguration ( 'haskell' ) . get ( 'useSystemGHCup' ) as boolean ;
362
- if ( systemGHCup ) {
363
- const localGHCup = [ 'ghcup' ] . find ( executableExists ) ;
363
+ logger . info ( 'Checking for ghcup installation' ) ;
364
+ const localGHCup = [ 'ghcup' ] . find ( executableExists ) ;
365
+
366
+ if ( systemGHCup === null ) {
367
+ if ( localGHCup !== undefined ) {
368
+ const promptMessage =
369
+ 'Detected system ghcup. Do you want VSCode to use it instead of an internal ghcup?' ;
370
+
371
+ systemGHCup = await window . showInformationMessage ( promptMessage , 'Yes' , 'No' ) . then ( b => b === 'Yes' ) ;
372
+ logger . info ( `set useSystemGHCup to ${ systemGHCup } ` ) ;
373
+
374
+ } else { // no local ghcup, disable
375
+ systemGHCup = false ;
376
+ }
377
+ }
378
+ // set config globally
379
+ workspace . getConfiguration ( 'haskell' ) . update ( 'useSystemGHCup' , systemGHCup , ConfigurationTarget . Global ) ;
380
+
381
+ if ( systemGHCup === true ) {
382
+ if ( localGHCup === undefined ) {
383
+ const msg = 'Could not find a system ghcup installation, please follow instructions at https://www.haskell.org/ghcup/' ;
384
+ window . showErrorMessage ( msg ) ;
385
+ throw new Error ( msg ) ;
386
+ }
387
+ logger . info ( `found system ghcup at ${ localGHCup } ` ) ;
364
388
return localGHCup ;
365
389
}
366
390
367
- logger . info ( 'Checking for ghcup installation' ) ;
368
391
369
392
const storagePath : string = await getStoragePath ( context ) ;
370
393
logger . info ( `Using ${ storagePath } to store downloaded binaries` ) ;
0 commit comments