Skip to content

Commit 3397b1a

Browse files
hasufellfendor
authored andcommitted
Prompt user if they want to use system ghcup or not
1 parent 10bf377 commit 3397b1a

File tree

3 files changed

+49
-25
lines changed

3 files changed

+49
-25
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
"haskell.useSystemGHCup": {
161161
"scope": "resource",
162162
"type": "boolean",
163-
"default": false,
163+
"default": null,
164164
"description": "Whether to use the system ghcup or an internal one for installing HLS."
165165
},
166166
"haskell.checkProject": {

src/extension.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,9 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
147147
let serverExecutable;
148148
let addInternalServerPath: string | undefined; // if we download HLS, add that bin dir to PATH
149149
try {
150-
serverExecutable = await findHaskellLanguageServer(context, logger, currentWorkingDir, folder);
151-
await validateHLSToolchain(serverExecutable, currentWorkingDir, logger);
150+
const [serverExecutable_, projectGhc] = await findHaskellLanguageServer(context, logger, currentWorkingDir, folder);
151+
serverExecutable = serverExecutable_;
152+
await validateHLSToolchain(serverExecutable, projectGhc, currentWorkingDir, logger);
152153
addInternalServerPath = path.dirname(serverExecutable);
153154
if (!serverExecutable) {
154155
return;

src/hlsBinaries.ts

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as path from 'path';
66
import { match } from 'ts-pattern';
77
import * as url from 'url';
88
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';
1010
import { Logger } from 'vscode-languageclient';
1111
import { downloadFile, executableExists, httpsGetSilently, resolvePathPlaceHolders } from './utils';
1212

@@ -17,6 +17,8 @@ export interface IEnvVars {
1717
[key: string]: string;
1818
}
1919

20+
let systemGHCup = workspace.getConfiguration('haskell').get('useSystemGHCup') as boolean | null;
21+
2022
// On Windows the executable needs to be stored somewhere with an .exe extension
2123
const exeExt = process.platform === 'win32' ? '.exe' : '';
2224

@@ -189,7 +191,10 @@ export async function findHaskellLanguageServer(
189191
logger: Logger,
190192
workingDir: string,
191193
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+
193198
logger.info('Finding haskell-language-server');
194199

195200
const storagePath: string = await getStoragePath(context);
@@ -201,25 +206,23 @@ export async function findHaskellLanguageServer(
201206

202207
const manageHLS = workspace.getConfiguration('haskell').get('manageHLS') as boolean;
203208
const wrapper = findHLSinPATH(context, logger, folder);
209+
const [installableHls, projectGhc] = await getLatestSuitableHLS(
210+
context,
211+
logger,
212+
workingDir,
213+
(wrapper === null) ? undefined : wrapper
214+
);
204215

205216
if (!manageHLS) {
206217
if (!wrapper) {
207218
const msg = 'Could not find a HLS binary! Consider installing HLS via ghcup or set "haskell.manageHLS" to true';
208219
window.showErrorMessage(msg);
209220
throw new Error(msg);
210221
} else {
211-
return wrapper;
222+
return [wrapper, projectGhc];
212223
}
213224
}
214-
// we manage HLS, make sure ghcup is installed/available
215-
await getGHCup(context, logger);
216225

217-
const installableHls = await getLatestSuitableHLS(
218-
context,
219-
logger,
220-
workingDir,
221-
(wrapper === null) ? undefined : wrapper
222-
);
223226
const symHLSPath = path.join(storagePath, 'hls', installableHls);
224227

225228
// check if the found existing wrapper is suitable
@@ -229,7 +232,7 @@ export async function findHaskellLanguageServer(
229232

230233
// is the currently set hls wrapper matching the required version?
231234
if (comparePVP(setVersion, installableHls) !== 0) {
232-
return wrapper;
235+
return [wrapper, projectGhc];
233236
}
234237
}
235238

@@ -238,7 +241,7 @@ export async function findHaskellLanguageServer(
238241
`Installing HLS ${installableHls}`,
239242
true
240243
);
241-
return path.join(symHLSPath, `haskell-language-server-wrapper${exeExt}`);
244+
return [path.join(symHLSPath, `haskell-language-server-wrapper${exeExt}`), projectGhc];
242245
}
243246

244247
async function callGHCup(
@@ -250,8 +253,7 @@ async function callGHCup(
250253
): Promise<string> {
251254

252255
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}`);
255257
if (systemGHCup) {
256258
return await callAsync('ghcup', ['--no-verbose'].concat(args), storagePath, logger, title, cancellable);
257259
} else {
@@ -266,7 +268,7 @@ async function getLatestSuitableHLS(
266268
logger: Logger,
267269
workingDir: string,
268270
wrapper?: string
269-
): Promise<string> {
271+
): Promise<[string, string]> {
270272
const storagePath: string = await getStoragePath(context);
271273

272274
// get latest hls version
@@ -291,16 +293,16 @@ async function getLatestSuitableHLS(
291293
projectGhc !== null ? await getLatestHLSforGHC(context, storagePath, projectGhc, logger) : null;
292294
const installableHls = latestMetadataHls !== null ? latestMetadataHls : latestHlsVersion;
293295

294-
return installableHls;
296+
return [installableHls, projectGhc];
295297
}
296298

297299
// also serves as sanity check
298300
export async function validateHLSToolchain(
299301
wrapper: string,
302+
ghc: string,
300303
workingDir: string,
301304
logger: Logger
302305
): Promise<void> {
303-
const ghc = await getProjectGHCVersion(wrapper, workingDir, logger);
304306
const wrapperDir = path.dirname(wrapper);
305307
const hlsExe = path.join(wrapperDir, `haskell-language-server-${ghc}${exeExt}`);
306308
const hlsVer = await callAsync(wrapper, ['--numeric-version'], workingDir, logger);
@@ -358,13 +360,34 @@ export async function getProjectGHCVersion(
358360
* Returns undefined if it can't find any for the given architecture/platform.
359361
*/
360362
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}`);
364388
return localGHCup;
365389
}
366390

367-
logger.info('Checking for ghcup installation');
368391

369392
const storagePath: string = await getStoragePath(context);
370393
logger.info(`Using ${storagePath} to store downloaded binaries`);

0 commit comments

Comments
 (0)