Skip to content

Commit

Permalink
Update project outline on configure (#3270)
Browse files Browse the repository at this point in the history
* Prototype outline update

* Replace "for" by "find"

* Provide source dir for target node commands

* Make formating the same

* Update by code model update not projectcontroller

* Pass cmake project to remove duplication

* Remove unused project controller

* Indent long return statement

* Add changelog entry

---------

Co-authored-by: Garrett Campbell <[email protected]>
  • Loading branch information
vlavati and gcampbell-msft authored Sep 14, 2023
1 parent df0f09e commit 1fb304b
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 68 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Features:
- Support different debug config for different targets. [PR #2801](https://github.com/microsoft/vscode-cmake-tools/pull/2801) [@RichardLuo0](https://github.com/RichardLuo0)
- Add ability to get a test's `WORKING_DIRECTORY` in launch.json via `cmake.testWorkingDirectory` [PR #3336](https://github.com/microsoft/vscode-cmake-tools/pull/3336)

Improvements:
- In multi-root workspace, the Project Outline View now shows all configured projects. [PR #3270](https://github.com/microsoft/vscode-cmake-tools/pull/3270) [@vlavati](https://github.com/vlavati)

## 1.15
Features:
- Added support for the CMake Debugger. [#3093](https://github.com/microsoft/vscode-cmake-tools/issues/3093)
Expand Down
81 changes: 36 additions & 45 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -628,14 +628,7 @@ export class ExtensionManager implements vscode.Disposable {
return;
}
const folder: vscode.WorkspaceFolder = cmakeProject.workspaceFolder;
this.projectOutline.updateCodeModel(
cmakeProject.workspaceContext.folder,
cmakeProject.codeModelContent,
{
defaultTarget: cmakeProject.defaultBuildTarget || undefined,
launchTargetName: cmakeProject.launchTargetName
}
);
this.projectOutline.updateCodeModel(cmakeProject, cmakeProject.codeModelContent);
rollbar.invokeAsync(localize('update.code.model.for.cpptools', 'Update code model for cpptools'), {}, async () => {
if (vscode.workspace.getConfiguration('C_Cpp', folder.uri).get<string>('intelliSenseEngine')?.toLocaleLowerCase() === 'disabled') {
log.debug(localize('update.intellisense.disabled', 'Not updating the configuration provider because {0} is set to {1}', '"C_Cpp.intelliSenseEngine"', '"Disabled"'));
Expand Down Expand Up @@ -677,14 +670,7 @@ export class ExtensionManager implements vscode.Disposable {
} else if (drv && drv.codeModelContent) {
codeModelContent = drv.codeModelContent;
this.configProvider.updateConfigurationData({ cache, codeModelContent, clCompilerPath, activeTarget: cmakeProject.defaultBuildTarget, activeBuildTypeVariant: actualBuildType, folder: cmakeProject.folderPath });
this.projectOutline.updateCodeModel(
cmakeProject.workspaceContext.folder,
codeModelContent,
{
defaultTarget: cmakeProject.defaultBuildTarget || undefined,
launchTargetName: cmakeProject.launchTargetName
}
);
this.projectOutline.updateCodeModel(cmakeProject, codeModelContent);
}
// Inform cpptools that custom CppConfigurationProvider will be able to service the current workspace.
this.ensureCppToolsProviderRegistered();
Expand Down Expand Up @@ -1044,32 +1030,30 @@ export class ExtensionManager implements vscode.Disposable {
return 0;
}

private getProjectFromFolder(folder?: vscode.WorkspaceFolder | string) {
private getProjectFromFolder(folder?: vscode.WorkspaceFolder | string, sourceDir?: string) {
const workspaceFolder: vscode.WorkspaceFolder | undefined = this.getWorkspaceFolder(folder);
if (workspaceFolder) {
const activeProject: CMakeProject | undefined = this.getActiveProject();
const projects: CMakeProject[] | undefined = this.projectController.getProjectsForWorkspaceFolder(workspaceFolder);
if (!projects || projects.length === 0) {
return activeProject;
} else {
for (const project of projects) {
// Choose the active project.
if (activeProject?.folderPath === project.folderPath) {
return project;
}
}
// Choose the first project folder.
return projects[0];
// Choose project by corresponding source directory
return projects.find(project => sourceDir && (path.normalize(sourceDir) === path.normalize(project.folderPath)))
// Choose project by folder of active project
?? projects.find(project => activeProject?.folderPath === project.folderPath)
// Fallback to first project
?? projects[0];
}
}
return undefined;
}

runCMakeCommand(command: RunCMakeCommand, folder?: vscode.WorkspaceFolder, precheck?: (cmakeProject: CMakeProject) => Promise<boolean>, cleanOutputChannel?: boolean): Promise<any> {
runCMakeCommand(command: RunCMakeCommand, folder?: vscode.WorkspaceFolder, precheck?: (cmakeProject: CMakeProject) => Promise<boolean>, cleanOutputChannel?: boolean, sourceDir?: string): Promise<any> {
if (cleanOutputChannel) {
this.cleanOutputChannel();
}
const project = this.getProjectFromFolder(folder);
const project = this.getProjectFromFolder(folder, sourceDir);
if (project) {
return this.runCMakeCommandForProject(command, project, precheck);
}
Expand Down Expand Up @@ -1157,13 +1141,20 @@ export class ExtensionManager implements vscode.Disposable {
return this.runCMakeCommand(cmakeProject => cmakeProject.editCacheUI());
}

build(folder?: vscode.WorkspaceFolder, name?: string, showCommandOnly?: boolean, isBuildCommand?: boolean) {
build(folder?: vscode.WorkspaceFolder, name?: string, sourceDir?: string, showCommandOnly?: boolean, isBuildCommand?: boolean) {
telemetry.logEvent("build", { all: "false"});
return this.runCMakeCommand(cmakeProject => cmakeProject.build(name ? [name] : undefined, showCommandOnly, (isBuildCommand === undefined) ? true : isBuildCommand), folder, this.ensureActiveBuildPreset, true);
return this.runCMakeCommand(cmakeProject => {
const targets = name ? [name] : undefined;
return cmakeProject.build(targets, showCommandOnly, (isBuildCommand === undefined) ? true : isBuildCommand);
},
folder,
this.ensureActiveBuildPreset,
true,
sourceDir);
}

showBuildCommand(folder?: vscode.WorkspaceFolder, name?: string) {
return this.build(folder, name, true, false);
return this.build(folder, name, undefined, true, false);
}

buildAll(name?: string | string[]) {
Expand All @@ -1176,8 +1167,8 @@ export class ExtensionManager implements vscode.Disposable {
true);
}

setDefaultTarget(folder?: vscode.WorkspaceFolder, name?: string) {
return this.runCMakeCommand(cmakeProject => cmakeProject.setDefaultTarget(name), folder);
setDefaultTarget(folder?: vscode.WorkspaceFolder, name?: string, sourceDir?: string) {
return this.runCMakeCommand(cmakeProject => cmakeProject.setDefaultTarget(name), folder, undefined, undefined, sourceDir);
}

setVariant(folder?: vscode.WorkspaceFolder, name?: string) {
Expand Down Expand Up @@ -1218,7 +1209,7 @@ export class ExtensionManager implements vscode.Disposable {

clean(folder?: vscode.WorkspaceFolder) {
telemetry.logEvent("clean", { all: "false"});
return this.build(folder, 'clean', undefined, false);
return this.build(folder, 'clean', undefined, undefined, false);
}

cleanAll() {
Expand Down Expand Up @@ -1437,9 +1428,9 @@ export class ExtensionManager implements vscode.Disposable {
return this.queryCMakeProject(cmakeProject => cmakeProject.tasksBuildCommand(), folder);
}

debugTarget(folder?: vscode.WorkspaceFolder, name?: string): Promise<vscode.DebugSession | null> {
debugTarget(folder?: vscode.WorkspaceFolder, name?: string, sourceDir?: string): Promise<vscode.DebugSession | null> {
telemetry.logEvent("debug", { all: "false" });
return this.runCMakeCommand(cmakeProject => cmakeProject.debugTarget(name), folder);
return this.runCMakeCommand(cmakeProject => cmakeProject.debugTarget(name), folder, undefined, undefined, sourceDir);
}

async debugTargetAll(): Promise<(vscode.DebugSession | null)[]> {
Expand All @@ -1451,9 +1442,9 @@ export class ExtensionManager implements vscode.Disposable {
return debugSessions;
}

launchTarget(folder?: vscode.WorkspaceFolder, name?: string): Promise<vscode.Terminal | null> {
launchTarget(folder?: vscode.WorkspaceFolder, name?: string, sourceDir?: string): Promise<vscode.Terminal | null> {
telemetry.logEvent("launch", { all: "false" });
return this.runCMakeCommand(cmakeProject => cmakeProject.launchTarget(name), folder);
return this.runCMakeCommand(cmakeProject => cmakeProject.launchTarget(name), folder, undefined, undefined, sourceDir);
}

async launchTargetAll(): Promise<(vscode.Terminal | null)[]> {
Expand All @@ -1465,8 +1456,8 @@ export class ExtensionManager implements vscode.Disposable {
return terminals;
}

selectLaunchTarget(folder?: vscode.WorkspaceFolder, name?: string) {
return this.runCMakeCommand(cmakeProject => cmakeProject.selectLaunchTarget(name), folder);
selectLaunchTarget(folder?: vscode.WorkspaceFolder, name?: string, sourceDir?: string) {
return this.runCMakeCommand(cmakeProject => cmakeProject.selectLaunchTarget(name), folder, undefined, undefined, sourceDir);
}

async resetState(folder?: vscode.WorkspaceFolder) {
Expand Down Expand Up @@ -1876,12 +1867,12 @@ async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle
vscode.commands.registerCommand('cmake.outline.editCacheUI', () => runCommand('editCacheUI')),
vscode.commands.registerCommand('cmake.outline.cleanRebuildAll', () => runCommand('cleanRebuildAll')),
// Commands for outline items
vscode.commands.registerCommand('cmake.outline.buildTarget', (what: TargetNode) => runCommand('build', what.folder, what.name)),
vscode.commands.registerCommand('cmake.outline.runUtilityTarget', (what: TargetNode) => runCommand('build', what.folder, what.name)),
vscode.commands.registerCommand('cmake.outline.debugTarget', (what: TargetNode) => runCommand('debugTarget', what.folder, what.name)),
vscode.commands.registerCommand('cmake.outline.launchTarget', (what: TargetNode) => runCommand('launchTarget', what.folder, what.name)),
vscode.commands.registerCommand('cmake.outline.setDefaultTarget', (what: TargetNode) => runCommand('setDefaultTarget', what.folder, what.name)),
vscode.commands.registerCommand('cmake.outline.setLaunchTarget', (what: TargetNode) => runCommand('selectLaunchTarget', what.folder, what.name)),
vscode.commands.registerCommand('cmake.outline.buildTarget', (what: TargetNode) => runCommand('build', what.folder, what.name, what.sourceDir)),
vscode.commands.registerCommand('cmake.outline.runUtilityTarget', (what: TargetNode) => runCommand('build', what.folder, what.name, what.sourceDir)),
vscode.commands.registerCommand('cmake.outline.debugTarget', (what: TargetNode) => runCommand('debugTarget', what.folder, what.name, what.sourceDir)),
vscode.commands.registerCommand('cmake.outline.launchTarget', (what: TargetNode) => runCommand('launchTarget', what.folder, what.name, what.sourceDir)),
vscode.commands.registerCommand('cmake.outline.setDefaultTarget', (what: TargetNode) => runCommand('setDefaultTarget', what.folder, what.name, what.sourceDir)),
vscode.commands.registerCommand('cmake.outline.setLaunchTarget', (what: TargetNode) => runCommand('selectLaunchTarget', what.folder, what.name, what.sourceDir)),
vscode.commands.registerCommand('cmake.outline.revealInCMakeLists', (what: TargetNode) => what.openInCMakeLists()),
vscode.commands.registerCommand('cmake.outline.compileFile', (what: SourceFileNode) => runCommand('compileFile', what.filePath)),
// vscode.commands.registerCommand('cmake.outline.selectWorkspace', (what: WorkspaceFolderNode) => runCommand('selectWorkspace', what.wsFolder))
Expand Down
69 changes: 46 additions & 23 deletions src/projectOutline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as nls from 'vscode-nls';
import * as codeModel from '@cmt/drivers/codeModel';
import rollbar from '@cmt/rollbar';
import { lexicographicalCompare, splitPath } from '@cmt/util';
import CMakeProject from '@cmt/cmakeProject';

nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
Expand Down Expand Up @@ -400,7 +401,7 @@ class ProjectNode extends BaseNode {
private readonly _rootDir = new DirectoryNode<TargetNode>(this.id, '', '');

getOrderTuple() {
return [];
return [this.sourceDirectory, this.name];
}

getChildren() {
Expand Down Expand Up @@ -450,16 +451,10 @@ class ProjectNode extends BaseNode {
}
}

interface ExternalUpdateContext {
launchTargetName: string | null;
defaultTarget?: string;
}

export class WorkspaceFolderNode extends BaseNode {
constructor(readonly wsFolder: vscode.WorkspaceFolder) {
super(`wsf/${wsFolder.uri.fsPath}`);
}
private _children: BaseNode[] = [];

private _active: boolean = false;
setActive(active: boolean) {
Expand All @@ -485,29 +480,48 @@ export class WorkspaceFolderNode extends BaseNode {
return item;
}

private _codeModel: codeModel.CodeModelContent = { configurations: [], toolchains: new Map<string, codeModel.CodeModelToolchain>() };
get codeModel() {
return this._codeModel;
private readonly _projects = new Map<string, Map<string, ProjectNode>>();

private getNode(cmakeProject: CMakeProject, modelProjectName: string) {
return this._projects.get(cmakeProject.folderPath)?.get(modelProjectName);
}

private setNode(cmakeProject: CMakeProject, modelProjectName: string, node: ProjectNode) {
let sub_map = this._projects.get(cmakeProject.folderPath);
if (!sub_map) {
sub_map = new Map<string, ProjectNode>();
this._projects.set(cmakeProject.folderPath, sub_map);
}
return sub_map.set(modelProjectName, node);
}

private removeNodes(cmakeProject: CMakeProject) {
this._projects.delete(cmakeProject.folderPath);
}
updateCodeModel(model: codeModel.CodeModelContent | null, ctx: TreeUpdateContext) {

updateCodeModel(cmakeProject: CMakeProject, model: codeModel.CodeModelContent | null, ctx: TreeUpdateContext) {
if (!model || model.configurations.length < 1) {
this._children = [];
this.removeNodes(cmakeProject);
ctx.nodesToUpdate.push(this);
return;
}
this._codeModel = model;
const config = model.configurations[0];
const new_children: BaseNode[] = [];
for (const pr of config.projects) {
const item = new ProjectNode(pr.name, ctx.folder, pr.sourceDirectory);
item.update(pr, ctx);
new_children.push(item);

for (const modelProj of model.configurations[0].projects) {
let item = this.getNode(cmakeProject, modelProj.name);
if (!item) {
item = new ProjectNode(modelProj.name, this.wsFolder, cmakeProject.folderPath);
this.setNode(cmakeProject, modelProj.name, item);
}
item.update(modelProj, ctx);
}
this._children = new_children;
}

getChildren() {
return this._children;
const children: BaseNode[] = [];
for (const sub_map of this._projects.values()) {
children.push(...sub_map.values());
}
return children.sort((a, b) => lexicographicalCompare(a.getOrderTuple(), b.getOrderTuple()));
}
}

Expand Down Expand Up @@ -536,7 +550,8 @@ export class ProjectOutline implements vscode.TreeDataProvider<BaseNode> {
this._changeEvent.fire(null);
}

updateCodeModel(folder: vscode.WorkspaceFolder, model: codeModel.CodeModelContent | null, ctx: ExternalUpdateContext) {
updateCodeModel(cmakeProject: CMakeProject, model: codeModel.CodeModelContent | null) {
const folder = cmakeProject.workspaceContext.folder;
let existing = this._folders.get(folder.uri.fsPath);
if (!existing) {
rollbar.error(localize('error.update.code.model.on.nonexist.folder', 'Updating code model on folder that has not yet been loaded.'));
Expand All @@ -546,7 +561,15 @@ export class ProjectOutline implements vscode.TreeDataProvider<BaseNode> {
}

const updates: BaseNode[] = [];
existing.updateCodeModel(model, { ...ctx, nodesToUpdate: updates, folder });
existing.updateCodeModel(
cmakeProject,
model,
{
defaultTarget: cmakeProject.defaultBuildTarget || undefined,
launchTargetName: cmakeProject.launchTargetName,
nodesToUpdate: updates,
folder
});

this._changeEvent.fire(null);
}
Expand Down

0 comments on commit 1fb304b

Please sign in to comment.