Skip to content

Commit d8c8b52

Browse files
committed
Rename and add more docs
1 parent ea9ae78 commit d8c8b52

File tree

4 files changed

+72
-57
lines changed

4 files changed

+72
-57
lines changed

src/config.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { OutputChannel, Uri, window, WorkspaceConfiguration, WorkspaceFolder } from 'vscode';
1+
import { OutputChannel, Uri, window, WorkspaceConfiguration, WorkspaceFolder } from 'vscode';
22
import { expandHomeDir, IEnvVars } from './utils';
33
import * as path from 'path';
44
import { Logger } from 'vscode-languageclient';
@@ -94,10 +94,10 @@ function getClientLogLevel(workspaceConfig: WorkspaceConfiguration): ClientLogLe
9494
clientLogLevel = clientLogLevel_;
9595
break;
9696
default:
97-
throw new Error();
97+
throw new Error("Option \"haskell.trace.client\" is expected to be one of 'off', 'error', 'info', 'debug'.");
9898
}
9999
} else {
100-
throw new Error();
100+
throw new Error('Option "haskell.trace.client" is expected to be a string');
101101
}
102102
return clientLogLevel;
103103
}
@@ -113,10 +113,10 @@ function getLogLevel(workspaceConfig: WorkspaceConfiguration): LogLevel {
113113
logLevel = logLevel_;
114114
break;
115115
default:
116-
throw new Error("haskell.trace.server is expected to be one of 'off', 'messages', 'verbose'.");
116+
throw new Error("Option \"haskell.trace.server\" is expected to be one of 'off', 'messages', 'verbose'.");
117117
}
118118
} else {
119-
throw new Error('haskell.trace.server is expected to be a string');
119+
throw new Error('Option "haskell.trace.server" is expected to be a string');
120120
}
121121
return logLevel;
122122
}

src/errors.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ export class MissingToolError extends HlsError {
2020
prettyTool = 'GHCup';
2121
break;
2222
case 'haskell-language-server':
23-
prettyTool = 'HLS';
24-
break;
2523
case 'hls':
2624
prettyTool = 'HLS';
2725
break;

src/extension.ts

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -120,34 +120,7 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
120120
try {
121121
hlsExecutable = await findHaskellLanguageServer(context, logger, config.ghcupConfig, config.workingDir, folder);
122122
} catch (e) {
123-
if (e instanceof MissingToolError) {
124-
const link = e.installLink();
125-
if (link) {
126-
if (await window.showErrorMessage(e.message, `Install ${e.tool}`)) {
127-
env.openExternal(link);
128-
}
129-
} else {
130-
await window.showErrorMessage(e.message);
131-
}
132-
} else if (e instanceof HlsError) {
133-
logger.error(`General HlsError: ${e.message}`);
134-
window.showErrorMessage(e.message);
135-
} else if (e instanceof NoMatchingHls) {
136-
const link = e.docLink();
137-
logger.error(`${e.message}`);
138-
if (await window.showErrorMessage(e.message, 'Open documentation')) {
139-
env.openExternal(link);
140-
}
141-
} else if (e instanceof Error) {
142-
logger.error(`Internal Error: ${e.message}`);
143-
window.showErrorMessage(e.message);
144-
}
145-
if (e instanceof Error) {
146-
// general stack trace printing
147-
if (e.stack) {
148-
logger.error(`${e.stack}`);
149-
}
150-
}
123+
await handleInitializationError(e, logger);
151124
return;
152125
}
153126

@@ -261,6 +234,43 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
261234
await langClient.start();
262235
}
263236

