Skip to content

Commit f256ba6

Browse files
committed
Add configuration to set path for the hie executable, fixes #62
1 parent 4b66403 commit f256ba6

File tree

4 files changed

+77
-22
lines changed

4 files changed

+77
-22
lines changed

Changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
### 0.0.13
2+
3+
Add configuration to set the path to your HIE executable, if it's not on your PATH. Note
4+
that this adds the `--lsp` argument to the call of this executable.
5+
16
### 0.0.12
27

38
Add configuration to enable/disable HIE, useful for multi-root workspaces.

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Furthermore, the extension supports multiple ways of initializing hie, depending
4646
"languageServerHaskell.useHieWrapper": true,
4747
```
4848

49-
makes VSCode use the `hie-wrapper.sh` file to start hie through. This does assume that you built the hie executable using make build, but will fall back to plain hie.
49+
makes VSCode use the `hie-wrapper.sh` file to start hie through. This does assume that you built the hie executable using make build, but will fall back to plain hie. This will take precedence over `hieExecutablePath`.
5050

5151
#### Custom Wrapper
5252

@@ -62,7 +62,7 @@ There are a few placeholders which will be expanded:
6262
- `~`, `${HOME}` and `${home}` will be expanded into your users' home folder.
6363
- `${workspaceFolder}` and `${workspaceRoot}` will expand into your current project root.
6464

65-
This can be beneficial if you are using something like nix, to have a wrapper script tailored to your setup.
65+
This can be beneficial if you are using something like nix, to have a wrapper script tailored to your setup. This will take precedence over `useHieWrapper` and `hieExecutablePath`.
6666

6767
#### Enable/disable HIE
6868

@@ -72,6 +72,16 @@ You can enable or disable HIE via configuration. This is useful, because multi-r
7272
"languageServerHaskell.enableHIE": true
7373
```
7474

75+
#### Path for hie executable
76+
77+
If you `hie` executable is not on your path, you can manually set it,
78+
79+
```json
80+
"languageServerHaskell.hieExecutablePath": "~/.local/bin/hie"
81+
```
82+
83+
The path placeholders work here as well. Note that this adds the `--lsp` argument to the call of this executable.
84+
7585
## Manual Installation
7686
Either install the extension via the marketplace (preferred), or if you are testing an unreleased version by,
7787

package.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vscode-hie-server",
33
"displayName": "Haskell Language Server",
44
"description": "Language Server Protocol for Haskell via HIE",
5-
"version": "0.0.12",
5+
"version": "0.0.13",
66
"license": "MIT",
77
"publisher": "alanz",
88
"engines": {
@@ -71,23 +71,29 @@
7171
"default": 100,
7272
"description": "Controls the maximum number of problems produced by the server."
7373
},
74+
"languageServerHaskell.hieExecutablePath": {
75+
"scope": "resource",
76+
"type": "string",
77+
"default": "",
78+
"description": "Set the path to your hie executable, if it's not already on your $PATH. Works with ~, ${HOME} and ${workspaceFolder}."
79+
},
7480
"languageServerHaskell.useHieWrapper": {
7581
"scope": "resource",
7682
"type": "boolean",
7783
"default": false,
78-
"description": "Try to automatically select the correct hie version, based on your projects GHC version. NOTE: Build hie using the Makefile to get all versions."
84+
"description": "Try to automatically select the correct hie version, based on your projects GHC version. NOTE: Build hie using the Makefile to get all versions. This will take precedence over hieExecutablePath."
7985
},
8086
"languageServerHaskell.useCustomHieWrapper": {
8187
"scope": "resource",
8288
"type": "boolean",
8389
"default": false,
84-
"description": "Use your own custom wrapper for hie (remember to specify the path!). This will take effect over useHieWrapper."
90+
"description": "Use your own custom wrapper for hie (remember to specify the path!). This will take precedence over useHieWrapper and hieExecutablePath."
8591
},
8692
"languageServerHaskell.useCustomHieWrapperPath": {
8793
"scope": "resource",
8894
"type": "string",
8995
"default": "",
90-
"description": "Specify the full path to your own custom hie wrapper (e.g. /Users/Alice/.hie-wrapper.sh)."
96+
"description": "Specify the full path to your own custom hie wrapper (e.g. ${HOME}/.hie-wrapper.sh). Works with ~, ${HOME} and ${workspaceFolder}."
9197
},
9298
"languageServerHaskell.hlintOn": {
9399
"scope": "resource",
@@ -126,7 +132,7 @@
126132
"scope": "resource",
127133
"type": "boolean",
128134
"default": true,
129-
"description": "Enable/Disable HIE (useful for multi-root workspaces)."
135+
"description": "Enable/disable HIE (useful for multi-root workspaces)."
130136
}
131137
}
132138
},

