Skip to content

Commit 950e02f

Browse files
authored
Merge pull request #543 from hasufell/issue-542
Consider user installed HLSes (e.g. via ghcup compile)
2 parents f410497 + 1a9b77a commit 950e02f

File tree

7 files changed

+510
-389
lines changed

7 files changed

+510
-389
lines changed

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,25 +71,26 @@ The environment _only will be visible for the lsp server_, not for other extensi
7171

7272
### Downloaded binaries
7373

74-
This extension will download `haskell-language-server` binaries via an (internal) ghcup to a specific location depending
75-
on your system, unless you set the config option `haskell.manageHLS` to `false` (the default is `true`).
74+
This extension will download `haskell-language-server` binaries either via an internal ghcup (it will download it automaticlaly)
75+
or via a system ghcup (which must be present), unless you set the config option `haskell.manageHLS` to `PATH` (the extension
76+
will ask you on first start).
7677

77-
It will download the newest version of haskell-language-server which has support for the required ghc.
78+
It will then download the newest version of haskell-language-server which has support for the required ghc.
7879
That means it could use an older version than the latest one, without the last features and bug fixes.
7980
For example, if a project needs ghc-8.10.4 the extension will download and use haskell-language-server-1.4.0, the lastest version which supported ghc-8.10.4. Even if the lastest global haskell language-server version is 1.5.1.
8081

81-
If you find yourself running out of disk space, you can try deleting old versions of language servers in this directory. The extension will redownload them, no strings attached.
82+
If you have disk space issues and use system ghcup, check `ghcup gc --help`.
83+
If you have disk space issues and use the internal ghcup, check the following directories, depending on your platform
84+
and possible delete them:
8285

8386
| Platform | Path |
8487
| -------- | ------------------------------------------------------------------------------- |
8588
| macOS | `~/Library/Application\ Support/Code/User/globalStorage/haskell.haskell/.ghcup` |
8689
| Windows | `%APPDATA%\Code\User\globalStorage\haskell.haskell\ghcup` |
8790
| Linux | `$HOME/.config/Code/User/globalStorage/haskell.haskell/.ghcup` |
8891

89-
If you want to manage HLS yourself, set `haskell.manageHLS` to `false` and make sure HLS is in your PATH
90-
or set `haskell.serverExecutablePath` to a valid executable.
91-
92-
You can also tell HLS to use your system provided ghcup by setting `haskell.useSystemGHCup` to `true` (default is `false`).
92+
If you want to manage HLS yourself, set `haskell.manageHLS` to `PATH` and make sure HLS is in your PATH
93+
or set `haskell.serverExecutablePath` (overrides all other settings) to a valid executable.
9394

9495
If you need to set mirrors for ghcup download info, check the settings `haskell.metadataURL` and `haskell.releasesURL`.
9596

