From e7cf93eec02fc4825194bc73c90f162463c5bb50 Mon Sep 17 00:00:00 2001 From: Fatima Qarni Date: Thu, 1 Aug 2024 12:53:21 -0700 Subject: [PATCH] Set usepresets context after manually creating presets file (#3935) Fixes #3854. Support for watching multiple paths (cmakepresets.json/cmakeuserpresets.json/include files) was broken due to a Chokidar bug. Created a wrapper class to handle this scenario. --- CHANGELOG.md | 1 + src/cmakeProject.ts | 4 +-- src/presetsController.ts | 68 ++++++++++++++++++++++++++++++++-------- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b312edcdf..2c72037d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Bug Fixes: - Fix our keybindings for debug and run without debugging to better match VS Code. [#3507](https://github.com/microsoft/vscode-cmake-tools/issues/3507) - Allow success recovery in the configure precondition handler. [#3554](https://github.com/microsoft/vscode-cmake-tools/issues/3554) - Prevent second configure after `QuickStart` if the `automaticReconfigure` setting is enabled. [#3910](https://github.com/microsoft/vscode-cmake-tools/issues/3910) +- Set usepresets context after manually creating a CMakePresets.json/CMakeUserPresets.json or using QuickStart to create it. [#3854](https://github.com/microsoft/vscode-cmake-tools/issues/3854) ## 1.18.43 diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts index 6dbc9022c..6e66b9b35 100644 --- a/src/cmakeProject.ts +++ b/src/cmakeProject.ts @@ -139,7 +139,7 @@ export class CMakeProject { private onDidOpenTextDocumentListener: vscode.Disposable | undefined; private disposables: vscode.Disposable[] = []; private readonly onUseCMakePresetsChangedEmitter = new vscode.EventEmitter(); - private projectController: ProjectController | undefined; + public projectController: ProjectController | undefined; public readonly cTestController: CTestDriver; public readonly cPackageController: CPackDriver; public readonly workflowController: WorkflowDriver; @@ -2921,8 +2921,6 @@ export class CMakeProject { } } - await this.projectController?.updateActiveProject(this.workspaceFolder); - // Regardless of the following configure return code, // we want full feature set view for the whole workspace. await enableFullFeatureSet(true); diff --git a/src/presetsController.ts b/src/presetsController.ts index e3bff981b..d687566c3 100644 --- a/src/presetsController.ts +++ b/src/presetsController.ts @@ -27,8 +27,8 @@ const log = logging.createLogger('presetController'); type SetPresetsFileFunc = (folder: string, presets: preset.PresetsFile | undefined) => void; -export class PresetsController { - private _presetsWatcher: chokidar.FSWatcher | undefined; +export class PresetsController implements vscode.Disposable { + private _presetsWatchers: FileWatcher | undefined; private _sourceDir: string = ''; private _sourceDirChangedSub: vscode.Disposable | undefined; private _presetsFileExists = false; @@ -1943,26 +1943,68 @@ export class PresetsController { return vscode.window.showTextDocument(vscode.Uri.file(presetsFilePath)); } + // this is used for the file watcher on adding a new presets file + async onCreatePresetsFile() { + await this.reapplyPresets(); + await this.project.projectController?.updateActiveProject(this.workspaceFolder); + } + private async watchPresetsChange() { - if (this._presetsWatcher) { - this._presetsWatcher.close().then(() => {}, () => {}); - } - const handler = () => { + const presetChangeHandler = () => { void this.reapplyPresets(); }; - this._presetsWatcher = chokidar.watch(this._referencedFiles, { ignoreInitial: true }) - .on('add', handler) - .on('change', handler) - .on('unlink', handler); + const presetCreatedHandler = () => { + void this.onCreatePresetsFile(); + }; + + const events: Map void> = new Map void>([ + ["change", presetChangeHandler], + ["unlink", presetChangeHandler], + ["add", presetCreatedHandler] + ]); + + this._presetsWatchers?.dispose(); + this._presetsWatchers = new FileWatcher(this._referencedFiles, events, { ignoreInitial: true }); }; dispose() { - if (this._presetsWatcher) { - this._presetsWatcher.close().then(() => {}, () => {}); - } + this._presetsWatchers?.dispose(); + if (this._sourceDirChangedSub) { this._sourceDirChangedSub.dispose(); } } } + +/** + * FileWatcher is a wrapper around chokidar's FSWatcher that allows for watching multiple paths. + * Chokidar's support for watching multiple paths is currently broken, if it is fixed in the future, this class can be removed. + */ +class FileWatcher implements vscode.Disposable { + private watchers: Map; + + public constructor(paths: string | string[], eventHandlers: Map void>, options?: chokidar.WatchOptions) { + this.watchers = new Map(); + + for (const path of Array.isArray(paths) ? paths : [paths]) { + try { + const watcher = chokidar.watch(path, { ...options }); + const eventHandlerEntries = Array.from(eventHandlers); + for (let i = 0; i < eventHandlerEntries.length; i++) { + const [event, handler] = eventHandlerEntries[i]; + watcher.on(event, handler); + } + this.watchers.set(path, watcher); + } catch (error) { + log.error(localize('failed.to.watch', 'Watcher could not be created for {0}: {1}', path, util.errorToString(error))); + } + } + } + + public dispose() { + for (const watcher of this.watchers.values()) { + watcher.close().then(() => {}, () => {}); + } + } +}