@@ -168,19 +168,26 @@ export async function downloadHaskellLanguageServer(
168
168
const storagePath : string = await getStoragePath ( context ) ;
169
169
logger . info ( `Using ${ storagePath } to store downloaded binaries` ) ;
170
170
171
+ const ghcupBinDir = await callGHCup ( context , logger , [ 'whereis' , 'bindir' ] , undefined , false ) ;
172
+
171
173
if ( ! fs . existsSync ( storagePath ) ) {
172
174
fs . mkdirSync ( storagePath ) ;
173
175
}
174
176
175
177
const localWrapper = [ 'haskell-language-server-wrapper' ] . find ( executableExists ) ;
176
- const downloadedWrapper = path . join ( storagePath , process . platform === 'win32' ? 'ghcup' : '.ghcup' , 'bin' , `haskell-language-server-wrapper${ exeExt } ` ) ;
177
178
let wrapper : string | undefined ;
178
179
if ( localWrapper ) {
179
180
// first try PATH
180
181
wrapper = localWrapper ;
181
- } else if ( executableExists ( downloadedWrapper ) ) {
182
- // then try internal ghcup
183
- wrapper = downloadedWrapper ;
182
+ } else {
183
+ // then try ghcup
184
+ const ghcupHlsWrapper = path . join (
185
+ ghcupBinDir ,
186
+ `haskell-language-server-wrapper${ exeExt } `
187
+ ) ;
188
+ if ( executableExists ( ghcupHlsWrapper ) ) {
189
+ wrapper = ghcupHlsWrapper ;
190
+ }
184
191
}
185
192
186
193
const updateBehaviour = workspace . getConfiguration ( 'haskell' ) . get ( 'updateBehavior' ) as UpdateBehaviour ;
@@ -207,30 +214,36 @@ export async function downloadHaskellLanguageServer(
207
214
throw new Error ( 'No version of HLS installed or found and installation was denied, giving up...' ) ;
208
215
}
209
216
}
210
- await callGHCup (
211
- context ,
212
- logger ,
213
- [ 'install' , 'hls' , installableHls ] ,
217
+ // we use this command to both install a HLS, but also create a nice
218
+ // isolated symlinked dir with only the given HLS in place, so
219
+ // this works for installing and setting
220
+ const symHLSPath = path . join ( storagePath , 'hls' , installableHls ) ;
221
+ await callGHCup ( context , logger ,
222
+ [ 'run' , '--hls' , installableHls , '-b' , symHLSPath , '-i' ] ,
214
223
`Installing HLS ${ installableHls } ` ,
215
- true ,
224
+ true
216
225
) ;
217
- await callGHCup ( context , logger , [ 'set' , 'hls' , installableHls ] , undefined , false ) ;
218
- return downloadedWrapper ;
226
+ return path . join ( symHLSPath , `haskell-language-server-wrapper${ exeExt } ` ) ;
219
227
} else {
220
228
// version of active hls wrapper
221
229
const setVersion = await callAsync ( wrapper , [ '--numeric-version' ] , storagePath , logger ) ;
222
230
231
+ // is the currently set hls wrapper matching the required version?
232
+ const activeOk = comparePVP ( setVersion , installableHls ) ;
233
+
234
+ // do we need a downgrade?
223
235
const downgrade : boolean = comparePVP ( latestHlsVersion , installableHls ) > 0 ;
224
236
225
- const projectHlsWrapper = path . join (
226
- storagePath ,
227
- process . platform === 'win32' ? 'ghcup' : '.ghcup' ,
228
- 'bin' ,
229
- `haskell-language-server-wrapper-${ installableHls } ${ exeExt } `
230
- ) ;
231
- const needInstall = ! executableExists ( projectHlsWrapper ) ;
237
+ if ( activeOk !== 0 ) {
238
+ // Maybe there is a versioned wrapper from ghcup,
239
+ // indicating we don't need to install, just set this one as active.
240
+ // We check for this so that we can potentially have less annoying popups.
241
+ const projectHlsWrapper = path . join (
242
+ path . dirname ( wrapper ) ,
243
+ `haskell-language-server-wrapper-${ installableHls } ${ exeExt } `
244
+ ) ;
245
+ const needInstall = ! executableExists ( projectHlsWrapper ) ;
232
246
233
- if ( comparePVP ( setVersion , installableHls ) !== 0 ) {
234
247
// only update if the user wants to
235
248
if ( updateBehaviour === 'never-check' ) {
236
249
logger . warn (
@@ -287,10 +300,15 @@ async function callGHCup(
287
300
cancellable ?: boolean
288
301
) : Promise < string > {
289
302
const storagePath : string = await getStoragePath ( context ) ;
290
- const ghcup = path . join ( storagePath , `ghcup${ exeExt } ` ) ;
291
- return await callAsync ( ghcup , [ '--no-verbose' ] . concat ( args ) , storagePath , logger , title , cancellable , {
292
- GHCUP_INSTALL_BASE_PREFIX : storagePath ,
293
- } ) ;
303
+ const systemGHCup = workspace . getConfiguration ( 'haskell' ) . get ( 'useSystemGHCup' ) as boolean ;
304
+ if ( systemGHCup ) {
305
+ return await callAsync ( 'ghcup' , [ '--no-verbose' ] . concat ( args ) , storagePath , logger , title , cancellable ) ;
306
+ } else {
307
+ const ghcup = path . join ( storagePath , `ghcup${ exeExt } ` ) ;
308
+ return await callAsync ( ghcup , [ '--no-verbose' ] . concat ( args ) , storagePath , logger , title , cancellable , {
309
+ GHCUP_INSTALL_BASE_PREFIX : storagePath ,
310
+ } ) ;
311
+ }
294
312
}
295
313
296
314
async function getLatestSuitableHLS (
@@ -304,7 +322,7 @@ async function getLatestSuitableHLS(
304
322
// get latest hls version
305
323
const hlsVersions = await callGHCup (
306
324
context ,
307
- logger ,
325
+ logger ,
308
326
[ 'list' , '-t' , 'hls' , '-c' , 'available' , '-r' ] ,
309
327
undefined ,
310
328
false ,
@@ -387,9 +405,15 @@ export async function getProjectGHCVersion(
387
405
388
406
/**
389
407
* Downloads the latest ghcup binary.
390
- * Returns null if it can't find any for the given architecture/platform.
408
+ * Returns undefined if it can't find any for the given architecture/platform.
391
409
*/
392
- export async function downloadGHCup ( context : ExtensionContext , logger : Logger ) : Promise < string | null > {
410
+ export async function downloadGHCup ( context : ExtensionContext , logger : Logger ) : Promise < string | undefined > {
411
+ const systemGHCup = workspace . getConfiguration ( 'haskell' ) . get ( 'useSystemGHCup' ) as boolean ;
412
+ if ( systemGHCup ) {
413
+ const localGHCup = [ 'ghcup' ] . find ( executableExists ) ;
414
+ return localGHCup
415
+ }
416
+
393
417
logger . info ( 'Checking for ghcup installation' ) ;
394
418
395
419
const storagePath : string = await getStoragePath ( context ) ;
@@ -415,7 +439,7 @@ export async function downloadGHCup(context: ExtensionContext, logger: Logger):
415
439
. otherwise ( ( _ ) => null ) ;
416
440
if ( plat === null ) {
417
441
window . showErrorMessage ( `Couldn't find any pre-built ghcup binary for ${ process . platform } ` ) ;
418
- return null ;
442
+ return undefined ;
419
443
}
420
444
const arch = match ( process . arch )
421
445
. with ( 'arm' , ( _ ) => 'armv7' )
@@ -425,7 +449,7 @@ export async function downloadGHCup(context: ExtensionContext, logger: Logger):
425
449
. otherwise ( ( _ ) => null ) ;
426
450
if ( arch === null ) {
427
451
window . showErrorMessage ( `Couldn't find any pre-built ghcup binary for ${ process . arch } ` ) ;
428
- return null ;
452
+ return undefined ;
429
453
}
430
454
const dlUri = `https://downloads.haskell.org/~ghcup/${ arch } -${ plat } -ghcup${ exeExt } ` ;
431
455
const title = `Downloading ${ dlUri } ` ;
0 commit comments