diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b203316..af3641346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,6 +101,7 @@ Improvements: - Make it easier for a new developer of CMake Tools to run tests. [#4620](https://github.com/microsoft/vscode-cmake-tools/pull/4620) [@cwalther](https://github.com/cwalther) Bug Fixes: +- Fix cache edits from "Edit Cache (UI)" being overwritten in presets mode when the edited variable is defined by the active configure preset. The extension now writes an override into CMakeUserPresets.json and reconfigures with that user preset so the change persists. - Fix stale C/C++ custom-configuration entries persisting after reconfigure/preset switches, which could cause Go to Definition/IntelliSense to surface symbols from inactive sources in the same folder. [#4472](https://github.com/microsoft/vscode-cmake-tools/issues/4472) - Fix IntelliSense not updating when switching the active project in multi-project workspaces with multiple `cmake.sourceDirectory` entries. [#4390](https://github.com/microsoft/vscode-cmake-tools/issues/4390) - Fix `tasks.json` schema validation rejecting valid CMake task commands `package` and `workflow`. [#4167](https://github.com/microsoft/vscode-cmake-tools/issues/4167) diff --git a/src/presets/presetsController.ts b/src/presets/presetsController.ts index 249e7f5b8..c517b5a1c 100644 --- a/src/presets/presetsController.ts +++ b/src/presets/presetsController.ts @@ -1723,7 +1723,7 @@ export class PresetsController implements vscode.Disposable { return { insertSpaces, tabSize }; } - async updatePresetsFile(presetsFile: preset.PresetsFile, isUserPresets = false): Promise { + async updatePresetsFile(presetsFile: preset.PresetsFile, isUserPresets = false, showInEditor = true): Promise { const presetsFilePath = isUserPresets ? this.userPresetsPath : this.presetsPath; const indent = this.getIndentationSettings(); try { @@ -1733,6 +1733,10 @@ export class PresetsController implements vscode.Disposable { return; } + if (!showInEditor) { + return undefined; + } + return vscode.window.showTextDocument(vscode.Uri.file(presetsFilePath)); } diff --git a/src/ui/cacheView.ts b/src/ui/cacheView.ts index 92567f65d..ded30d124 100644 --- a/src/ui/cacheView.ts +++ b/src/ui/cacheView.ts @@ -12,12 +12,14 @@ nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFo const localize: nls.LocalizeFunc = nls.loadMessageBundle(); export interface IOption { - key: string; // same as CMake cache variable key names - type: string; // "Bool" for boolean and "String" for anything else for now + key: string; // same as CMake cache variable key names + type: string; // "Bool" for boolean and "String" for anything else for now helpString: string; choices: string[]; - value: string; // value from the cache file or changed in the UI + value: string | boolean; // value from the cache file or changed in the UI dirty: boolean; // if the variable was edited in the UI + presetSource?: string; // where this variable is defined in presets + differsFromPresetValue?: boolean; // true if cache value differs from preset value } /** @@ -50,7 +52,11 @@ export class ConfigurationWebview { private options: IOption[] = []; - constructor(protected cachePath: string, protected save: () => void) { + constructor( + protected cachePath: string, + protected save: (dirtyOptions: IOption[]) => Promise, + protected getPresetOptionMetadata?: (options: IOption[]) => Promise> + ) { this.panel = vscode.window.createWebviewPanel( 'cmakeConfiguration', // Identifies the type of the webview. Used internally this.cmakeCacheEditorText, // Title of the panel displayed to the user @@ -66,11 +72,17 @@ export class ConfigurationWebview { async persistCacheEntries() { if (this.isDirty) { telemetry.logEvent("editCMakeCache", { command: "saveCMakeCacheUI" }); - await this.saveCmakeCache(this.options); + const dirtyOptions = this.options.filter(option => option.dirty); + await this.saveCmakeCache(dirtyOptions); void vscode.window.showInformationMessage(localize('cmake.cache.saved', 'CMake options have been saved.')); - // start configure - this.save(); + // Start configure after persisting the edited values. + await this.save(dirtyOptions); this.isDirty = false; + // Force a post-configure refresh so the editor reflects cache changes immediately. + this.options = await this.getConfigurationOptions(); + if (this.panel.visible) { + await this.renderWebview(this.panel, false); + } } } @@ -207,7 +219,11 @@ export class ConfigurationWebview { async saveCmakeCache(options: IOption[]) { const cmakeCache = await CMakeCache.fromPath(this.cachePath); - await cmakeCache.saveAll(options); + const serializedOptions = options.map(option => ({ + key: option.key, + value: util.isBoolean(option.value) ? (option.value ? 'TRUE' : 'FALSE') : option.value + })); + await cmakeCache.saveAll(serializedOptions); } /** @@ -225,6 +241,18 @@ export class ConfigurationWebview { options.push({ key: entry.key, helpString: entry.helpString, choices: entry.choices, type: (entry.type === CacheEntryType.Bool) ? "Bool" : "String", value: entry.value, dirty: false }); } } + + if (this.getPresetOptionMetadata) { + const metadataByKey = await this.getPresetOptionMetadata(options); + for (const option of options) { + const metadata = metadataByKey.get(option.key); + if (metadata) { + option.presetSource = metadata.presetSource; + option.differsFromPresetValue = metadata.differsFromPresetValue; + } + } + } + return options; } @@ -254,6 +282,10 @@ export class ConfigurationWebview { const saveButtonText = localize("save", "Save"); const keyColumnText = localize("key", "Key"); const valueColumnText = localize("value", "Value"); + const sourceColumnText = localize("source", "Source"); + const notSetInPresetsText = localize("not.set.in.presets", "Cmake Cache"); + const differsFromPresetText = localize("differs.from.preset", "Changed in cache"); + const matchesPresetText = localize("matches.preset", "Matches preset"); let html = ` @@ -484,6 +516,7 @@ export class ConfigurationWebview { ${keyColumnText} ${valueColumnText} + ${sourceColumnText} ${key} @@ -506,9 +539,10 @@ export class ConfigurationWebview { const id = escapeAttribute(option.key); let editControls = ''; + const stringValue = util.isBoolean(option.value) ? (option.value ? 'TRUE' : 'FALSE') : String(option.value); if (option.type === "Bool") { - editControls = ` + editControls = `