src/extension.ts

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ async function activateHie(context: ExtensionContext, document: TextDocument) {
6969

7070
try {
7171
const useCustomWrapper = workspace.getConfiguration('languageServerHaskell', uri).useCustomHieWrapper;
72+
const hieExecutablePath = workspace.getConfiguration('languageServerHaskell', uri).hieExecutablePath;
7273
// Check if hie is installed.
73-
if (!await isHieInstalled() && !useCustomWrapper) {
74+
if (!await isHieInstalled() && !useCustomWrapper && hieExecutablePath === '') {
7475
// TODO: Once haskell-ide-engine is on hackage/stackage, enable an option to install it via cabal/stack.
7576
const notInstalledMsg: string =
7677
'hie executable missing, please make sure it is installed, see github.com/haskell/haskell-ide-engine.';
@@ -102,40 +103,67 @@ function activateHieNoCheck(context: ExtensionContext, folder: WorkspaceFolder,
102103
docsBrowserRegistered = true;
103104
}
104105

105-
let hieLaunchScript = 'hie-vscode.sh';
106106
const useCustomWrapper = workspace.getConfiguration('languageServerHaskell', uri).useCustomHieWrapper;
107+
const useHieWrapper = workspace.getConfiguration('languageServerHaskell', uri).useHieWrapper;
108+
let hieExecutablePath = workspace.getConfiguration('languageServerHaskell', uri).hieExecutablePath;
107109
let customWrapperPath = workspace.getConfiguration('languageServerHaskell', uri).useCustomHieWrapperPath;
108110

109-
// Substitute variables with their corresponding locations.
111+
// Substitute path variables with their corresponding locations.
110112
if (useCustomWrapper) {
111113
customWrapperPath = customWrapperPath
112114
.replace('${workspaceFolder}', folder.uri.path)
113115
.replace('${workspaceRoot}', folder.uri.path)
114116
.replace('${HOME}', os.homedir)
115117
.replace('${home}', os.homedir)
116118
.replace(/^~/, os.homedir);
119+
} else if (hieExecutablePath !== '') {
120+
hieExecutablePath = hieExecutablePath
121+
.replace('${workspaceFolder}', folder.uri.path)
122+
.replace('${workspaceRoot}', folder.uri.path)
123+
.replace('${HOME}', os.homedir)
124+
.replace('${home}', os.homedir)
125+
.replace(/^~/, os.homedir);
117126
}
118127

128+
// Set the executable, based on the settings. The order goes:
129+
// First check useCustomWrapper, then check useHieWrapper, then
130+
// check hieExecutablePath, else retain original path.
131+
let hieLaunchScript = 'hie-vscode.sh';
119132
if (useCustomWrapper) {
120133
hieLaunchScript = customWrapperPath;
121-
} else if (workspace.getConfiguration('languageServerHaskell', uri).useHieWrapper) {
134+
} else if (useHieWrapper) {
122135
hieLaunchScript = 'hie-wrapper.sh';
136+
} else if (hieExecutablePath !== '') {
137+
hieLaunchScript = hieExecutablePath;
123138
}
124-
// Don't use the .bat launcher, if the user specified a custom wrapper.
125-
const startupScript = ( process.platform === 'win32' && !useCustomWrapper ) ? 'hie-vscode.bat' : hieLaunchScript;
126-
const serverPath = useCustomWrapper ? startupScript : context.asAbsolutePath(path.join('.', startupScript));
127139

140+
// Don't use the .bat launcher, if the user specified a custom wrapper or a executable path.
141+
const startupScript = ( process.platform === 'win32' && !useCustomWrapper && !hieExecutablePath )
142+
? 'hie-vscode.bat'
143+
: hieLaunchScript;
144+
// If using a custom wrapper or specificed an executable path, the path is assumed to already
145+
// be absolute.
146+
const serverPath = useCustomWrapper || hieExecutablePath
147+
? startupScript
148+
: context.asAbsolutePath(path.join('.', startupScript));
149+
150+
const tempDir = ( process.platform === 'win32' ) ? '%TEMP%' : '/tmp';
151+
const runArgs = [];
152+
const debugArgs = ['-d', '-l', path.join(tempDir, 'hie.log')];
153+
if (!useCustomWrapper && !useHieWrapper && hieExecutablePath !== '') {
154+
runArgs.unshift('--lsp');
155+
debugArgs.unshift('--lsp');
156+
}
128157
// If the extension is launched in debug mode then the debug server options are used,
129158
// otherwise the run options are used
130-
const tempDir = ( process.platform === 'win32' ) ? '%TEMP%' : '/tmp';
131159
const serverOptions: ServerOptions = {
132-
run: { command: serverPath, transport: TransportKind.stdio, },
133-
debug: { command: serverPath, transport: TransportKind.stdio, args: ['-d', '-l', path.join(tempDir, 'hie.log')] },
160+
run: { command: serverPath, transport: TransportKind.stdio, args: runArgs, },
161+
debug: { command: serverPath, transport: TransportKind.stdio, args: debugArgs },
134162
};
135163

164+
// Set a unique name per workspace folder (useful for multi-root workspaces).
136165
const langName = 'Haskell HIE (' + folder.name + ')';
137166
const outputChannel: OutputChannel = window.createOutputChannel(langName);
138-
// Options to control the language client
139167
const clientOptions: LanguageClientOptions = {
140168
// Use the document selector to only notify the LSP on files inside the folder
141169
// path for the specific workspace.
@@ -156,18 +184,18 @@ function activateHieNoCheck(context: ExtensionContext, folder: WorkspaceFolder,
156184
middleware: {
157185
provideHover: DocsBrowser.hoverLinksMiddlewareHook,
158186
},
159-
// Set the CWD to the workspace folder.
187+
// Set the current working directory, for HIE, to be the workspace folder.
160188
workspaceFolder: folder,
161189
};
162190

163-
// Create the language client and start the client.
191+
// Create the LSP client.
164192
const langClient = new LanguageClient(langName, langName, serverOptions, clientOptions);
165193

166194
if (workspace.getConfiguration('languageServerHaskell', uri).showTypeForSelection.onHover) {
167195
context.subscriptions.push(ShowTypeHover.registerTypeHover(clients));
168196
}
197+
// Register editor commands for HIE, but only register the commands once.
169198
if (!hieCommandsRegistered) {
170-
// Only register the commands once.
171199
context.subscriptions.push(InsertType.registerCommand(clients));
172200
ShowTypeCommand.registerCommand(clients).forEach(x => context.subscriptions.push(x));
173201
registerHiePointCommand('hie.commands.demoteDef', 'hare:demote', context);
@@ -178,7 +206,7 @@ function activateHieNoCheck(context: ExtensionContext, folder: WorkspaceFolder,
178206
hieCommandsRegistered = true;
179207
}
180208

181-
// langClient.registerProposedFeatures();
209+
// Finally start the client and add it to the list of clients.
182210
langClient.start();
183211
clients.set(folder.uri.toString(), langClient);
184212
}
@@ -194,13 +222,19 @@ export function deactivate(): Thenable<void> {
194222
return Promise.all(promises).then(() => undefined);
195223
}
196224

225+
/*
226+
* Check if HIE is installed.
227+
*/
197228
async function isHieInstalled(): Promise<boolean> {
198229
return new Promise<boolean>((resolve, reject) => {
199230
const cmd: string = ( process.platform === 'win32' ) ? 'where hie' : 'which hie';
200231
child_process.exec(cmd, (error, stdout, stderr) => resolve(!error));
201232
});
202233
}
203234

235+
/*
236+
* Create an editor command that calls an action on the active LSP server.
237+
*/
204238
async function registerHiePointCommand(name: string,
205239
command: string,
206240
context: ExtensionContext) {

0 commit comments

Comments
 (0)