Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 1.23

Features:
- Add "Pin Cache Variable" functionality to the Project Status panel, allowing users to pin frequently changed CMake cache variables for quick inline editing without opening the full Cache Editor UI. [#3463](https://github.com/microsoft/vscode-cmake-tools/issues/3463)
- Add `cmake.shell` setting to route CMake/CTest/CPack subprocess invocations through a custom shell (e.g., Git Bash, MSYS2), enabling embedded toolchains that require POSIX path translation on Windows. [#1750](https://github.com/microsoft/vscode-cmake-tools/issues/1750)
- triple: Add riscv32be riscv64be support. [#4648](https://github.com/microsoft/vscode-cmake-tools/pull/4648) [@lygstate](https://github.com/lygstate)
- Add command to clear build diagnostics from the Problems pane. [#4691](https://github.com/microsoft/vscode-cmake-tools/pull/4691)
Expand Down
53 changes: 53 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,27 @@
"category": "CMake",
"icon": "$(refresh)"
},
{
"command": "cmake.projectStatus.pinCacheVariable",
"when": "cmake:enableFullFeatureSet",
"title": "%cmake-tools.command.cmake.projectStatus.pinCacheVariable.title%",
"category": "CMake",
"icon": "$(pin)"
},
{
"command": "cmake.projectStatus.editPinnedCacheVariable",
"when": "cmake:enableFullFeatureSet",
"title": "%cmake-tools.command.cmake.projectStatus.editPinnedCacheVariable.title%",
"category": "CMake",
"icon": "$(edit)"
},
{
"command": "cmake.projectStatus.unpinCacheVariable",
"when": "cmake:enableFullFeatureSet",
"title": "%cmake-tools.command.cmake.projectStatus.unpinCacheVariable.title%",
"category": "CMake",
"icon": "$(close)"
},
{
"command": "cmake.pinnedCommands.add",
"when": "cmake:enableFullFeatureSet",
Expand Down Expand Up @@ -1588,6 +1609,18 @@
"command": "cmake.projectStatus.update",
"when": "never"
},
{
"command": "cmake.projectStatus.pinCacheVariable",
"when": "never"
},
{
"command": "cmake.projectStatus.editPinnedCacheVariable",
"when": "never"
},
{
"command": "cmake.projectStatus.unpinCacheVariable",
"when": "never"
},
{
"command": "cmake.pinnedCommands.add",
"when": "never"
Expand Down Expand Up @@ -1735,6 +1768,11 @@
"when": "view == cmake.projectStatus && cmake:enableFullFeatureSet",
"group": "navigation@4"
},
{
"command": "cmake.projectStatus.pinCacheVariable",
"when": "view == cmake.projectStatus && cmake:enableFullFeatureSet",
"group": "navigation@5"
},
{
"command": "cmake.outline.configureAll",
"when": "view == cmake.outline && !cmake:isBuilding",
Expand Down Expand Up @@ -1934,6 +1972,21 @@
"when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'activeProject'",
"group": "inline"
},
{
"command": "cmake.projectStatus.editPinnedCacheVariable",
"when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'pinnedCacheVariable'",
"group": "inline@1"
},
{
"command": "cmake.projectStatus.unpinCacheVariable",
"when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'pinnedCacheVariable'",
"group": "inline@2"
},
{
"command": "cmake.projectStatus.pinCacheVariable",
"when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'pinnedCacheVariables'",
"group": "inline"
},
{
"command": "cmake.outline.search",
"when": "view == cmake.outline && viewItem == 'filter'",
Expand Down
3 changes: 3 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,9 @@
"cmake-tools.configuration.cmake.launchBehavior.newTerminal.markdownDescriptions": "A new terminal instance is created and the target is launched in it. Existing terminals are not automatically cleaned up.",
"cmake-tools.configuration.cmake.loadCompileCommands.description": "Controls whether the extension reads compile_commands.json to enable single file compilation.",
"cmake-tools.command.cmake.projectStatus.update.title": "Refresh the project status",
"cmake-tools.command.cmake.projectStatus.pinCacheVariable.title": "Pin a Cache Variable",
"cmake-tools.command.cmake.projectStatus.editPinnedCacheVariable.title": "Edit Pinned Cache Variable",
"cmake-tools.command.cmake.projectStatus.unpinCacheVariable.title": "Unpin Cache Variable",
"cmake-tools.command.cmake.pinnedCommands.add.title": "Add a CMake command to pin",
"cmake-tools.command.cmake.pinnedCommands.remove.title": "Unpin Command",
"cmake-tools.command.cmake.pinnedCommands.run.title": "Run Command",
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ export class ExtensionManager implements vscode.Disposable {
/**
* The project status view controller
*/
projectStatus = new ProjectStatus();
projectStatus = new ProjectStatus(this.extensionContext);

// NOTE: (from sidebar) The project controller manages all the projects in the workspace
public readonly projectController = new ProjectController(this.extensionContext, this.projectStatus, this.workspaceConfig);
Expand Down
176 changes: 173 additions & 3 deletions src/ui/projectStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CMakeProject from '@cmt/cmakeProject';
import * as preset from '@cmt/presets/preset';
import { runCommand } from '@cmt/util';
import { OptionConfig, checkBuildOverridesPresent, checkConfigureOverridesPresent, checkTestOverridesPresent, checkPackageOverridesPresent } from '@cmt/config';
import { CMakeCache, CacheEntryType } from '@cmt/cache';

nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
Expand All @@ -16,12 +17,14 @@ const noWorkflowPresetSelected = localize('no.workflow.preset.selected', '[No Wo

export let treeDataProvider: TreeDataProvider;

const pinnedCacheVariablesKey = 'cmake.pinnedCacheVariables';

export class ProjectStatus {

protected disposables: vscode.Disposable[] = [];

constructor() {
treeDataProvider = new TreeDataProvider();
constructor(extensionContext?: vscode.ExtensionContext) {
treeDataProvider = new TreeDataProvider(extensionContext);
this.disposables.push(...[
// Commands for projectStatus items
vscode.commands.registerCommand('cmake.projectStatus.stop', async (_node: Node) => {
Expand Down Expand Up @@ -119,6 +122,15 @@ export class ProjectStatus {
}),
vscode.commands.registerCommand('cmake.projectStatus.update', async () => {
await this.refresh();
}),
vscode.commands.registerCommand('cmake.projectStatus.pinCacheVariable', async () => {
await treeDataProvider.pinCacheVariable();
}),
vscode.commands.registerCommand('cmake.projectStatus.editPinnedCacheVariable', async (node: PinnedCacheVariableNode) => {
await treeDataProvider.editPinnedCacheVariable(node);
}),
vscode.commands.registerCommand('cmake.projectStatus.unpinCacheVariable', async (node: PinnedCacheVariableNode) => {
await treeDataProvider.unpinCacheVariable(node);
})
]);
}
Expand Down Expand Up @@ -186,12 +198,14 @@ class TreeDataProvider implements vscode.TreeDataProvider<Node>, vscode.Disposab
private testNode: TestNode | undefined;
private packageNode: PackageNode | undefined;
private workflowNode: WorkflowNode | undefined;
private extensionContext?: vscode.ExtensionContext;

get onDidChangeTreeData(): vscode.Event<Node | undefined> {
return this._onDidChangeTreeData.event;
}

constructor() {
constructor(extensionContext?: vscode.ExtensionContext) {
this.extensionContext = extensionContext;
this.treeView = vscode.window.createTreeView('cmake.projectStatus', { treeDataProvider: this });
}

Expand Down Expand Up @@ -289,6 +303,13 @@ class TreeDataProvider implements vscode.TreeDataProvider<Node>, vscode.Disposab
}
nodes.push(configNode);
}
// Add pinned cache variables between configure and build
const pinnedNames = this.getPinnedCacheVariableNames();
if (pinnedNames.length > 0) {
const pinnedNode = new PinnedCacheVariablesNode(pinnedNames);
await pinnedNode.initialize();
nodes.push(pinnedNode);
}
if (!this.isBuildButtonHidden) {
const buildNode = new BuildNode();
this.buildNode = buildNode;
Expand Down Expand Up @@ -434,6 +455,92 @@ class TreeDataProvider implements vscode.TreeDataProvider<Node>, vscode.Disposab
}
}

// Pinned cache variable management
getPinnedCacheVariableNames(): string[] {
if (!this.extensionContext) {
return [];
}
return this.extensionContext.workspaceState.get<string[]>(pinnedCacheVariablesKey, []);
}

private async savePinnedCacheVariableNames(names: string[]): Promise<void> {
if (!this.extensionContext) {
return;
}
await this.extensionContext.workspaceState.update(pinnedCacheVariablesKey, names);
}

async pinCacheVariable(): Promise<void> {
if (!this.activeCMakeProject) {
return;
}
const cachePathStr = await this.activeCMakeProject.cachePath;
if (!cachePathStr) {
void vscode.window.showErrorMessage(localize('no.cache.available', 'No CMake cache available. Please configure the project first.'));
return;
}
const cache = await CMakeCache.fromPath(cachePathStr);
const entries = cache.allEntries.filter(e => e.type !== CacheEntryType.Internal && e.type !== CacheEntryType.Static);
const alreadyPinned = new Set(this.getPinnedCacheVariableNames());

const items = entries
.filter(e => !alreadyPinned.has(e.key))
.map(e => ({
label: e.key,
description: String(e.value),
detail: e.helpString
}));

if (items.length === 0) {
void vscode.window.showInformationMessage(localize('no.variables.to.pin', 'No cache variables available to pin.'));
return;
}

const chosen = await vscode.window.showQuickPick(items, {
placeHolder: localize('select.variable.to.pin', 'Select a cache variable to pin')
});

if (chosen) {
const names = this.getPinnedCacheVariableNames();
names.push(chosen.label);
await this.savePinnedCacheVariableNames(names);
await this.refresh();
}
}

async editPinnedCacheVariable(node: PinnedCacheVariableNode): Promise<void> {
if (!this.activeCMakeProject || !node.variableName) {
return;
}

const currentValue = node.variableValue ?? '';
const newValue = await vscode.window.showInputBox({
prompt: localize('enter.new.value', 'Enter new value for {0}', node.variableName),
value: currentValue
});

if (newValue !== undefined && newValue !== currentValue) {
// Update cmake.configureSettings with the new value
const configurationScope = this.activeCMakeProject.workspaceFolder;
const config = vscode.workspace.getConfiguration('cmake', configurationScope);
const currentSettings = config.get<{ [key: string]: any }>('configureSettings') ?? {};
currentSettings[node.variableName] = newValue;
await config.update('configureSettings', currentSettings, vscode.ConfigurationTarget.WorkspaceFolder);

// configureOnEdit is handled automatically by the config onChange subscription
await this.refresh();
}
}

async unpinCacheVariable(node: PinnedCacheVariableNode): Promise<void> {
if (!node.variableName) {
return;
}
const names = this.getPinnedCacheVariableNames().filter(n => n !== node.variableName);
await this.savePinnedCacheVariableNames(names);
await this.refresh();
}

}

class Node extends vscode.TreeItem {
Expand Down Expand Up @@ -1142,3 +1249,66 @@ class Variant extends Node {
this.label = treeDataProvider.cmakeProject.activeVariantName || "Debug";
}
}

class PinnedCacheVariablesNode extends Node {

private pinnedNames: string[];
private childNodes: PinnedCacheVariableNode[] = [];

constructor(pinnedNames: string[]) {
super();
this.pinnedNames = pinnedNames;
}

async initialize(): Promise<void> {
this.label = localize('pinned.cache.variables', 'Pinned Cache Variables');
this.tooltip = this.label;
this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
this.contextValue = 'pinnedCacheVariables';
await this.initializeChildren();
}

private async initializeChildren(): Promise<void> {
this.childNodes = [];

let cache: CMakeCache | undefined;
if (treeDataProvider.cmakeProject) {
const cachePathStr = await treeDataProvider.cmakeProject.cachePath;
if (cachePathStr) {
cache = await CMakeCache.fromPath(cachePathStr);
}
}

for (const name of this.pinnedNames) {
const node = new PinnedCacheVariableNode(name);
const entry = cache?.get(name);
node.variableValue = entry ? String(entry.value) : undefined;
await node.initialize();
this.childNodes.push(node);
}
}

getChildren(): Node[] {
return this.childNodes;
}
}

export class PinnedCacheVariableNode extends Node {

public variableName: string;
public variableValue: string | undefined;

constructor(variableName: string) {
super();
this.variableName = variableName;
}

async initialize(): Promise<void> {
const displayValue = this.variableValue ?? localize('not.configured', '[Not configured]');
this.label = this.variableName;
this.description = displayValue;
this.tooltip = `${this.variableName}: ${displayValue}`;
this.collapsibleState = vscode.TreeItemCollapsibleState.None;
this.contextValue = 'pinnedCacheVariable';
}
}