1
- 'use strict' ;
2
-
3
1
import * as child_process from 'child_process' ;
4
2
import * as fs from 'fs' ;
5
3
import * as https from 'https' ;
6
4
import * as path from 'path' ;
7
5
import * as url from 'url' ;
8
- import { ExtensionContext , ProgressLocation , Uri , window , workspace , WorkspaceFolder } from 'vscode' ;
6
+ import { env , ExtensionContext , ProgressLocation , Uri , window , workspace , WorkspaceFolder } from 'vscode' ;
9
7
import { downloadFile , executableExists , userAgentHeader } from './utils' ;
10
8
11
9
/** GitHub API release */
@@ -23,6 +21,43 @@ interface IAsset {
23
21
// On Windows the executable needs to be stored somewhere with an .exe extension
24
22
const exeExtension = process . platform === 'win32' ? '.exe' : '' ;
25
23
24
+ class MissingToolError extends Error {
25
+ public readonly tool : string ;
26
+ constructor ( tool : string ) {
27
+ let prettyTool : string ;
28
+ switch ( tool ) {
29
+ case 'stack' :
30
+ prettyTool = 'Stack' ;
31
+ break ;
32
+ case 'cabal' :
33
+ prettyTool = 'Cabal' ;
34
+ break ;
35
+ case 'ghc' :
36
+ prettyTool = 'GHC' ;
37
+ break ;
38
+ default :
39
+ prettyTool = tool ;
40
+ break ;
41
+ }
42
+ super ( `Project requires ${ prettyTool } but it isn't installed` ) ;
43
+ this . tool = prettyTool ;
44
+ }
45
+
46
+ public installLink ( ) : Uri | null {
47
+ switch ( this . tool ) {
48
+ case 'Stack' :
49
+ return Uri . parse ( 'https://docs.haskellstack.org/en/stable/install_and_upgrade/' ) ;
50
+ case 'Cabal' :
51
+ case 'GHC' :
52
+ return process . platform === 'win32'
53
+ ? Uri . parse ( 'https://www.haskell.org/platform/index.html#windows' )
54
+ : Uri . parse ( 'https://www.haskell.org/ghcup/' ) ;
55
+ default :
56
+ return null ;
57
+ }
58
+ }
59
+ }
60
+
26
61
/** Works out what the project's ghc version is, downloading haskell-language-server-wrapper
27
62
* if needed. Returns null if there was an error in either downloading the wrapper or
28
63
* in working out the ghc version
@@ -41,7 +76,15 @@ async function getProjectGhcVersion(context: ExtensionContext, dir: string, rele
41
76
throw out . error ;
42
77
}
43
78
if ( out . status !== 0 ) {
44
- throw Error ( `${ wrapper } --project-ghc-version exited with exit code ${ out . status } :\n${ out . stderr } ` ) ;
79
+ const regex = / C r a d l e r e q u i r e s ( .+ ) b u t c o u l d n ' t f i n d i t / ;
80
+ const res = regex . exec ( out . stderr ) ;
81
+ if ( res ) {
82
+ throw new MissingToolError ( res [ 1 ] ) ;
83
+ }
84
+
85
+ throw Error (
86
+ `${ wrapper } --project-ghc-version exited with exit code ${ out . status } :\n${ out . stdout } \n${ out . stderr } `
87
+ ) ;
45
88
}
46
89
return out . stdout . trim ( ) ;
47
90
}
@@ -87,16 +130,11 @@ async function getProjectGhcVersion(context: ExtensionContext, dir: string, rele
87
130
* Downloads the latest haskell-language-server binaries from GitHub releases.
88
131
* Returns null if it can't find any that match.
89
132
*/
90
- export async function downloadServer (
133
+ export async function downloadHaskellLanguageServer (
91
134
context : ExtensionContext ,
92
135
resource : Uri ,
93
136
folder ?: WorkspaceFolder
94
137
) : Promise < string | null > {
95
- // We only download binaries for haskell-language-server at the moment
96
- if ( workspace . getConfiguration ( 'haskell' , resource ) . languageServerVariant !== 'haskell-language-server' ) {
97
- return null ;
98
- }
99
-
100
138
// Fetch the latest release from GitHub
101
139
const releases : IRelease [ ] = await new Promise ( ( resolve , reject ) => {
102
140
let data : string = '' ;
@@ -137,8 +175,19 @@ export async function downloadServer(
137
175
try {
138
176
ghcVersion = await getProjectGhcVersion ( context , dir , release ) ;
139
177
} catch ( error ) {
140
- // We couldn't figure out the right ghc version to download
141
- window . showErrorMessage ( `Couldn't figure out what GHC version the project is using:\n${ error . message } ` ) ;
178
+ if ( error instanceof MissingToolError ) {
179
+ const link = error . installLink ( ) ;
180
+ if ( link ) {
181
+ if ( await window . showErrorMessage ( error . message , `Install ${ error . tool } ` ) ) {
182
+ env . openExternal ( link ) ;
183
+ }
184
+ } else {
185
+ await window . showErrorMessage ( error . message ) ;
186
+ }
187
+ } else {
188
+ // We couldn't figure out the right ghc version to download
189
+ window . showErrorMessage ( `Couldn't figure out what GHC version the project is using:\n${ error . message } ` ) ;
190
+ }
142
191
return null ;
143
192
}
144
193
0 commit comments