Skip to content

Commit 77416e0

Browse files
committed
Add error hierarchy and print stacktraces
1 parent df18811 commit 77416e0

File tree

3 files changed

+74
-61
lines changed

3 files changed

+74
-61
lines changed

src/errors.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Uri } from 'vscode';
2+
3+
// tslint:disable max-classes-per-file
4+
export class HlsError extends Error {}
5+
6+
export class MissingToolError extends HlsError {
7+
public readonly tool: string;
8+
constructor(tool: string) {
9+
let prettyTool: string;
10+
switch (tool.toLowerCase()) {
11+
case 'stack':
12+
prettyTool = 'Stack';
13+
break;
14+
case 'cabal':
15+
prettyTool = 'Cabal';
16+
break;
17+
case 'ghc':
18+
prettyTool = 'GHC';
19+
break;
20+
case 'ghcup':
21+
prettyTool = 'GHCup';
22+
break;
23+
case 'haskell-language-server':
24+
prettyTool = 'HLS';
25+
break;
26+
case 'hls':
27+
prettyTool = 'HLS';
28+
break;
29+
default:
30+
prettyTool = tool;
31+
break;
32+
}
33+
super(`Project requires ${prettyTool} but it isn't installed`);
34+
this.tool = prettyTool;
35+
}
36+
37+
public installLink(): Uri | null {
38+
switch (this.tool) {
39+
case 'Stack':
40+
return Uri.parse('https://docs.haskellstack.org/en/stable/install_and_upgrade/');
41+
case 'GHCup':
42+
case 'Cabal':
43+
case 'HLS':
44+
case 'GHC':
45+
return Uri.parse('https://www.haskell.org/ghcup/');
46+
default:
47+
return null;
48+
}
49+
}
50+
}
51+
52+
export class NoMatchingHls extends Error {
53+
constructor(readonly ghcProjVersion: string) {
54+
const noMatchingHLS = `No HLS version was found for supporting GHC ${ghcProjVersion}.`;
55+
super(noMatchingHLS);
56+
}
57+
}

src/extension.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import {
2323
import { CommandNames } from './commands/constants';
2424
import { ImportIdentifier } from './commands/importIdentifier';
2525
import { DocsBrowser } from './docsBrowser';
26-
import { findHaskellLanguageServer, IEnvVars, MissingToolError } from './hlsBinaries';
26+
import { HlsError, MissingToolError } from './errors';
27+
import { findHaskellLanguageServer, IEnvVars } from './hlsBinaries';
2728
import { addPathToProcessPath, expandHomeDir, ExtensionLogger } from './utils';
2829

2930
// The current map of documents & folders to language servers.
@@ -172,8 +173,17 @@ async function activateServerForFolder(context: ExtensionContext, uri: Uri, fold
172173
} else {
173174
await window.showErrorMessage(e.message);
174175
}
176+
} else if (e instanceof HlsError) {
177+
logger.error(`General HlsError: ${e.message}`);
178+
if (e.stack) {
179+
logger.error(`${e.stack}`);
180+
}
181+
window.showErrorMessage(e.message);
175182
} else if (e instanceof Error) {
176-
logger.error(`Error getting the server executable: ${e.message}`);
183+
logger.error(`Internal Error: ${e.message}`);
184+
if (e.stack) {
185+
logger.error(`${e.stack}`);
186+
}
177187
window.showErrorMessage(e.message);
178188
}
179189
return;

src/hlsBinaries.ts

