@@ -25,6 +25,12 @@ export type GHCupConfig = {
2525 executablePath ?: string ;
2626} ;
2727
28+ export type ToolInfo = {
29+ tool : Tool ;
30+ version : string ;
31+ tags : string [ ] ;
32+ } ;
33+
2834export class GHCup {
2935 constructor (
3036 readonly logger : Logger ,
@@ -64,27 +70,44 @@ export class GHCup {
6470 * Upgrade the `ghcup` binary unless this option was disabled by the user.
6571 */
6672 public async upgrade ( ) : Promise < void > {
67- const upgrade = this . config . upgradeGHCup ; // workspace.getConfiguration('haskell').get('upgradeGHCup') as boolean;
73+ const upgrade = this . config . upgradeGHCup ;
6874 if ( upgrade ) {
6975 await this . call ( [ 'upgrade' ] , 'Upgrading ghcup' , true ) ;
7076 }
7177 }
7278
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+
7396 /**
7497 * Find the latest version of a {@link Tool} that we can find in GHCup.
7598 * Prefer already installed versions, but fall back to all available versions, if there aren't any.
7699 * @param tool Tool you want to know the latest version of.
77100 * @returns The latest installed or generally available version of the {@link tool}
78101 */
79- public async getLatestVersion ( tool : Tool ) : Promise < string > {
102+ public async getAnyLatestVersion ( tool : Tool ) : Promise < ToolInfo | null > {
80103 // 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 ( ) ;
83106 if ( latestInstalled ) {
84- return latestInstalled . split ( / \s + / ) [ 1 ] ;
107+ return latestInstalled ;
108+ } else {
109+ return this . getLatestAvailableVersion ( tool ) ;
85110 }
86-
87- return this . getLatestAvailableVersion ( tool ) ;
88111 }
89112
90113 /**
@@ -95,16 +118,14 @@ export class GHCup {
95118 * @param tag The tag to filter the available versions with. By default `"latest"`.
96119 * @returns The latest available version filtered by {@link tag}.
97120 */
98- public async getLatestAvailableVersion ( tool : Tool , tag : string = 'latest' ) : Promise < string > {
121+ public async getLatestAvailableVersion ( tool : Tool , tag : string = 'latest' ) : Promise < ToolInfo > {
99122 // 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' ) ;
103124
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 ;
108129 }
109130 } ) ;
110131 if ( ! latestAvailable ) {
@@ -113,6 +134,31 @@ export class GHCup {
113134 return latestAvailable ;
114135 }
115136 }
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+ }
116162}
117163
118164function findGHCup ( logger : Logger , exePath ?: string , folder ?: WorkspaceFolder ) : string {
0 commit comments