@@ -25,6 +25,12 @@ export type GHCupConfig = {
25
25
executablePath ?: string ;
26
26
} ;
27
27
28
+ export type ToolInfo = {
29
+ tool : Tool ;
30
+ version : string ;
31
+ tags : string [ ] ;
32
+ } ;
33
+
28
34
export class GHCup {
29
35
constructor (
30
36
readonly logger : Logger ,
@@ -64,27 +70,44 @@ export class GHCup {
64
70
* Upgrade the `ghcup` binary unless this option was disabled by the user.
65
71
*/
66
72
public async upgrade ( ) : Promise < void > {
67
- const upgrade = this . config . upgradeGHCup ; // workspace.getConfiguration('haskell').get('upgradeGHCup') as boolean;
73
+ const upgrade = this . config . upgradeGHCup ;
68
74
if ( upgrade ) {
69
75
await this . call ( [ 'upgrade' ] , 'Upgrading ghcup' , true ) ;
70
76
}
71
77
}
72
78
79
+ /**
80
+ * Find the `set` version of a {@link Tool} in GHCup.
81
+ * If no version is set, return null.
82
+ * @param tool Tool you want to know the latest version of.
83
+ * @returns The latest installed or generally available version of the {@link tool}
84
+ */
85
+ public async getSetVersion ( tool : Tool ) : Promise < ToolInfo | null > {
86
+ // these might be custom/stray/compiled, so we try first
87
+ const installedVersions = await this . listTool ( tool , 'set' ) ;
88
+ const latestInstalled = installedVersions . pop ( ) ;
89
+ if ( latestInstalled ) {
90
+ return latestInstalled ;
91
+ } else {
92
+ return null ;
93
+ }
94
+ }
95
+
73
96
/**
74
97
* Find the latest version of a {@link Tool} that we can find in GHCup.
75
98
* Prefer already installed versions, but fall back to all available versions, if there aren't any.
76
99
* @param tool Tool you want to know the latest version of.
77
100
* @returns The latest installed or generally available version of the {@link tool}
78
101
*/
79
- public async getLatestVersion ( tool : Tool ) : Promise < string > {
102
+ public async getAnyLatestVersion ( tool : Tool ) : Promise < ToolInfo | null > {
80
103
// these might be custom/stray/compiled, so we try first
81
- const installedVersions = await this . call ( [ 'list' , '-t' , tool , '-c' , ' installed', '-r' ] , undefined , false ) ;
82
- const latestInstalled = installedVersions . split ( / \r ? \n / ) . pop ( ) ;
104
+ const installedVersions = await this . listTool ( tool , 'installed' ) ;
105
+ const latestInstalled = installedVersions . pop ( ) ;
83
106
if ( latestInstalled ) {
84
- return latestInstalled . split ( / \s + / ) [ 1 ] ;
107
+ return latestInstalled ;
108
+ } else {
109
+ return this . getLatestAvailableVersion ( tool ) ;
85
110
}
86
-
87
- return this . getLatestAvailableVersion ( tool ) ;
88
111
}
89
112
90
113
/**
@@ -95,16 +118,14 @@ export class GHCup {
95
118
* @param tag The tag to filter the available versions with. By default `"latest"`.
96
119
* @returns The latest available version filtered by {@link tag}.
97
120
*/
98
- public async getLatestAvailableVersion ( tool : Tool , tag : string = 'latest' ) : Promise < string > {
121
+ public async getLatestAvailableVersion ( tool : Tool , tag : string = 'latest' ) : Promise < ToolInfo > {
99
122
// fall back to installable versions
100
- const availableVersions = await this . call ( [ 'list' , '-t' , tool , '-c' , 'available' , '-r' ] , undefined , false ) . then (
101
- ( s ) => s . split ( / \r ? \n / ) ,
102
- ) ;
123
+ const availableVersions = await this . listTool ( tool , 'available' ) ;
103
124
104
- let latestAvailable : string | null = null ;
105
- availableVersions . forEach ( ( ver ) => {
106
- if ( ver . split ( / \s + / ) [ 2 ] . split ( ',' ) . includes ( tag ) ) {
107
- latestAvailable = ver . split ( / \s + / ) [ 1 ] ;
125
+ let latestAvailable : ToolInfo | null = null ;
126
+ availableVersions . forEach ( ( toolInfo ) => {
127
+ if ( toolInfo . tags . includes ( tag ) ) {
128
+ latestAvailable = toolInfo ;
108
129
}
109
130
} ) ;
110
131
if ( ! latestAvailable ) {
@@ -113,6 +134,31 @@ export class GHCup {
113
134
return latestAvailable ;
114
135
}
115
136
}
137
+
138
+ private async listTool ( tool : Tool , category : string ) : Promise < ToolInfo [ ] > {
139
+ // fall back to installable versions
140
+ const availableVersions = await this . call ( [ 'list' , '-t' , tool , '-c' , category , '-r' ] , undefined , false ) . then ( ( s ) =>
141
+ s . split ( / \r ? \n / ) ,
142
+ ) ;
143
+
144
+ return availableVersions . map ( ( toolString ) => {
145
+ const toolParts = toolString . split ( / \s + / ) ;
146
+ return {
147
+ tool : tool ,
148
+ version : toolParts [ 1 ] ,
149
+ tags : toolParts [ 2 ] ?. split ( ',' ) ?? [ ] ,
150
+ } ;
151
+ } ) ;
152
+ }
153
+
154
+ public async findLatestUserInstalledTool ( tool : Tool ) : Promise < ToolInfo > {
155
+ let toolInfo = null ;
156
+ toolInfo = await this . getSetVersion ( tool ) ;
157
+ if ( toolInfo ) return toolInfo ;
158
+ toolInfo = await this . getAnyLatestVersion ( tool ) ;
159
+ if ( toolInfo ) return toolInfo ;
160
+ throw new Error ( `Unable to find a version for tool ${ tool } ` ) ;
161
+ }
116
162
}
117
163
118
164
function findGHCup ( logger : Logger , exePath ?: string , folder ?: WorkspaceFolder ) : string {
0 commit comments