237+
/**
238+
* Handle errors the extension may throw. Errors are expected to be fatal.
239+
*
240+
* @param e Error thrown during the extension initialization.
241+
* @param logger
242+
*/
243+
async function handleInitializationError(e: unknown, logger: Logger) {
244+
if (e instanceof MissingToolError) {
245+
const link = e.installLink();
246+
if (link) {
247+
if (await window.showErrorMessage(e.message, `Install ${e.tool}`)) {
248+
env.openExternal(link);
249+
}
250+
} else {
251+
await window.showErrorMessage(e.message);
252+
}
253+
} else if (e instanceof HlsError) {
254+
logger.error(`General HlsError: ${e.message}`);
255+
window.showErrorMessage(e.message);
256+
} else if (e instanceof NoMatchingHls) {
257+
const link = e.docLink();
258+
logger.error(`${e.message}`);
259+
if (await window.showErrorMessage(e.message, 'Open documentation')) {
260+
env.openExternal(link);
261+
}
262+
} else if (e instanceof Error) {
263+
logger.error(`Internal Error: ${e.message}`);
264+
window.showErrorMessage(e.message);
265+
}
266+
if (e instanceof Error) {
267+
// general stack trace printing
268+
if (e.stack) {
269+
logger.error(`${e.stack}`);
270+
}
271+
}
272+
}
273+
264274
function initServerEnvironment(config: Config, hlsExecutable: HlsExecutable) {
265275
let serverEnvironment: IEnvVars = config.serverEnvironment;
266276
if (hlsExecutable.tag === 'ghcup') {

src/hlsBinaries.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ function findServerExecutable(logger: Logger, folder?: WorkspaceFolder): string
4949
}
5050
}
5151

