From a557dbd84da7cbc39b3f34f18442395cc17d0a81 Mon Sep 17 00:00:00 2001 From: Kalita Alexey Date: Sat, 22 Apr 2017 13:08:14 +0300 Subject: [PATCH] Do not start racer if it is known that it is not installed --- .../completion/completion_manager.ts | 70 +++++++++++++----- src/components/configuration/Configuration.ts | 73 +++++++++++++++++-- .../tools_installation/installator.ts | 11 ++- src/extension.ts | 8 +- src/legacy_mode_manager.ts | 14 +++- 5 files changed, 140 insertions(+), 36 deletions(-) diff --git a/src/components/completion/completion_manager.ts b/src/components/completion/completion_manager.ts index 30cd07d..72d781a 100644 --- a/src/components/completion/completion_manager.ts +++ b/src/components/completion/completion_manager.ts @@ -55,7 +55,6 @@ export default class CompletionManager { private tmpFile: string; private providers: Disposable[]; private listeners: Disposable[]; - private racerPath: string; private typeMap: { [type: string]: CompletionItemKind } = { 'Struct': CompletionItemKind.Class, 'Module': CompletionItemKind.Module, @@ -110,14 +109,29 @@ export default class CompletionManager { } /** - * Starts itself at first time + * Starts itself at first time. + * Starting fails if a user does not have either racer or Rust's source code */ - public initialStart(): void { + public async initialStart(): Promise { + const logger = this.logger.createChildLogger('initialStart: '); + + const pathToRacer: string | undefined = this.configuration.getPathToRacer(); + + if (!pathToRacer) { + logger.createChildLogger('racer is not installed'); + + return; + } + const isSourceCodeAvailable: boolean = this.ensureSourceCodeIsAvailable(); - if (isSourceCodeAvailable) { - this.start(); + if (!isSourceCodeAvailable) { + logger.createChildLogger('Rust\'s source is not installed'); + + return; } + + this.start(pathToRacer); } /** @@ -146,8 +160,9 @@ export default class CompletionManager { /** * Starts Racer as a daemon, adds itself as definition, completion, hover, signature provider + * @param pathToRacer A path to the executable of racer which does exist */ - private start(): void { + private start(pathToRacer: string): void { const logger = this.logger.createChildLogger('start: '); logger.debug('enter'); @@ -159,9 +174,7 @@ export default class CompletionManager { this.lastCommand = ''; this.providers = []; - this.racerPath = this.configuration.getRacerPath(); - - logger.debug(`racerPath=${this.racerPath}`); + logger.debug(`racerPath=${pathToRacer}`); this.racerStatusBarItem.showTurnedOn(); const cargoHomePath = this.configuration.getCargoHomePath(); @@ -184,7 +197,7 @@ export default class CompletionManager { logger.debug(`ENV[RUST_SRC_PATH] = ${racerSpawnOptions.env['RUST_SRC_PATH']}`); this.racerDaemon = spawn( - this.racerPath, + pathToRacer, ['--interface=tab-text', 'daemon'], racerSpawnOptions ); @@ -198,7 +211,7 @@ export default class CompletionManager { } else { this.racerStatusBarItem.showCrashed(); - this.scheduleRestart(); + this.scheduleRestart(pathToRacer); } }); this.racerDaemon.on('close', (code: number, signal: string) => { @@ -211,7 +224,7 @@ export default class CompletionManager { } else { this.racerStatusBarItem.showCrashed(); - this.scheduleRestart(); + this.scheduleRestart(pathToRacer); } }); @@ -225,11 +238,13 @@ export default class CompletionManager { this.hookCapabilities(); - this.listeners.push(workspace.onDidChangeConfiguration(() => { - const newPath = this.configuration.getRacerPath(); + this.listeners.push(workspace.onDidChangeConfiguration(async () => { + await this.configuration.updatePathToRacer(); - if (this.racerPath !== newPath) { - this.restart(); + const newPathToRacer: string | undefined = this.configuration.getPathToRacer(); + + if (pathToRacer !== newPathToRacer) { + this.restart(newPathToRacer); } })); } @@ -243,15 +258,30 @@ export default class CompletionManager { this.clearCommandCallbacks(); } - public restart(): void { + /** + * Stops the current running instance of racer and starts a new one if a path is defined + * @param pathToRacer A path to the executable of racer + */ + public restart(pathToRacer: string | undefined): void { this.logger.warning('restart'); this.stop(); - this.start(); + + if (pathToRacer) { + this.start(pathToRacer); + } } - private scheduleRestart(): void { - setTimeout(this.restart.bind(this), 3000); + /** + * Schedules a restart after some time + * @param pathToRacer A path to the executable of racer + */ + private scheduleRestart(pathToRacer: string): void { + const onTimeout = () => { + this.restart(pathToRacer); + }; + + setTimeout(onTimeout, 3000); } private stopDaemon(): void { diff --git a/src/components/configuration/Configuration.ts b/src/components/configuration/Configuration.ts index f438779..925d1b5 100644 --- a/src/components/configuration/Configuration.ts +++ b/src/components/configuration/Configuration.ts @@ -55,6 +55,15 @@ export class Configuration { */ private pathToRlsSpecifiedByUser: string | undefined; + /** + * A path to the executable of racer. + * It contains a value of either: + * - the configuration parameter `rust.racerPath` + * - a path found in any of directories specified in the envirionment variable PATH + * The configuration parameter has higher priority than automatically found path + */ + private pathToRacer: string | undefined; + /** * Creates a new instance of the class. * This method is asynchronous because it works with the file system @@ -83,6 +92,7 @@ export class Configuration { logger, rustInstallation, pathToRustSourceCodeSpecifiedByUser, + undefined, undefined ); @@ -91,6 +101,57 @@ export class Configuration { return configuration; } + /** + * Updates the value of the field `pathToRacer`. + * It checks if a user specified any path in the configuration. + * If no path specified or a specified path can't be used, it finds in directories specified in the environment variable PATH. + * This method is asynchronous because it checks if a path exists before setting it to the field + */ + public async updatePathToRacer(): Promise { + const logger = this.logger.createChildLogger('updatePathToRacer: '); + + this.pathToRacer = undefined; + + const pathToRacerSpecifiedByUser: string | undefined = Configuration.getPathConfigParameter('racerPath'); + + const doesPathToRacerSpecifiedByUserExist: boolean = pathToRacerSpecifiedByUser + ? await FileSystem.doesFileOrDirectoryExists(pathToRacerSpecifiedByUser) + : false; + + if (doesPathToRacerSpecifiedByUserExist) { + // A user specified existing path, we will use it + + logger.debug(`Path specified by a user exists. Path=${pathToRacerSpecifiedByUser}`); + + this.pathToRacer = pathToRacerSpecifiedByUser; + + return; + } + + // Either a user specified an invalid path or a user specified nothing + // Let's try to find a path to the executable of racer + const foundPathToRacer: string | undefined = await FileSystem.findExecutablePath('racer'); + + if (!foundPathToRacer) { + // We couldn't find any executable of Racer + + logger.debug('Failed to find racer in PATH'); + + return; + } + + logger.debug(`Found racer=${foundPathToRacer}`); + + this.pathToRacer = foundPathToRacer; + } + + /** + * Returns a value of the field `pathToRacer` + */ + public getPathToRacer(): string | undefined { + return this.pathToRacer; + } + /** * Returns either a path to the executable of RLS or undefined */ @@ -257,12 +318,6 @@ export class Configuration { return configPath || envPath || undefined; } - public getRacerPath(): string { - const racerPath = Configuration.getPathConfigParameter('racerPath'); - - return racerPath || 'racer'; - } - public getRustfmtPath(): string { const rustfmtPath = Configuration.getPathConfigParameter('rustfmtPath'); @@ -424,12 +479,14 @@ export class Configuration { * @param rustInstallation A value for the field `rustInstallation` * @param pathToRustSourceCodeSpecifiedByUser A value for the field `pathToRustSourceCodeSpecifiedByUser` * @param pathToRlsSpecifiedByUser A value for the field `pathToRlsSpecifiedByUser` + * @param pathToRacer A value for the field `pathToRacer` */ private constructor( logger: ChildLogger, rustInstallation: Rustup | NotRustup | undefined, pathToRustSourceCodeSpecifiedByUser: string | undefined, - pathToRlsSpecifiedByUser: string | undefined + pathToRlsSpecifiedByUser: string | undefined, + pathToRacer: string | undefined ) { this.logger = logger; @@ -438,6 +495,8 @@ export class Configuration { this.pathToRustSourceCodeSpecifiedByUser = pathToRustSourceCodeSpecifiedByUser; this.pathToRlsSpecifiedByUser = pathToRlsSpecifiedByUser; + + this.pathToRacer = pathToRacer; } private static getStringParameter(parameterName: string): string | null { diff --git a/src/components/tools_installation/installator.ts b/src/components/tools_installation/installator.ts index 3a98df5..3ce8834 100644 --- a/src/components/tools_installation/installator.ts +++ b/src/components/tools_installation/installator.ts @@ -92,8 +92,8 @@ export default class Installator { logger.debug(`pathDirectories=${JSON.stringify(pathDirectories)}`); - const tools: { [tool: string]: string } = { - 'racer': this.configuration.getRacerPath(), + const tools: { [tool: string]: string | undefined } = { + 'racer': this.configuration.getPathToRacer(), 'rustfmt': this.configuration.getRustfmtPath(), 'rustsym': this.configuration.getRustsymPath() }; @@ -105,6 +105,13 @@ export default class Installator { const missingTools = keys.map(tool => { // Check if the path exists as-is. let userPath = tools[tool]; + + if (!userPath) { + // A path is undefined, so a tool is missing + + return tool; + } + if (existsSync(userPath)) { logger.debug(`${tool}'s path=${userPath}`); diff --git a/src/extension.ts b/src/extension.ts index 2ea5283..18ef4de 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -199,7 +199,7 @@ export async function activate(ctx: ExtensionContext): Promise { logger.createChildLogger('Cargo Manager: ') ); - chooseModeAndRun(ctx, logger, configuration, currentWorkingDirectoryManager); + await chooseModeAndRun(ctx, logger, configuration, currentWorkingDirectoryManager); addExecutingActionOnSave(ctx, configuration, cargoManager); } @@ -243,12 +243,12 @@ function runInRlsMode( languageClientManager.initialStart(); } -function chooseModeAndRun( +async function chooseModeAndRun( context: ExtensionContext, logger: RootLogger, configuration: Configuration, currentWorkingDirectoryManager: CurrentWorkingDirectoryManager -): void { +): Promise { const pathToRlsExecutable = configuration.getPathToRlsExecutable(); if (pathToRlsExecutable) { @@ -261,7 +261,7 @@ function chooseModeAndRun( logger.createChildLogger('Legacy Mode Manager: ') ); - legacyModeManager.start(); + await legacyModeManager.start(); } } diff --git a/src/legacy_mode_manager.ts b/src/legacy_mode_manager.ts index 3edcc1c..64c47e1 100644 --- a/src/legacy_mode_manager.ts +++ b/src/legacy_mode_manager.ts @@ -22,6 +22,8 @@ import MissingToolsInstallator from './components/tools_installation/installator export default class LegacyModeManager { private context: ExtensionContext; + private configuration: Configuration; + private completionManager: CompletionManager; private formattingManager: FormattingManager; @@ -40,6 +42,8 @@ export default class LegacyModeManager { ) { this.context = context; + this.configuration = configuration; + this.completionManager = new CompletionManager( context, configuration, @@ -64,11 +68,15 @@ export default class LegacyModeManager { configuration, logger.createChildLogger('MissingToolsInstallator: ') ); - this.missingToolsInstallator.addStatusBarItemIfSomeToolsAreMissing(); } - public start(): void { + public async start(): Promise { this.context.subscriptions.push(this.completionManager.disposable()); - this.completionManager.initialStart(); + + await this.configuration.updatePathToRacer(); + + await this.missingToolsInstallator.addStatusBarItemIfSomeToolsAreMissing(); + + await this.completionManager.initialStart(); } }