Lines changed: 5 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,9 @@ import * as path from 'path';
77
import { match } from 'ts-pattern';
88
import * as url from 'url';
99
import { promisify } from 'util';
10-
import {
11-
ConfigurationTarget,
12-
ExtensionContext,
13-
ProgressLocation,
14-
Uri,
15-
window,
16-
workspace,
17-
WorkspaceFolder,
18-
} from 'vscode';
10+
import { ConfigurationTarget, ExtensionContext, ProgressLocation, window, workspace, WorkspaceFolder } from 'vscode';
1911
import { Logger } from 'vscode-languageclient';
12+
import { HlsError, MissingToolError, NoMatchingHls } from './errors';
2013
import {
2114
addPathToProcessPath,
2215
executableExists,
@@ -39,52 +32,6 @@ let manageHLS = workspace.getConfiguration('haskell').get('manageHLS') as Manage
3932
// On Windows the executable needs to be stored somewhere with an .exe extension
4033
const exeExt = process.platform === 'win32' ? '.exe' : '';
4134

42-
export class MissingToolError extends Error {
43-
public readonly tool: string;
44-
constructor(tool: string) {
45-
let prettyTool: string;
46-
switch (tool.toLowerCase()) {
47-
case 'stack':
48-
prettyTool = 'Stack';
49-
break;
50-
case 'cabal':
51-
prettyTool = 'Cabal';
52-
break;
53-
case 'ghc':
54-
prettyTool = 'GHC';
55-
break;
56-
case 'ghcup':
57-
prettyTool = 'GHCup';
58-
break;
59-
case 'haskell-language-server':
60-
prettyTool = 'HLS';
61-
break;
62-
case 'hls':
63-
prettyTool = 'HLS';
64-
break;
65-
default:
66-
prettyTool = tool;
67-
break;
68-
}
69-
super(`Project requires ${prettyTool} but it isn't installed`);
70-
this.tool = prettyTool;
71-
}
72-
73-
public installLink(): Uri | null {
74-
switch (this.tool) {
75-
case 'Stack':
76-
return Uri.parse('https://docs.haskellstack.org/en/stable/install_and_upgrade/');
77-
case 'GHCup':
78-
case 'Cabal':
79-
case 'HLS':
80-
case 'GHC':
81-
return Uri.parse('https://www.haskell.org/ghcup/');
82-
default:
83-
return null;
84-
}
85-
}
86-
}
87-
8835
/**
8936
* Call a process asynchronously.
9037
* While doing so, update the windows with progress information.
@@ -489,7 +436,7 @@ async function callGHCup(
489436
callback
490437
);
491438
} else {
492-
throw new Error(`Internal error: tried to call ghcup while haskell.manageHLS is set to ${manageHLS}. Aborting!`);
439+
throw new HlsError(`Internal error: tried to call ghcup while haskell.manageHLS is set to ${manageHLS}. Aborting!`);
493440
}
494441
}
495442

@@ -498,7 +445,7 @@ async function getLatestProjectHLS(
498445
logger: Logger,
499446
workingDir: string,
500447
toolchainBindir: string
501-
): Promise<[string, string | null]> {
448+
): Promise<[string, string]> {
502449
// get project GHC version, but fallback to system ghc if necessary.
503450
const projectGhc = toolchainBindir
504451
? await getProjectGHCVersion(toolchainBindir, workingDir, logger).catch(async (e) => {
@@ -509,7 +456,6 @@ async function getLatestProjectHLS(
509456
return await callAsync(`ghc${exeExt}`, ['--numeric-version'], logger, undefined, undefined, false);
510457
})
511458
: await callAsync(`ghc${exeExt}`, ['--numeric-version'], logger, undefined, undefined, false);
512-
const noMatchingHLS = `No HLS version was found for supporting GHC ${projectGhc}.`;
513459

514460
// first we get supported GHC versions from available HLS bindists (whether installed or not)
515461
const metadataMap = (await getHLSesfromMetadata(context, logger)) || new Map<string, string[]>();
@@ -526,7 +472,7 @@ async function getLatestProjectHLS(
526472
.pop();
527473

528474
if (!latest) {
529-
throw new Error(noMatchingHLS);
475+
throw new NoMatchingHls(projectGhc);
530476
} else {
531477
return [latest[0], projectGhc];
532478
}

0 commit comments

Comments
 (0)