Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update project outline on configure #3270

Merged
merged 13 commits into from
Sep 14, 2023
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))) ??
gcampbell-msft marked this conversation as resolved.
Show resolved Hide resolved
// 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(
gcampbell-msft marked this conversation as resolved.
Show resolved Hide resolved
cmakeProject,
model,
{
defaultTarget: cmakeProject.defaultBuildTarget || undefined,
launchTargetName: cmakeProject.launchTargetName,
nodesToUpdate: updates,
folder
});

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