package.json

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,19 @@
159159
},
160160
"haskell.manageHLS": {
161161
"scope": "resource",
162-
"type": "boolean",
163-
"default": true,
164-
"description": "Let this extension manage required HLS versions via ghcup."
162+
"type": "string",
163+
"default": null,
164+
"description": "How to manage/find HLS installations.",
165+
"enum": [
166+
"system-ghcup",
167+
"internal-ghcup",
168+
"PATH"
169+
],
170+
"enumDescriptions": [
171+
"Will use a user-wide installation of ghcup (usually in '~/.ghcup') to manage HLS automatically",
172+
"Will use an internal installation of ghcup to manage HLS automatically, to avoid interfering with system ghcup",
173+
"Discovers HLS executables in system PATH"
174+
]
165175
},
166176
"haskell.useSystemGHCup": {
167177
"scope": "resource",

src/commands/importIdentifier.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,27 @@ import { CommandNames } from './constants';
99
const askHoogle = async (variable: string): Promise<any> => {
1010
return await request({
1111
url: `https://hoogle.haskell.org/?hoogle=${variable}&scope=set%3Astackage&mode=json`,
12-
json: true
12+
json: true,
1313
}).promise();
1414
};
1515

16-
const withCache = <T, U>(theCache: LRU.Cache<T, U>, f: (a: T) => U) => (a: T) => {
17-
const maybeB = theCache.get(a);
18-
if (maybeB) {
19-
return maybeB;
20-
} else {
21-
const b = f(a);
22-
theCache.set(a, b);
23-
return b;
24-
}
25-
};
16+
const withCache =
17+
<T, U>(theCache: LRU.Cache<T, U>, f: (a: T) => U) =>
18+
(a: T) => {
19+
const maybeB = theCache.get(a);
20+
if (maybeB) {
21+
return maybeB;
22+
} else {
23+
const b = f(a);
24+
theCache.set(a, b);
25+
return b;
26+
}
27+
};
2628

2729
const cache: LRU.Cache<string, Promise<any>> = LRU({
2830
// 1 MB
2931
max: 1000 * 1000,
30-
length: (r: any) => JSON.stringify(r).length
32+
length: (r: any) => JSON.stringify(r).length,
3133
});
3234

3335
const askHoogleCached = withCache(cache, askHoogle);
@@ -42,14 +44,14 @@ const doImport = async (arg: { mod: string; package: string }): Promise<void> =>
4244
const edit = new vscode.WorkspaceEdit();
4345

4446
const lines = document.getText().split('\n');
45-
const moduleLine = lines.findIndex(line => {
47+
const moduleLine = lines.findIndex((line) => {
4648
const lineTrimmed = line.trim();
4749
return lineTrimmed === 'where' || lineTrimmed.endsWith(' where') || lineTrimmed.endsWith(')where');
4850
});
49-
const revInputLine = lines.reverse().findIndex(l => l.startsWith('import'));
51+
const revInputLine = lines.reverse().findIndex((l) => l.startsWith('import'));
5052
const nextInputLine = revInputLine !== -1 ? lines.length - 1 - revInputLine : moduleLine === -1 ? 0 : moduleLine + 1;
5153

52-
if (!lines.some(line => new RegExp('^import.*' + escapeRegExp(arg.mod)).test(line))) {
54+
if (!lines.some((line) => new RegExp('^import.*' + escapeRegExp(arg.mod)).test(line))) {
5355
edit.insert(document.uri, new vscode.Position(nextInputLine, 0), 'import ' + arg.mod + '\n');
5456
}
5557

@@ -99,11 +101,13 @@ export namespace ImportIdentifier {
99101
const response: any[] = await askHoogleCached(editor.document.getText(identifierRange));
100102

101103
const choice = await vscode.window.showQuickPick(
102-
response.filter(result => result.module.name).map(result => ({
103-
result,
104-
label: result.package.name,
105-
description: result.module.name + ' -- ' + (cheerio.load as any)(result.item, { xml: {} }).text()
106-
}))
104+
response
105+
.filter((result) => result.module.name)
106+
.map((result) => ({
107+
result,
108+
label: result.package.name,
109+
description: result.module.name + ' -- ' + (cheerio.load as any)(result.item, { xml: {} }).text(),
110+
}))
107111
);
108112

109113
if (!choice) {

src/extension.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22
import * as path from 'path';
33
import {
4+
env,
45
commands,
56
ExtensionContext,
67
OutputChannel,
@@ -22,7 +23,7 @@ import {
2223
import { CommandNames } from './commands/constants';
2324
import { ImportIdentifier } from './commands/importIdentifier';
2425
import { DocsBrowser } from './docsBrowser';
25-
import { addPathToProcessPath, findHaskellLanguageServer, IEnvVars } from './hlsBinaries';
26+
import { MissingToolError, addPathToProcessPath, findHaskellLanguageServer, IEnvVars } from './hlsBinaries';
2627
import { expandHomeDir, ExtensionLogger } from './utils';
2728

2829
// The current map of documents & folders to language servers.
@@ -153,7 +154,16 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
153154
return;
154155
}
155156
} catch (e) {
156-
if (e instanceof Error) {
157+
if (e instanceof MissingToolError) {
158+
const link = e.installLink();
159+
if (link) {
160+
if (await window.showErrorMessage(e.message, `Install ${e.tool}`)) {
161+
env.openExternal(link);
162+
}
163+
} else {
164+
await window.showErrorMessage(e.message);
165+
}
166+
} else if (e instanceof Error) {
157167
logger.error(`Error getting the server executable: ${e.message}`);
158168
window.showErrorMessage(e.message);
159169
}
@@ -188,11 +198,11 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
188198

189199
let serverEnvironment: IEnvVars = workspace.getConfiguration('haskell', uri).serverEnvironment;
190200
if (addInternalServerPath !== undefined) {
191-
const newPath = addPathToProcessPath(addInternalServerPath);
192-
serverEnvironment = {
193-
PATH: newPath,
194-
... serverEnvironment
195-
};
201+
const newPath = addPathToProcessPath(addInternalServerPath);
202+
serverEnvironment = {
203+
PATH: newPath,
204+
...serverEnvironment,
205+
};
196206
}
197207
const exeOptions: ExecutableOptions = {
198208
cwd: folder ? undefined : path.dirname(uri.fsPath),

0 commit comments

Comments
 (0)