52-
/** Searches the PATH. Fails if nothing is found.
52+
/**
53+
* Searches the `PATH` for `haskell-language-server` or `haskell-language-server-wrapper` binary.
54+
* Fails if nothing is found.
55+
* @param logger Log all the stuff!
56+
* @returns Location of the `haskell-language-server` or `haskell-language-server-wrapper` binary if found.
5357
*/
5458
function findHlsInPath(logger: Logger): string {
5559
// try PATH
@@ -87,17 +91,19 @@ export type HlsViaGhcup = {
8791
};
8892

8993
/**
90-
* Downloads the latest haskell-language-server binaries via GHCup.
91-
* Makes sure that either `ghcup` is available locally, otherwise installs
92-
* it into an isolated location.
93-
* If we figure out the correct GHC version, but it isn't compatible with
94-
* the latest HLS executables, we download the latest compatible HLS binaries
95-
* as a fallback.
94+
* Find and setup the Haskell Language Server.
95+
*
96+
* We support three ways of finding the HLS binary:
97+
*
98+
* 1. Let the user provide a location via `haskell.serverExecutablePath` option.
99+
* 2. Find a `haskell-language-server` binary on the `$PATH` if the user wants to do that.
100+
* 3. Use GHCup to install and locate HLS and other required tools, such as cabal, stack and ghc.
96101
*
97102
* @param context Context of the extension, required for metadata.
98103
* @param logger Logger for progress updates.
99104
* @param workingDir Working directory in VSCode.
100-
* @returns Path to haskell-language-server-wrapper
105+
* @param folder Optional workspace folder. If given, will be preferred over {@link workingDir} for finding configuration entries.
106+
* @returns Path to haskell-language-server, paired with additional data required for setting up.
101107
*/
102108
export async function findHaskellLanguageServer(
103109
context: ExtensionContext,
@@ -125,6 +131,7 @@ export async function findHaskellLanguageServer(
125131
// first extension initialization
126132
manageHLS = await promptUserForManagingHls(context, manageHLS);
127133

134+
// based on the user-decision
128135
if (manageHLS === 'PATH') {
129136
const exe = findHlsInPath(logger);
130137
return {
@@ -177,21 +184,21 @@ export async function findHaskellLanguageServer(
177184
// download popups
178185
const promptBeforeDownloads = workspace.getConfiguration('haskell').get('promptBeforeDownloads') as boolean;
179186
if (promptBeforeDownloads) {
180-
const hlsInstalled = latestHLS ? await toolInstalled(ghcup, 'hls', latestHLS) : undefined;
181-
const cabalInstalled = latestCabal ? await toolInstalled(ghcup, 'cabal', latestCabal) : undefined;
182-
const stackInstalled = latestStack ? await toolInstalled(ghcup, 'stack', latestStack) : undefined;
187+
const hlsInstalled = latestHLS ? await installationStatusOfGhcupTool(ghcup, 'hls', latestHLS) : undefined;
188+
const cabalInstalled = latestCabal ? await installationStatusOfGhcupTool(ghcup, 'cabal', latestCabal) : undefined;
189+
const stackInstalled = latestStack ? await installationStatusOfGhcupTool(ghcup, 'stack', latestStack) : undefined;
183190
const ghcInstalled = executableExists('ghc')
184-
? new InstalledTool(
191+
? new ToolStatus(
185192
'ghc',
186193
await callAsync(`ghc${exeExt}`, ['--numeric-version'], logger, undefined, undefined, false),
187194
)
188195
: // if recGHC is null, that means user disabled automatic handling,
189196
recGHC !== null
190-
? await toolInstalled(ghcup, 'ghc', recGHC)
197+
? await installationStatusOfGhcupTool(ghcup, 'ghc', recGHC)
191198
: undefined;
192-
const toInstall: InstalledTool[] = [hlsInstalled, cabalInstalled, stackInstalled, ghcInstalled].filter(
199+
const toInstall: ToolStatus[] = [hlsInstalled, cabalInstalled, stackInstalled, ghcInstalled].filter(
193200
(tool) => tool && !tool.installed,
194-
) as InstalledTool[];
201+
) as ToolStatus[];
195202
if (toInstall.length > 0) {
196203
const decision = await window.showInformationMessage(
197204
`Need to download ${toInstall.map((t) => t.nameWithVersion).join(', ')}, continue?`,
@@ -259,11 +266,11 @@ export async function findHaskellLanguageServer(
259266

260267
// more download popups
261268
if (promptBeforeDownloads) {
262-
const hlsInstalled = projectHls ? await toolInstalled(ghcup, 'hls', projectHls) : undefined;
263-
const ghcInstalled = projectGhc ? await toolInstalled(ghcup, 'ghc', projectGhc) : undefined;
264-
const toInstall: InstalledTool[] = [hlsInstalled, ghcInstalled].filter(
269+
const hlsInstalled = projectHls ? await installationStatusOfGhcupTool(ghcup, 'hls', projectHls) : undefined;
270+
const ghcInstalled = projectGhc ? await installationStatusOfGhcupTool(ghcup, 'ghc', projectGhc) : undefined;
271+
const toInstall: ToolStatus[] = [hlsInstalled, ghcInstalled].filter(
265272
(tool) => tool && !tool.installed,
266-
) as InstalledTool[];
273+
) as ToolStatus[];
267274
if (toInstall.length > 0) {
268275
const decision = await window.showInformationMessage(
269276
`Need to download ${toInstall.map((t) => t.nameWithVersion).join(', ')}, continue?`,
@@ -512,18 +519,18 @@ async function findAvailableHlsBinariesFromGHCup(ghcup: GHCup): Promise<Map<stri
512519
}
513520
}
514521

515-
async function toolInstalled(ghcup: GHCup, tool: Tool, version: string): Promise<InstalledTool> {
522+
async function installationStatusOfGhcupTool(ghcup: GHCup, tool: Tool, version: string): Promise<ToolStatus> {
516523
const b = await ghcup
517524
.call(['whereis', tool, version], undefined, false)
518525
.then(() => true)
519526
.catch(() => false);
520-
return new InstalledTool(tool, version, b);
527+
return new ToolStatus(tool, version, b);
521528
}
522529

523530
/**
524531
* Tracks the name, version and installation state of tools we need.
525532
*/
526-
class InstalledTool {
533+
class ToolStatus {
527534
/**
528535
* "\<name\>-\<version\>" of the installed Tool.
529536
*/

0 commit comments

Comments
 (0)