@@ -5,6 +5,7 @@ import { compare as compareVersion } from 'compare-versions';
55import { URL } from 'url' ;
66import * as path from 'path' ;
77import * as fs from 'fs/promises' ;
8+ import { EffektLanguageClient } from './effektLanguageClient' ;
89
910interface InstallationResult {
1011 success : boolean ;
@@ -18,6 +19,13 @@ interface EffektExecutableInfo {
1819 version : string ;
1920}
2021
22+ export class EffektExecutableNotFoundError extends Error {
23+ constructor ( message : string = "Effekt executable not found" ) {
24+ super ( message ) ;
25+ this . name = "EffektExecutableNotFoundError" ;
26+ }
27+ }
28+
2129/**
2230 * Manages Effekt installation, updates, and status within VS Code.
2331 */
@@ -83,6 +91,15 @@ export class EffektManager {
8391 throw new Error ( "Unable to determine Effekt version" ) ;
8492 }
8593
94+ public async getEffektVersion ( ) : Promise < string > {
95+ if ( ! this . effektVersion ) {
96+ const effektPath = await this . locateEffektExecutable ( ) ;
97+ const currentVersion = await this . fetchEffektVersion ( effektPath . path ) ;
98+ this . effektVersion = currentVersion ;
99+ }
100+ return this . effektVersion || '' ;
101+ }
102+
86103 /**
87104 * Executes a shell command and returns the output.
88105 * @param command The command to execute.
@@ -161,16 +178,15 @@ export class EffektManager {
161178 }
162179 }
163180
164- throw new Error ( 'Effekt executable not found' ) ;
181+ throw new EffektExecutableNotFoundError ( 'Effekt executable not found' ) ;
165182 }
166183
167184 /**
168185 * Installs or updates Effekt.
169- * @param version The version to install or update to.
170186 * @param action The action being performed ('install' or 'update').
171187 * @returns A promise that resolves with the installed/updated version or an empty string.
172188 */
173- private async installOrUpdateEffekt ( version : string , action : 'install' | 'update' ) : Promise < string > {
189+ private async installOrUpdateEffekt ( action : 'install' | 'update' , client ?: EffektLanguageClient ) : Promise < string > {
174190 if ( ! ( await this . checkJava ( ) ) ) {
175191 this . logMessage ( 'INFO' , 'Java is not installed.' ) ;
176192 return '' ;
@@ -186,6 +202,8 @@ export class EffektManager {
186202 cancellable : false
187203 } , async ( progress ) => {
188204 try {
205+ // The client is optional as when installing Effekt, there is no LSP initialized yet
206+ await client ?. stop ( ) ;
189207 progress . report ( { increment : 0 , message : 'Preparing...' } ) ;
190208 await this . runNpmInstall ( ) ;
191209 progress . report ( { increment : 50 , message : 'Verifying installation...' } ) ;
@@ -194,10 +212,14 @@ export class EffektManager {
194212 progress . report ( { increment : 100 , message : 'Completed' } ) ;
195213
196214 this . handleInstallationResult ( verificationResult , action ) ;
215+
216+ await client ?. start ( ) ;
197217 return verificationResult . success ? verificationResult . version || '' : '' ;
198218 } catch ( error ) {
199219 this . showErrorWithLogs ( `Failed to ${ action } Effekt: ${ error } ` ) ;
200220 this . updateStatusBar ( ) ;
221+
222+ await client ?. start ( ) ;
201223 return '' ;
202224 }
203225 } ) ;
@@ -269,20 +291,20 @@ export class EffektManager {
269291 const baseMessage = `Effekt has been ${ action === 'update' ? 'updated' : 'installed' } to version ${ result . version } .` ;
270292
271293 if ( result . executable && ! result . executable . includes ( path . sep ) ) {
272- // Effekt is in PATH
294+ // Effekt is in PATH
273295 const isUpdate = action === 'update' ;
274-
296+
275297 const options = isUpdate ? [ 'View Release Notes' , 'Close' ] : [ 'View Language Introduction' , 'Close' ] ;
276298 const changelogResponse = await vscode . window . showInformationMessage ( baseMessage , ...options ) ;
277-
299+
278300 if ( changelogResponse === 'View Release Notes' ) {
279301 const changelogUrl = `https://github.com/effekt-lang/effekt/releases/tag/v${ result . version } ` ;
280302 vscode . env . openExternal ( vscode . Uri . parse ( changelogUrl ) ) ;
281303 } else if ( changelogResponse === 'View Language Introduction' ) {
282304 const introUrl = 'https://effekt-lang.org/docs/introduction' ;
283305 vscode . env . openExternal ( vscode . Uri . parse ( introUrl ) ) ;
284306 }
285-
307+
286308 } else {
287309 // Effekt is not in PATH
288310 const fullMessage = `${ baseMessage } \n${ result . message } \nConsider adding it to your PATH for easier access.` ;
@@ -306,29 +328,24 @@ export class EffektManager {
306328 * Checks for Effekt updates and offers to install/update if necessary.
307329 * @returns A promise that resolves with the current Effekt version.
308330 */
309- public async checkForUpdatesAndInstall ( ) : Promise < string > {
331+ public async checkForUpdatesAndInstall ( client ?: EffektLanguageClient ) : Promise < string > {
310332 try {
311- const effektPath = await this . locateEffektExecutable ( ) ;
312- if ( ! this . effektVersion ) {
313- const currentVersion = await this . fetchEffektVersion ( effektPath . path ) ;
314- this . effektVersion = currentVersion ;
315- }
316-
333+ let currentVersion = await this . getEffektVersion ( ) ;
317334 const latestVersion = await this . getLatestNPMVersion ( this . effektNPMPackage ) ;
318335
319336 // check if the latest version strictly newer than the current version
320- if ( ! this . effektVersion || compareVersion ( latestVersion , this . effektVersion , '>' ) ) {
321- return this . promptForAction ( latestVersion , 'update' ) ;
337+ if ( ! currentVersion || compareVersion ( latestVersion , currentVersion , '>' ) ) {
338+ return this . promptForAction ( latestVersion , 'update' , client ) ;
322339 } else {
323- vscode . window . showInformationMessage ( `Effekt is up-to-date (version ${ this . effektVersion } ).` ) ;
340+ vscode . window . showInformationMessage ( `Effekt is up-to-date (version ${ currentVersion } ).` ) ;
324341 }
325342
326343 this . updateStatusBar ( ) ;
327344 return this . effektVersion || '' ;
328345 } catch ( error ) {
329346 if ( error instanceof Error && error . message . includes ( 'Effekt executable not found' ) ) {
330347 const latestVersion = await this . getLatestNPMVersion ( this . effektNPMPackage ) ;
331- return this . promptForAction ( latestVersion , 'install' ) ;
348+ return this . promptForAction ( latestVersion , 'install' , client ) ;
332349 } else {
333350 this . showErrorWithLogs ( `Failed to check Effekt: ${ error } ` ) ;
334351 return '' ;
@@ -342,14 +359,14 @@ export class EffektManager {
342359 * @param action The action to perform ('install' or 'update').
343360 * @returns A promise that resolves with the installed/updated version or an empty string.
344361 */
345- private async promptForAction ( version : string , action : 'install' | 'update' ) : Promise < string > {
362+ private async promptForAction ( version : string , action : 'install' | 'update' , client ?: EffektLanguageClient ) : Promise < string > {
346363 const message = action === 'update'
347364 ? `A new version of Effekt is available (${ version } ). Would you like to update?`
348365 : `Effekt ${ version } is available. Would you like to install it?` ;
349366
350367 const response = await vscode . window . showInformationMessage ( message , 'Yes' , 'No' ) ;
351368 if ( response === 'Yes' ) {
352- return this . installOrUpdateEffekt ( version , action ) ;
369+ return this . installOrUpdateEffekt ( action , client ) ;
353370 }
354371 this . updateStatusBar ( ) ;
355372 return this . effektVersion || '' ;
0 commit comments