From 7aa4eaba8804f55d8b178d90fbac39aee5a09bea Mon Sep 17 00:00:00 2001 From: snehara99 <113148726+snehara99@users.noreply.github.com> Date: Thu, 1 Jun 2023 10:30:32 -0700 Subject: [PATCH] Dev/snehara/sidebar ab testing (#3167) * both sidebar and statusbar visible * toggle is working * updated description * renamed sideBar.ts to projectStatus.ts * changed name of projectOutlineProvider.ts to projectOutline.ts to match * removed unused icons * updated sidebar to use displayName whenever possible * fixed issue with build/debug/launch presets when no kit is selected * addressed some comments and fixed loc * selecting kit refreshed all nodes * fixed build error (unused variable) * simplified code calling useProjectStatusView * updated changelog * updated changelog --- CHANGELOG.md | 4 +- package.json | 309 ++++++- package.nls.json | 13 + res/dark/binary-icon.svg | 3 - res/dark/library-icon.svg | 3 - res/dark/stop-icon.svg | 3 - res/dark/utility-icon.svg | 3 - res/light/binary-icon.svg | 3 - res/light/library-icon.svg | 3 - res/light/stop-icon.svg | 3 - res/light/utility-icon.svg | 3 - src/cmakeProject.ts | 6 +- src/config.ts | 7 + src/extension.ts | 80 +- src/projectController.ts | 212 ++--- ...ctOutlineProvider.ts => projectOutline.ts} | 2 +- src/projectStatus.ts | 772 ++++++++++++++++++ test/unit-tests/config.test.ts | 1 + tsconfig.json | 1 - 19 files changed, 1246 insertions(+), 185 deletions(-) delete mode 100644 res/dark/binary-icon.svg delete mode 100644 res/dark/library-icon.svg delete mode 100644 res/dark/stop-icon.svg delete mode 100644 res/dark/utility-icon.svg delete mode 100644 res/light/binary-icon.svg delete mode 100644 res/light/library-icon.svg delete mode 100644 res/light/stop-icon.svg delete mode 100644 res/light/utility-icon.svg rename src/{projectOutlineProvider.ts => projectOutline.ts} (99%) create mode 100644 src/projectStatus.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ad1a5e7c..8eb39abd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # What's New? -## 1.15.0 +## 1.14.32 +Features: +- Add a new UI to show the project status in the side bar. This feature appears and replaces the status bar when `Cmake: Use Project Status View` is toggled `true`. This will be used for A/B testing the views. [PR #3167](https://github.com/microsoft/vscode-cmake-tools/pull/3167) Improvements: - Added ability to select either C or C++ with the Quick Start commmand. [#3183](https://github.com/microsoft/vscode-cmake-tools/pull/3183) diff --git a/package.json b/package.json index b8388d7db..d174583c9 100644 --- a/package.json +++ b/package.json @@ -88,18 +88,46 @@ "when": "cmake:enableFullFeatureSet && useCMakePresets", "category": "CMake" }, + { + "command": "cmake.projectStatus.selectConfigurePreset", + "title": "%cmake-tools.command.cmake.selectConfigurePreset.title%", + "when": "cmake:enableFullFeatureSet && useCMakePresets", + "category": "CMake", + "icon": "$(edit)" + }, { "command": "cmake.selectBuildPreset", "title": "%cmake-tools.command.cmake.selectBuildPreset.title%", "when": "cmake:enableFullFeatureSet && useCMakePresets", "category": "CMake" }, + { + "command": "cmake.projectStatus.selectBuildPreset", + "title": "%cmake-tools.command.cmake.selectBuildPreset.title%", + "when": "cmake:enableFullFeatureSet && useCMakePresets", + "category": "CMake", + "icon": "$(edit)" + }, { "command": "cmake.selectTestPreset", "title": "%cmake-tools.command.cmake.selectTestPreset.title%", "when": "cmake:enableFullFeatureSet && useCMakePresets", "category": "CMake" }, + { + "command": "cmake.projectStatus.selectTestPreset", + "title": "%cmake-tools.command.cmake.selectTestPreset.title%", + "when": "cmake:enableFullFeatureSet && useCMakePresets", + "category": "CMake", + "icon": "$(edit)" + }, + { + "command": "cmake.projectStatus.setTestTarget", + "title": "%cmake-tools.command.cmake.projectStatus.selectTestPreset.title%", + "when": "cmake:enableFullFeatureSet && useCMakePresets", + "category": "CMake", + "icon": "$(edit)" + }, { "command": "cmake.viewLog", "title": "%cmake-tools.command.cmake.viewLog.title%", @@ -118,6 +146,13 @@ "when": "cmake:enableFullFeatureSet", "category": "CMake" }, + { + "command": "cmake.projectStatus.selectActiveProject", + "title": "%cmake-tools.command.cmake.selectActiveFolder.title%", + "when": "cmake:enableFullFeatureSet", + "category": "CMake", + "icon": "$(edit)" + }, { "command": "cmake.outline.selectWorkspace", "when": "cmake:enableFullFeatureSet", @@ -148,12 +183,26 @@ "when": "cmake:enableFullFeatureSet && !useCMakePresets", "category": "CMake" }, + { + "command": "cmake.projectStatus.selectKit", + "title": "%cmake-tools.command.cmake.selectKit.title%", + "when": "cmake:enableFullFeatureSet", + "category": "CMake", + "icon": "$(edit)" + }, { "command": "cmake.setVariant", "title": "%cmake-tools.command.cmake.setVariant.title%", "when": "cmake:enableFullFeatureSet && !useCMakePresets", "category": "CMake" }, + { + "command": "cmake.projectStatus.setVariant", + "title": "%cmake-tools.command.cmake.setVariant.title%", + "when": "cmake:enableFullFeatureSet && !useCMakePresets", + "category": "CMake", + "icon": "$(edit)" + }, { "command": "cmake.setVariantAll", "title": "%cmake-tools.command.cmake.setVariantAll.title%", @@ -173,6 +222,16 @@ "light": "res/light/configure-icon.svg" } }, + { + "command": "cmake.projectStatus.configure", + "title": "%cmake-tools.command.cmake.configure.title%", + "icon": { + "dark": "res/dark/configure-icon.svg", + "light": "res/light/configure-icon.svg" + }, + "when": "cmake:enableFullFeatureSet", + "category": "CMake" + }, { "command": "cmake.showConfigureCommand", "title": "%cmake-tools.command.cmake.showConfigureCommand.title%", @@ -206,6 +265,15 @@ "light": "res/light/build-icon.svg" } }, + { + "command": "cmake.projectStatus.build", + "title": "%cmake-tools.command.cmake.build.title%", + "when": "cmake:enableFullFeatureSet", + "icon": { + "dark": "res/dark/build-icon.svg", + "light": "res/light/build-icon.svg" + } + }, { "command": "cmake.outline.buildTarget", "title": "%cmake-tools.command.cmake.build.title%", @@ -289,6 +357,13 @@ "when": "cmake:enableFullFeatureSet", "title": "%cmake-tools.command.cmake.outline.setDefaultTarget.title%" }, + { + "command": "cmake.projectStatus.setDefaultTarget", + "title": "%cmake-tools.command.cmake.setDefaultTarget.title%", + "when": "cmake:enableFullFeatureSet", + "category": "CMake", + "icon": "$(edit)" + }, { "command": "cmake.cleanConfigure", "title": "%cmake-tools.command.cmake.cleanConfigure.title%", @@ -368,6 +443,13 @@ "when": "cmake:enableFullFeatureSet", "category": "CMake" }, + { + "command": "cmake.projectStatus.ctest", + "title": "%cmake-tools.command.cmake.ctest.title%", + "when": "cmake:enableFullFeatureSet", + "icon": "$(beaker)", + "category": "CMake" + }, { "command": "cmake.ctestAll", "title": "%cmake-tools.command.cmake.ctestAll.title%", @@ -414,12 +496,25 @@ "when": "cmake:enableFullFeatureSet", "title": "%cmake-tools.command.cmake.outline.debugTarget.title%" }, + { + "command": "cmake.projectStatus.debugTarget", + "when": "cmake:enableFullFeatureSet", + "title": "%cmake-tools.command.cmake.debugTarget.title%", + "category": "CMake", + "icon": "$(debug-alt)" + }, { "command": "cmake.debugTargetAll", "title": "%cmake-tools.command.cmake.debugTargetAll.title%", "when": "cmake:enableFullFeatureSet", "category": "CMake" }, + { + "command": "cmake.projectStatus.setDebugTarget", + "when": "cmake:enableFullFeatureSet", + "title": "%cmake-tools.command.cmake.selectLaunchTarget.title%", + "icon": "$(edit)" + }, { "command": "cmake.launchTarget", "title": "%cmake-tools.command.cmake.launchTarget.title%", @@ -431,6 +526,13 @@ "when": "cmake:enableFullFeatureSet", "title": "%cmake-tools.command.cmake.outline.launchTarget.title%" }, + { + "command": "cmake.projectStatus.launchTarget", + "when": "cmake:enableFullFeatureSet", + "title": "%cmake-tools.command.cmake.outline.launchTarget.title%", + "category": "CMake", + "icon": "$(run)" + }, { "command": "cmake.launchTargetAll", "title": "%cmake-tools.command.cmake.launchTargetAll.title%", @@ -448,20 +550,30 @@ "when": "cmake:enableFullFeatureSet", "title": "%cmake-tools.command.cmake.outline.setLaunchTarget.title%" }, + { + "command": "cmake.projectStatus.setLaunchTarget", + "when": "cmake:enableFullFeatureSet", + "title": "%cmake-tools.command.cmake.outline.setLaunchTarget.title%", + "icon": "$(edit)" + }, { "command": "cmake.stop", "title": "%cmake-tools.command.cmake.stop.title%", "when": "cmake:enableFullFeatureSet", "category": "CMake" }, + { + "command": "cmake.projectStatus.stop", + "title": "%cmake-tools.command.cmake.stop.title%", + "when": "cmake:enableFullFeatureSet", + "category": "CMake", + "icon": "$(x)" + }, { "command": "cmake.outline.stop", "title": "%cmake-tools.command.cmake.stop.title%", "when": "cmake:enableFullFeatureSet", - "icon": { - "dark": "res/dark/stop-icon.svg", - "light": "res/light/stop-icon.svg" - } + "icon": "$(x)" }, { "command": "cmake.stopAll", @@ -473,10 +585,7 @@ "command": "cmake.outline.stopAll", "title": "%cmake-tools.command.cmake.stopAll.title%", "when": "cmake:enableFullFeatureSet", - "icon": { - "dark": "res/dark/stop-icon.svg", - "light": "res/light/stop-icon.svg" - } + "icon": "$(x)" }, { "command": "cmake.resetState", @@ -492,6 +601,13 @@ "command": "cmake.outline.revealInCMakeLists", "when": "cmake:enableFullFeatureSet", "title": "%cmake-tools.command.cmake.outline.revealInCMakeLists.title%" + }, + { + "command": "cmake.projectStatus.update", + "when": "cmake:enableFullFeatureSet", + "title": "%cmake-tools.command.cmake.projectStatus.update.title%", + "category": "CMake", + "icon": "$(refresh)" } ], "taskDefinitions": [ @@ -825,9 +941,89 @@ { "command": "cmake.outline.revealInCMakeLists", "when": "never" + }, + { + "command": "cmake.projectStatus.update", + "when": "never" + }, + { + "command": "cmake.projectStatus.stop", + "when": "never" + }, + { + "command": "cmake.projectStatus.selectKit", + "when": "never" + }, + { + "command": "cmake.projectStatus.configure", + "when": "never" + }, + { + "command": "cmake.projectStatus.setVariant", + "when": "never" + }, + { + "command": "cmake.projectStatus.selectConfigurePreset", + "when": "never" + }, + { + "command": "cmake.projectStatus.build", + "when": "never" + }, + { + "command": "cmake.projectStatus.setDefaultTarget", + "when": "never" + }, + { + "command": "cmake.projectStatus.selectBuildPreset", + "when": "never" + }, + { + "command": "cmake.projectStatus.ctest", + "when": "never" + }, + { + "command": "cmake.projectStatus.debugTarget", + "when": "never" + }, + { + "command": "cmake.projectStatus.launchTarget", + "when": "never" + }, + { + "command": "cmake.projectStatus.setDebugTarget", + "when": "never" + }, + { + "command": "cmake.projectStatus.setLaunchTarget", + "when": "never" + }, + { + "command": "cmake.projectStatus.selectActiveProject", + "when": "never" } ], "view/title": [ + { + "command": "cmake.projectStatus.update", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet", + "group": "navigation@1" + }, + { + "command": "cmake.projectStatus.stop", + "when": "view == cmake.projectStatus && cmake:isBuilding && cmake:enableFullFeatureSet", + "group": "navigation@2" + }, + { + "command": "cmake.projectStatus.debugTarget", + "when": "view == cmake.projectStatus && !cmake:isBuilding && !cmake:hideDebugCommand && cmake:enableFullFeatureSet", + "group": "navigation@3" + }, + { + "command": "cmake.projectStatus.launchTarget", + "when": "view == cmake.projectStatus && !cmake:isBuilding && !cmake:hideLaunchCommand && cmake:enableFullFeatureSet", + "group": "navigation@3" + }, { "command": "cmake.outline.configureAll", "when": "view == cmake.outline && !cmake:isBuilding", @@ -865,6 +1061,86 @@ } ], "view/item/context": [ + { + "command": "cmake.projectStatus.stop", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'stop'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.selectKit", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'kit'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.configure", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'configure'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.setVariant", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'variant'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.selectConfigurePreset", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'configPreset'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.build", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'build'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.setDefaultTarget", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'buildTarget'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.selectBuildPreset", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'buildPreset'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.ctest", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'test'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.setTestTarget", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'testTarget'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.selectTestPreset", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'testPreset'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.debugTarget", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'debug'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.setDebugTarget", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'debugTarget'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.launchTarget", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'launch'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.setLaunchTarget", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'launchTarget'", + "group": "inline" + }, + { + "command": "cmake.projectStatus.selectActiveProject", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'activeProject'", + "group": "inline" + }, { "command": "cmake.outline.buildTarget", "when": "view == cmake.outline && cmake:enableFullFeatureSet && viewItem =~ /canBuild=true|canRun=true/", @@ -1722,6 +1998,7 @@ "type": "string", "default": "default", "description": "%cmake-tools.configuration.cmake.statusbar.visibility.description%", + "markdownDescription": "%cmake-tools.configuration.cmake.statusbar.visibility.markdownDescription%", "enum": [ "default", "compact", @@ -1734,6 +2011,7 @@ "type": "object", "default": {}, "description": "%cmake-tools.configuration.cmake.statusbar.advanced.description%", + "markdownDescription": "%cmake-tools.configuration.cmake.statusbar.advanced.markdownDescription%", "properties": { "configurePreset": { "type": "object", @@ -1942,6 +2220,12 @@ } } }, + "cmake.useProjectStatusView": { + "type": "boolean", + "default": true, + "description": "%cmake-tools.configuration.cmake.useProjectStatusView.description%", + "scope": "window" + }, "cmake.additionalKits": { "type": "array", "default": [], @@ -2055,7 +2339,7 @@ "viewsContainers": { "activitybar": [ { - "id": "cmake__viewContainer", + "id": "cmake-view", "title": "CMake", "icon": "res/cmake-view-icon2.svg", "when": "cmake:enableFullFeatureSet" @@ -2063,7 +2347,12 @@ ] }, "views": { - "cmake__viewContainer": [ + "cmake-view": [ + { + "id": "cmake.projectStatus", + "name": "%cmake-tools.configuration.views.cmake.projectStatus.description%", + "when": "cmake:enableFullFeatureSet && config.cmake.useProjectStatusView != false" + }, { "id": "cmake.outline", "name": "%cmake-tools.configuration.views.cmake.outline.description%", diff --git a/package.nls.json b/package.nls.json index 10463c9b3..51d079623 100644 --- a/package.nls.json +++ b/package.nls.json @@ -151,10 +151,23 @@ "cmake-tools.configuration.cmake.touchbar.visibility.default.description": "Show Touch Bar buttons on supported systems.", "cmake-tools.configuration.cmake.touchbar.visibility.hidden.description": "Do not show Touch Bar buttons.", "cmake-tools.configuration.cmake.statusbar.visibility.description": "Configures how the extension displays the buttons in the status bar.", + "cmake-tools.configuration.cmake.statusbar.visibility.markdownDescription": { + "message": "Configures how the extension displays the buttons in the status bar. Only applies when `#cmake.useProjectStatusView#` is set to `false`.", + "comment": [ + "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." + ] + }, "cmake-tools.configuration.cmake.statusbar.advanced.description": "Configures the settings for individual status bar buttons. These settings overwrite the more general 'cmake.statusbar.visibility' setting.", + "cmake-tools.configuration.cmake.statusbar.advanced.markdownDescription": { + "message": "Configures the settings for individual status bar buttons. These settings overwrite the more general `#cmake.statusbar.visibility` setting. Only applies when `#cmake.useProjectStatusView#` is set to `false`.", + "comment": [ + "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." + ] + }, "cmake-tools.configuration.cmake.statusbar.advanced.visibility.description": "Configures how the extension displays this button in the status bar.", "cmake-tools.configuration.cmake.statusbar.advanced.ctest.color.description": "Enables a change in color for this button depending on test results.", "cmake-tools.configuration.cmake.statusbar.advanced.length.description": "Configures the maximum length of visible text in 'compact' mode.", + "cmake-tools.configuration.cmake.useProjectStatusView.description": "Enables the Project Status view in the CMake side panel and hides the status bar buttons.", "cmake-tools.configuration.views.cmake.folders.description": "Folders", "cmake-tools.configuration.views.cmake.projectStatus.description": "Project Status", "cmake-tools.configuration.views.cmake.outline.description": "Project Outline", diff --git a/res/dark/binary-icon.svg b/res/dark/binary-icon.svg deleted file mode 100644 index 03f7ebeb8..000000000 --- a/res/dark/binary-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/dark/library-icon.svg b/res/dark/library-icon.svg deleted file mode 100644 index 4d433d56e..000000000 --- a/res/dark/library-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/dark/stop-icon.svg b/res/dark/stop-icon.svg deleted file mode 100644 index bffa4e9da..000000000 --- a/res/dark/stop-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/dark/utility-icon.svg b/res/dark/utility-icon.svg deleted file mode 100644 index e4e6384f7..000000000 --- a/res/dark/utility-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/light/binary-icon.svg b/res/light/binary-icon.svg deleted file mode 100644 index 42e201dc0..000000000 --- a/res/light/binary-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/light/library-icon.svg b/res/light/library-icon.svg deleted file mode 100644 index 95a115b2e..000000000 --- a/res/light/library-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/light/stop-icon.svg b/res/light/stop-icon.svg deleted file mode 100644 index c84816a03..000000000 --- a/res/light/stop-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/light/utility-icon.svg b/res/light/utility-icon.svg deleted file mode 100644 index 05627b5df..000000000 --- a/res/light/utility-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts index a252debc0..e5d54db48 100644 --- a/src/cmakeProject.ts +++ b/src/cmakeProject.ts @@ -885,11 +885,9 @@ export class CMakeProject { private async init(sourceDirectory: string) { log.debug(localize('second.phase.init', 'Starting CMake Tools second-phase init')); await this.setSourceDir(await util.normalizeAndVerifySourceDir(sourceDirectory, CMakeDriver.sourceDirExpansionOptions(this.workspaceContext.folder.uri.fsPath))); - this.hideBuildButton = (this.workspaceContext.config.statusbar.advanced?.build?.visibility === "hidden") ? true : false; this.hideDebugButton = (this.workspaceContext.config.statusbar.advanced?.debug?.visibility === "hidden") ? true : false; this.hideLaunchButton = (this.workspaceContext.config.statusbar.advanced?.launch?.visibility === "hidden") ? true : false; - // Start up the variant manager await this.variantManager.initialize(this.folderName); // Set the status bar message @@ -1451,6 +1449,9 @@ export class CMakeProject { if (drv) { return drv.allTargetName; } else { + if (!this.useCMakePresets && !this.activeKit) { + return localize('targets.status', '[N/A - Select Kit]'); + } return ''; } } @@ -2761,7 +2762,6 @@ export class CMakeProject { this.hideDebugButton = (statusbar.advanced?.debug?.visibility === "hidden") ? true : false; this.hideLaunchButton = (statusbar.advanced?.launch?.visibility === "hidden") ? true : false; } - } export default CMakeProject; diff --git a/src/config.ts b/src/config.ts index 4845a170f..0b9676cf3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -139,6 +139,7 @@ export interface ExtensionConfigurationSettings { additionalKits: string[]; touchbar: TouchBarConfig; statusbar: StatusBarConfig; + useProjectStatusView: boolean; useCMakePresets: UseCMakePresets; allowCommentsInPresetsFile: boolean; allowUnsupportedPresetsVersions: boolean; @@ -446,10 +447,15 @@ export class ConfigurationReader implements vscode.Disposable { get touchbar(): TouchBarConfig { return this.configData.touchbar; } + get statusbar() { return this.configData.statusbar; } + get useProjectStatusView(): boolean { + return this.configData.useProjectStatusView; + } + get launchBehavior(): string { return this.configData.launchBehavior; } @@ -509,6 +515,7 @@ export class ConfigurationReader implements vscode.Disposable { additionalKits: new vscode.EventEmitter(), touchbar: new vscode.EventEmitter(), statusbar: new vscode.EventEmitter(), + useProjectStatusView: new vscode.EventEmitter(), useCMakePresets: new vscode.EventEmitter(), allowCommentsInPresetsFile: new vscode.EventEmitter(), allowUnsupportedPresetsVersions: new vscode.EventEmitter(), diff --git a/src/extension.ts b/src/extension.ts index 355f043f8..e35df6611 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -30,10 +30,9 @@ import rollbar from '@cmt/rollbar'; import { StateManager } from './state'; import { cmakeTaskProvider, CMakeTaskProvider } from '@cmt/cmakeTaskProvider'; import * as telemetry from '@cmt/telemetry'; -import { ProjectOutlineProvider, TargetNode, SourceFileNode, WorkspaceFolderNode } from '@cmt/projectOutlineProvider'; +import { ProjectOutline, TargetNode, SourceFileNode, WorkspaceFolderNode } from '@cmt/projectOutline'; import * as util from '@cmt/util'; -// import { ProgressHandle, DummyDisposable, reportProgress, runCommand } from '@cmt/util'; -import { ProgressHandle, DummyDisposable, reportProgress } from '@cmt/util'; +import { ProgressHandle, DummyDisposable, reportProgress, runCommand } from '@cmt/util'; import { DEFAULT_VARIANTS } from '@cmt/variant'; import { expandString, KitContextVars } from '@cmt/expand'; import paths from '@cmt/paths'; @@ -42,23 +41,20 @@ import { platform } from 'os'; import { defaultBuildPreset } from './preset'; import { CMakeToolsApiImpl } from './api'; import { DirectoryContext } from './workspace'; -// import { ProjectStatus } from './sideBar'; +import { ProjectStatus } from './projectStatus'; import { StatusBar } from '@cmt/status'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); let taskProvider: vscode.Disposable; -// export let projectStatus: ProjectStatus; +export let projectStatus: ProjectStatus; const log = logging.createLogger('extension'); const multiProjectModeKey = 'cmake:multiProject'; -// export const hideLaunchCommandKey = 'cmake:hideLaunchCommand'; -// export const hideDebugCommandKey = 'cmake:hideDebugCommand'; -// export const hideBuildCommandKey = 'cmake:hideBuildCommand'; -const hideLaunchCommandKey = 'cmake:hideLaunchCommand'; -const hideDebugCommandKey = 'cmake:hideDebugCommand'; -const hideBuildCommandKey = 'cmake:hideBuildCommand'; +export const hideLaunchCommandKey = 'cmake:hideLaunchCommand'; +export const hideDebugCommandKey = 'cmake:hideDebugCommand'; +export const hideBuildCommandKey = 'cmake:hideBuildCommand'; /** * The global extension manager. There is only one of these, even if multiple @@ -121,7 +117,7 @@ export class ExtensionManager implements vscode.Disposable { await this.initActiveProject(); } await util.setContextValue(multiProjectModeKey, this.projectController.hasMultipleProjects); - this.projectOutlineProvider.addFolder(folder); + this.projectOutline.addFolder(folder); if (this.codeModelUpdateSubs.get(folder.uri.fsPath)) { this.codeModelUpdateSubs.get(folder.uri.fsPath)?.forEach(sub => sub.dispose()); this.codeModelUpdateSubs.delete(folder.uri.fsPath); @@ -162,7 +158,7 @@ export class ExtensionManager implements vscode.Disposable { await enableFullFeatureSet(this.workspaceHasAtLeastOneProject()); } - this.projectOutlineProvider.removeFolder(folder); + this.projectOutline.removeFolder(folder); }); this.workspaceConfig.onChange('autoSelectActiveFolder', v => { @@ -171,6 +167,9 @@ export class ExtensionManager implements vscode.Disposable { } this.statusBar.setAutoSelectActiveProject(v); }); + this.workspaceConfig.onChange('useProjectStatusView', v => { + telemetry.logEvent('configChanged.useProjectStatusView', { useProjectStatusView: `${v}`}); + }); this.workspaceConfig.onChange('additionalCompilerSearchDirs', async _ => { KitsController.additionalCompilerSearchDirs = await this.getAdditionalCompilerDirs(); }); @@ -184,7 +183,7 @@ export class ExtensionManager implements vscode.Disposable { await this.projectController.loadAllProjects(); isMultiProject = this.projectController.hasMultipleProjects; await util.setContextValue(multiProjectModeKey, isMultiProject); - this.projectOutlineProvider.addAllCurrentFolders(); + this.projectOutline.addAllCurrentFolders(); if (this.workspaceConfig.autoSelectActiveFolder && isMultiProject) { this.statusBar.setAutoSelectActiveProject(true); } @@ -209,7 +208,10 @@ export class ExtensionManager implements vscode.Disposable { } public showStatusBar(fullFeatureSet: boolean) { - this.statusBar.setVisible(fullFeatureSet); + const useProjectStatusView = this.workspaceConfig.useProjectStatusView; + if (!useProjectStatusView) { + this.statusBar.setVisible(fullFeatureSet); + } } public getStatusBar(): StatusBar { @@ -240,26 +242,26 @@ export class ExtensionManager implements vscode.Disposable { private targetNameSub: vscode.Disposable = new DummyDisposable(); private buildTypeSub: vscode.Disposable = new DummyDisposable(); private launchTargetSub: vscode.Disposable = new DummyDisposable(); - // private projectSubscriptions: vscode.Disposable[] = [ - // this.targetNameSub, - // this.launchTargetSub - // ]; + private projectSubscriptions: vscode.Disposable[] = [ + this.targetNameSub, + this.launchTargetSub + ]; private ctestEnabledSub: vscode.Disposable = new DummyDisposable(); private isBusySub: vscode.Disposable = new DummyDisposable(); private activeConfigurePresetSub: vscode.Disposable = new DummyDisposable(); private activeBuildPresetSub: vscode.Disposable = new DummyDisposable(); private activeTestPresetSub: vscode.Disposable = new DummyDisposable(); - // Watch the code model so that we may update teh tree view + // Watch the code model so that we may update the tree view // private readonly codeModelUpdateSubs = new Map(); /** * The project outline tree data provider */ - private readonly projectOutlineProvider = new ProjectOutlineProvider(); + private readonly projectOutline = new ProjectOutline(); private readonly projectOutlineTreeView = vscode.window.createTreeView('cmake.outline', { - treeDataProvider: this.projectOutlineProvider, + treeDataProvider: this.projectOutline, showCollapseAll: true }); @@ -540,7 +542,7 @@ export class ExtensionManager implements vscode.Disposable { if (vscode.workspace.workspaceFolders?.length) { const selection: CMakeProject | undefined = await this.pickCMakeProject(); if (selection) { - // Ingore if user cancelled + // Ignore if user cancelled await this.setActiveProject(selection); telemetry.logEvent("selectactivefolder"); const currentActiveFolderPath = this.activeFolderPath(); @@ -569,15 +571,13 @@ export class ExtensionManager implements vscode.Disposable { // Update the active project private async updateActiveProject(workspaceFolder?: vscode.WorkspaceFolder, editor?: vscode.TextEditor): Promise { - // await this.projectController.updateActiveProject(workspaceFolder, editor); - this.projectController.updateActiveProject(workspaceFolder, editor); + await this.projectController.updateActiveProject(workspaceFolder, editor); await this.postUpdateActiveProject(); } // Update the active project from the staus bar private async setActiveProject(project: CMakeProject): Promise { - // await this.projectController.setActiveProject(project); - this.projectController.setActiveProject(project); + await this.projectController.setActiveProject(project); await this.postUpdateActiveProject(); } @@ -594,7 +594,7 @@ export class ExtensionManager implements vscode.Disposable { if (!useCMakePresets) { this.statusBar.setActiveKitName(activeProject.activeKit?.name || ''); } - this.projectOutlineProvider.setActiveFolder(activeProject.folderPath); + this.projectOutline.setActiveFolder(activeProject.folderPath); this.setupSubscriptions(); this.onActiveProjectChangedEmitter.fire(vscode.Uri.file(activeProject.folderPath)); const currentActiveFolderPath = this.activeFolderPath(); @@ -603,7 +603,7 @@ export class ExtensionManager implements vscode.Disposable { } private disposeSubs() { - // util.disposeAll(this.projectSubscriptions); + util.disposeAll(this.projectSubscriptions); for (const sub of [this.statusMessageSub, this.targetNameSub, this.buildTypeSub, this.launchTargetSub, this.ctestEnabledSub, this.isBusySub, this.activeConfigurePresetSub, this.activeBuildPresetSub, this.activeTestPresetSub]) { sub.dispose(); } @@ -614,7 +614,7 @@ export class ExtensionManager implements vscode.Disposable { return; } const folder: vscode.WorkspaceFolder = cmakeProject.workspaceFolder; - this.projectOutlineProvider.updateCodeModel( + this.projectOutline.updateCodeModel( cmakeProject.workspaceContext.folder, cmakeProject.codeModelContent, { @@ -682,7 +682,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.projectOutlineProvider.updateCodeModel( + this.projectOutline.updateCodeModel( cmakeProject.workspaceContext.folder, codeModelContent, { @@ -732,7 +732,11 @@ export class ExtensionManager implements vscode.Disposable { } else { // this.targetNameSub = cmakeProject.onTargetNameChanged(FireNow, target => this.onBuildTargetChangedEmitter.fire(target)); // this.launchTargetSub = cmakeProject.onLaunchTargetNameChanged(FireNow, target => this.onLaunchTargetChangedEmitter.fire(target || '')); - this.statusBar.setVisible(true); + if (vscode.workspace.getConfiguration('cmake').get('useProjectStatusView', true)) { + this.statusBar.setVisible(false); + } else { + this.statusBar.setVisible(true); + } this.statusMessageSub = cmakeProject.onStatusMessageChanged(FireNow, s => this.statusBar.setStatusMessage(s)); this.targetNameSub = cmakeProject.onTargetNameChanged(FireNow, t => { this.statusBar.setBuildTargetName(t); @@ -1741,11 +1745,6 @@ async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle context.subscriptions.push(vscode.commands.registerCommand('cmake.getSettingsChangePromise', () => getSettingsChangePromise())); } - // Util for the special commands to forward to real commands - function runCommand(key: keyof ExtensionManager, ...args: any[]) { - return vscode.commands.executeCommand(`cmake.${key}`, ...args); - } - context.subscriptions.push(...[ // Special commands that don't require logging or separate error handling vscode.commands.registerCommand('cmake.outline.configureAll', () => runCommand('configureAll')), @@ -1809,7 +1808,7 @@ export async function activate(context: vscode.ExtensionContext): Promise(); private readonly installPrefixSub = new Map(); private readonly useCMakePresetsSub = new Map(); - // private readonly hideDebugButtonSub = new Map(); + private readonly useProjectStatusView = new Map(); + private readonly hideDebugButtonSub = new Map(); private readonly beforeAddFolderEmitter = new vscode.EventEmitter(); private readonly afterAddFolderEmitter = new vscode.EventEmitter(); @@ -47,25 +46,25 @@ export class ProjectController implements vscode.Disposable { this.afterRemoveFolderEmitter ]; - // // Subscription on active project - // private targetNameSub: vscode.Disposable = new DummyDisposable(); - // private variantNameSub: vscode.Disposable = new DummyDisposable(); - // private launchTargetSub: vscode.Disposable = new DummyDisposable(); - // private ctestEnabledSub: vscode.Disposable = new DummyDisposable(); - // private activeConfigurePresetSub: vscode.Disposable = new DummyDisposable(); - // private activeBuildPresetSub: vscode.Disposable = new DummyDisposable(); - // private activeTestPresetSub: vscode.Disposable = new DummyDisposable(); - // private isBusySub = new DummyDisposable(); - // private projectSubscriptions: vscode.Disposable[] = [ - // this.targetNameSub, - // this.variantNameSub, - // this.launchTargetSub, - // this.ctestEnabledSub, - // this.activeConfigurePresetSub, - // this.activeBuildPresetSub, - // this.activeTestPresetSub, - // this.isBusySub - // ]; + // Subscription on active project + private targetNameSub: vscode.Disposable = new DummyDisposable(); + private variantNameSub: vscode.Disposable = new DummyDisposable(); + private launchTargetSub: vscode.Disposable = new DummyDisposable(); + private ctestEnabledSub: vscode.Disposable = new DummyDisposable(); + private activeConfigurePresetSub: vscode.Disposable = new DummyDisposable(); + private activeBuildPresetSub: vscode.Disposable = new DummyDisposable(); + private activeTestPresetSub: vscode.Disposable = new DummyDisposable(); + private isBusySub = new DummyDisposable(); + private projectSubscriptions: vscode.Disposable[] = [ + this.targetNameSub, + this.variantNameSub, + this.launchTargetSub, + this.ctestEnabledSub, + this.activeConfigurePresetSub, + this.activeBuildPresetSub, + this.activeTestPresetSub, + this.isBusySub + ]; get onBeforeAddFolder() { return this.beforeAddFolderEmitter.event; @@ -94,22 +93,19 @@ export class ProjectController implements vscode.Disposable { } private activeProject: CMakeProject | undefined; - // async updateActiveProject(workspaceFolder?: vscode.WorkspaceFolder, openEditor?: vscode.TextEditor): Promise { - updateActiveProject(workspaceFolder?: vscode.WorkspaceFolder, openEditor?: vscode.TextEditor): void { + async updateActiveProject(workspaceFolder?: vscode.WorkspaceFolder, openEditor?: vscode.TextEditor): Promise { const projects: CMakeProject[] | undefined = this.getProjectsForWorkspaceFolder(workspaceFolder); if (projects && projects.length > 0) { if (openEditor) { for (const project of projects) { if (util.isFileInsideFolder(openEditor.document.uri, project.folderPath)) { - // await this.setActiveProject(project); - this.setActiveProject(project); + await this.setActiveProject(project); break; } } if (!this.activeProject) { if (util.isFileInsideFolder(openEditor.document.uri, projects[0].workspaceFolder.uri.fsPath)) { - // await this.setActiveProject(projects[0]); - this.setActiveProject(projects[0]); + await this.setActiveProject(projects[0]); } } // If active project is found, return. @@ -118,48 +114,45 @@ export class ProjectController implements vscode.Disposable { } } else { // Set a default active project. - // await this.setActiveProject(projects[0]); - this.setActiveProject(projects[0]); + await this.setActiveProject(projects[0]); return; } } - // await this.setActiveProject(undefined); - this.setActiveProject(undefined); + await this.setActiveProject(undefined); } - // async setActiveProject(project?: CMakeProject): Promise { - setActiveProject(project?: CMakeProject): void { + async setActiveProject(project?: CMakeProject): Promise { this.activeProject = project; void this.updateUsePresetsState(this.activeProject); - // await projectStatus.updateActiveProject(project); - // await this.setupProjectSubscriptions(project); - } - - // async setupProjectSubscriptions(project?: CMakeProject): Promise { - // disposeAll(this.projectSubscriptions); - // if (!project) { - // this.targetNameSub = new DummyDisposable(); - // this.variantNameSub = new DummyDisposable(); - // this.launchTargetSub = new DummyDisposable(); - // this.ctestEnabledSub = new DummyDisposable(); - // this.activeConfigurePresetSub = new DummyDisposable(); - // this.activeBuildPresetSub = new DummyDisposable(); - // this.activeTestPresetSub = new DummyDisposable(); - // this.isBusySub = new DummyDisposable(); - // } else { - // this.targetNameSub = project.onTargetNameChanged(FireNow, () => void projectStatus.refresh()); - // this.variantNameSub = project.onActiveVariantNameChanged(FireNow, () => void projectStatus.refresh()); - // this.launchTargetSub = project.onLaunchTargetNameChanged(FireNow, () => void projectStatus.refresh()); - // this.ctestEnabledSub = project.onCTestEnabledChanged(FireNow, () => void projectStatus.refresh()); - // this.activeConfigurePresetSub = project.onActiveConfigurePresetChanged(FireNow, () => void projectStatus.refresh()); - // this.activeBuildPresetSub = project.onActiveConfigurePresetChanged(FireNow, () => void projectStatus.refresh()); - // this.activeTestPresetSub = project.onActiveTestPresetChanged(FireNow, () => void projectStatus.refresh()); - // this.isBusySub = project.onIsBusyChanged(FireNow, (isBusy) => void projectStatus.setIsBusy(isBusy)); - // await util.setContextValue(ext.hideBuildCommandKey, project.hideBuildButton); - // await util.setContextValue(ext.hideDebugCommandKey, project.hideDebugButton); - // await util.setContextValue(ext.hideLaunchCommandKey, project.hideLaunchButton); - // } - // } + await projectStatus.updateActiveProject(project); + await this.setupProjectSubscriptions(project); + } + + async setupProjectSubscriptions(project?: CMakeProject): Promise { + disposeAll(this.projectSubscriptions); + if (!project) { + this.targetNameSub = new DummyDisposable(); + this.variantNameSub = new DummyDisposable(); + this.launchTargetSub = new DummyDisposable(); + this.ctestEnabledSub = new DummyDisposable(); + this.activeConfigurePresetSub = new DummyDisposable(); + this.activeBuildPresetSub = new DummyDisposable(); + this.activeTestPresetSub = new DummyDisposable(); + this.isBusySub = new DummyDisposable(); + } else { + this.targetNameSub = project.onTargetNameChanged(FireNow, () => void projectStatus.refresh()); + this.variantNameSub = project.onActiveVariantNameChanged(FireNow, () => void projectStatus.refresh()); + this.launchTargetSub = project.onLaunchTargetNameChanged(FireNow, () => void projectStatus.refresh()); + this.ctestEnabledSub = project.onCTestEnabledChanged(FireNow, () => void projectStatus.refresh()); + this.activeConfigurePresetSub = project.onActiveConfigurePresetChanged(FireNow, () => void projectStatus.refresh()); + this.activeBuildPresetSub = project.onActiveConfigurePresetChanged(FireNow, () => void projectStatus.refresh()); + this.activeTestPresetSub = project.onActiveTestPresetChanged(FireNow, () => void projectStatus.refresh()); + this.isBusySub = project.onIsBusyChanged(FireNow, (isBusy) => void projectStatus.setIsBusy(isBusy)); + await util.setContextValue(ext.hideBuildCommandKey, project.hideBuildButton); + await util.setContextValue(ext.hideDebugCommandKey, project.hideDebugButton); + await util.setContextValue(ext.hideLaunchCommandKey, project.hideLaunchButton); + } + } public getActiveCMakeProject(): CMakeProject | undefined { return this.activeProject; @@ -211,14 +204,14 @@ export class ProjectController implements vscode.Disposable { async dispose() { disposeAll(this.subscriptions); - // disposeAll(this.projectSubscriptions); + disposeAll(this.projectSubscriptions); // Dispose of each CMakeProject we have loaded. for (const project of this.getAllCMakeProjects()) { await project.asyncDispose(); } - // if (projectStatus) { - // projectStatus.dispose(); - // } + if (projectStatus) { + projectStatus.dispose(); + } } /** @@ -346,7 +339,8 @@ export class ProjectController implements vscode.Disposable { this.buildDirectorySub.set(folder, config.onChange('buildDirectory', async () => this.refreshDriverSettings(config, folder))); this.installPrefixSub.set(folder, config.onChange('installPrefix', async () => this.refreshDriverSettings(config, folder))); this.useCMakePresetsSub.set(folder, config.onChange('useCMakePresets', async (useCMakePresets: string) => this.doUseCMakePresetsChange(folder, useCMakePresets))); - // this.hideDebugButtonSub.set(folder, config.onChange('statusbar', async (statusbar: StatusBarConfig) => this.doStatusBarChange(folder, statusbar))); + this.useProjectStatusView.set(folder, config.onChange('useProjectStatusView', async (useProjectStatusView: boolean) => this.doProjectStatusChange(useProjectStatusView))); + this.hideDebugButtonSub.set(folder, config.onChange('statusbar', async (statusbar: StatusBarConfig) => this.doStatusBarChange(folder, statusbar))); } } this.afterAddFolderEmitter.fire({ folder: folder, projects: projects }); @@ -424,8 +418,7 @@ export class ProjectController implements vscode.Disposable { for (let i = 0; i < sourceDirectories.length; i++) { const cmakeProject: CMakeProject = await CMakeProject.create(workspaceContext, sourceDirectories[i], this, sourceDirectories.length > 1); if (activeProjectPath === cmakeProject.sourceDir) { - // await this.setActiveProject(cmakeProject); - this.setActiveProject(cmakeProject); + await this.setActiveProject(cmakeProject); activeProjectPath = undefined; } @@ -435,8 +428,7 @@ export class ProjectController implements vscode.Disposable { if (activeProjectPath !== undefined) { // Active project is no longer available. Pick a different one. - // await this.setActiveProject(projects.length > 0 ? projects[0] : undefined); - this.setActiveProject(projects.length > 0 ? projects[0] : undefined); + await this.setActiveProject(projects.length > 0 ? projects[0] : undefined); } @@ -473,38 +465,48 @@ export class ProjectController implements vscode.Disposable { } } - // private async doStatusBarChange(folder: vscode.WorkspaceFolder, statusbar: StatusBarConfig): Promise { - // const projects: CMakeProject[] | undefined = this.getProjectsForWorkspaceFolder(folder); - // if (projects) { - // for (const project of projects) { - // project.doStatusBarChange(statusbar); - // } - // } - // await projectStatus.doStatusBarChange(); - // await util.setContextValue(ext.hideBuildCommandKey, (statusbar.advanced?.build?.visibility === "hidden") ? true : false); - // await util.setContextValue(ext.hideDebugCommandKey, (statusbar.advanced?.debug?.visibility === "hidden") ? true : false); - // await util.setContextValue(ext.hideLaunchCommandKey, (statusbar.advanced?.launch?.visibility === "hidden") ? true : false); - // } - - // async hideBuildButton(isHidden: boolean) { - // await projectStatus.hideBuildButton(isHidden); - // await util.setContextValue(ext.hideBuildCommandKey, isHidden); - // } - - // async hideDebugButton(isHidden: boolean) { - // await projectStatus.hideDebugButton(isHidden); - // await util.setContextValue(ext.hideDebugCommandKey, isHidden); - // } - - // async hideLaunchButton(isHidden: boolean) { - // await projectStatus.hideLaunchButton(isHidden); - // await util.setContextValue(ext.hideLaunchCommandKey, isHidden); - // } + private async doProjectStatusChange(useProjectStatusView: boolean): Promise { + const statusbar: StatusBar | undefined = getStatusBar(); + if (statusbar) { + statusbar.setVisible(!useProjectStatusView); + } + } + + private async doStatusBarChange(folder: vscode.WorkspaceFolder, statusbar: StatusBarConfig): Promise { + const projects: CMakeProject[] | undefined = this.getProjectsForWorkspaceFolder(folder); + if (projects) { + for (const project of projects) { + project.doStatusBarChange(statusbar); + } + } + await projectStatus.doStatusBarChange(); + await util.setContextValue(ext.hideBuildCommandKey, (statusbar.advanced?.build?.visibility === "hidden") ? true : false); + await util.setContextValue(ext.hideDebugCommandKey, (statusbar.advanced?.debug?.visibility === "hidden") ? true : false); + await util.setContextValue(ext.hideLaunchCommandKey, (statusbar.advanced?.launch?.visibility === "hidden") ? true : false); + } + + async hideBuildButton(isHidden: boolean) { + // Doesn't hide the button in the Side Bar because there are no space-saving issues there vs status bar + // await projectStatus.hideBuildButton(isHidden); + await util.setContextValue(ext.hideBuildCommandKey, isHidden); + } + + async hideDebugButton(isHidden: boolean) { + // Doesn't hide the button in the Side Bar because there are no space-saving issues there vs status bar + // await projectStatus.hideDebugButton(isHidden); + await util.setContextValue(ext.hideDebugCommandKey, isHidden); + } + + async hideLaunchButton(isHidden: boolean) { + // Doesn't hide the button in the Side Bar because there are no space-saving issues there vs status bar + // await projectStatus.hideLaunchButton(isHidden); + await util.setContextValue(ext.hideLaunchCommandKey, isHidden); + } private async updateUsePresetsState(project?: CMakeProject): Promise { const state: boolean = project?.useCMakePresets || false; await util.setContextValue('useCMakePresets', state); - // await projectStatus.refresh(); + await projectStatus.refresh(); const statusBar: StatusBar | undefined = getStatusBar(); if (statusBar) { statusBar.useCMakePresets(state); diff --git a/src/projectOutlineProvider.ts b/src/projectOutline.ts similarity index 99% rename from src/projectOutlineProvider.ts rename to src/projectOutline.ts index fd1b2340d..ce1d308ad 100644 --- a/src/projectOutlineProvider.ts +++ b/src/projectOutline.ts @@ -511,7 +511,7 @@ export class WorkspaceFolderNode extends BaseNode { } } -export class ProjectOutlineProvider implements vscode.TreeDataProvider { +export class ProjectOutline implements vscode.TreeDataProvider { private readonly _changeEvent = new vscode.EventEmitter(); get onDidChangeTreeData() { return this._changeEvent.event; diff --git a/src/projectStatus.ts b/src/projectStatus.ts new file mode 100644 index 000000000..714e2cc60 --- /dev/null +++ b/src/projectStatus.ts @@ -0,0 +1,772 @@ +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import CMakeProject from './cmakeProject'; +import * as preset from './preset'; +import { runCommand } from './util'; + +nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); +const localize: nls.LocalizeFunc = nls.loadMessageBundle(); +const noKitSelected = localize('no.kit.selected', '[No Kit Selected]'); +const noConfigPresetSelected = localize('no.configure.preset.selected', '[No Configure Preset Selected]'); +const noBuildPresetSelected = localize('no.build.preset.selected', '[No Build Preset Selected]'); +const noTestPresetSelected = localize('no.test.preset.selected', '[No Test Preset Selected]'); + +let treeDataProvider: TreeDataProvider; + +export class ProjectStatus { + + protected disposables: vscode.Disposable[] = []; + + constructor() { + treeDataProvider = new TreeDataProvider(); + this.disposables.push(...[ + // Commands for projectStatus items + vscode.commands.registerCommand('cmake.projectStatus.stop', async (_node: Node) => { + await runCommand('stop'); + }), + vscode.commands.registerCommand('cmake.projectStatus.selectKit', async (_node: Node) => { + await runCommand('selectKit'); + await this.refresh(); + }), + vscode.commands.registerCommand('cmake.projectStatus.selectConfigurePreset', async (node: Node) => { + await runCommand('selectConfigurePreset'); + await this.refresh(node); + }), + vscode.commands.registerCommand('cmake.projectStatus.configure', async (_node: Node) => { + void runCommand('configure'); + }), + vscode.commands.registerCommand('cmake.projectStatus.setVariant', async (node: Node) => { + await runCommand('setVariant'); + await this.refresh(node); + }), + vscode.commands.registerCommand('cmake.projectStatus.build', async (_node: Node) => { + void runCommand('build'); + }), + vscode.commands.registerCommand('cmake.projectStatus.setDefaultTarget', async (node: Node) => { + await runCommand('setDefaultTarget'); + await this.refresh(node); + }), + vscode.commands.registerCommand('cmake.projectStatus.selectBuildPreset', async (node: Node) => { + await runCommand('selectBuildPreset'); + await this.refresh(node); + }), + vscode.commands.registerCommand('cmake.projectStatus.ctest', async (_node: Node) => { + void runCommand('ctest'); + }), + vscode.commands.registerCommand('cmake.projectStatus.setTestTarget', async (_node: Node) => { + // Do nothing + }), + vscode.commands.registerCommand('cmake.projectStatus.selectTestPreset', async (node: Node) => { + await runCommand('selectTestPreset'); + await this.refresh(node); + }), + vscode.commands.registerCommand('cmake.projectStatus.debugTarget', async (_node: Node) => { + await runCommand('debugTarget'); + }), + vscode.commands.registerCommand('cmake.projectStatus.setDebugTarget', async (node: Node) => { + await runCommand('selectLaunchTarget'); + await this.refresh(node); + }), + vscode.commands.registerCommand('cmake.projectStatus.launchTarget', async (_node: Node) => { + await runCommand('launchTarget'); + }), + vscode.commands.registerCommand('cmake.projectStatus.setLaunchTarget', async (node: Node) => { + await runCommand('selectLaunchTarget'); + await this.refresh(node); + }), + vscode.commands.registerCommand('cmake.projectStatus.selectActiveProject', async () => { + await runCommand('selectActiveFolder'); + await this.refresh(); + }), + vscode.commands.registerCommand('cmake.projectStatus.update', async () => { + await this.refresh(); + }) + ]); + } + + async updateActiveProject(cmakeProject?: CMakeProject): Promise { + // Update Active Project + await treeDataProvider.updateActiveProject(cmakeProject); + } + + refresh(node?: Node): Promise { + return treeDataProvider.refresh(node); + } + + clear(): Promise { + return treeDataProvider.clear(); + } + + dispose() { + vscode.Disposable.from(...this.disposables).dispose(); + treeDataProvider.dispose(); + } + + async hideBuildButton(isHidden: boolean) { + await treeDataProvider.hideBuildButton(isHidden); + } + + async hideDebugButton(isHidden: boolean) { + await treeDataProvider.hideDebugButton(isHidden); + } + + async hideLaunchButton(isHidden: boolean) { + await treeDataProvider.hideLaunchButton(isHidden); + } + + async setIsBusy(isBusy: boolean) { + await treeDataProvider.setIsBusy(isBusy); + } + + async doStatusBarChange() { + await treeDataProvider.doStatusBarChange(); + } + +} + +class TreeDataProvider implements vscode.TreeDataProvider, vscode.Disposable { + + private treeView: vscode.TreeView; + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + private activeCMakeProject?: CMakeProject; + private isBuildButtonHidden: boolean = false; + private isDebugButtonHidden: boolean = false; + private isLaunchButtonHidden: boolean = false; + private isBusy: boolean = false; + + get onDidChangeTreeData(): vscode.Event { + return this._onDidChangeTreeData.event; + } + + constructor() { + this.treeView = vscode.window.createTreeView('cmake.projectStatus', { treeDataProvider: this }); + } + + get cmakeProject(): CMakeProject | undefined { + return this.activeCMakeProject; + } + + async updateActiveProject(cmakeProject?: CMakeProject): Promise { + // Use project to create the tree + if (cmakeProject) { + this.activeCMakeProject = cmakeProject; + // this.isBuildButtonHidden = cmakeProject.hideBuildButton; + // this.isDebugButtonHidden = cmakeProject.hideDebugButton; + // this.isLaunchButtonHidden = cmakeProject.hideLaunchButton; + // temporary to not allow status bar settings to affect side bar view + this.isBuildButtonHidden = false; + this.isDebugButtonHidden = false; + this.isLaunchButtonHidden = false; + } else { + this.isBuildButtonHidden = false; + this.isDebugButtonHidden = false; + this.isLaunchButtonHidden = false; + } + await this.refresh(); + } + + public async refresh(node?: Node): Promise { + if (node) { + await node.refresh(); + this._onDidChangeTreeData.fire(node); + } else { + this._onDidChangeTreeData.fire(undefined); + } + } + + clear(): Promise { + this.activeCMakeProject = undefined; + return this.refresh(); + } + + dispose(): void { + this.treeView.dispose(); + } + + getTreeItem(node: Node): vscode.TreeItem { + return node.getTreeItem(); + } + + async getChildren(node?: Node | undefined): Promise { + if (node) { + return node.getChildren(); + } else { + // Initializing the tree for the first time + const nodes: Node[] = []; + const projectNode = new ProjectNode(); + await projectNode.initialize(); + nodes.push(projectNode); + const configNode = new ConfigNode(); + await configNode.initialize(); + if (this.isBusy) { + configNode.convertToStopCommand(); + } + nodes.push(configNode); + if (!this.isBuildButtonHidden) { + const buildNode = new BuildNode(); + await buildNode.initialize(); + if (this.isBusy) { + buildNode.convertToStopCommand(); + } + nodes.push(buildNode); + } + const testNode = new TestNode(); + await testNode.initialize(); + nodes.push(testNode); + if (!this.isDebugButtonHidden) { + const debugNode = new DebugNode(); + await debugNode.initialize(); + nodes.push(debugNode); + } + if (!this.isLaunchButtonHidden) { + const launchNode = new LaunchNode(); + await launchNode.initialize(); + nodes.push(launchNode); + } + return nodes; + } + } + + public async doStatusBarChange() { + // temporary change to prevent status bar settings from affecting side bar + // let didChange: boolean = false; + // if (this.activeCMakeProject) { + // if (this.isBuildButtonHidden !== this.activeCMakeProject.hideBuildButton) { + // didChange = true; + // this.isBuildButtonHidden = this.activeCMakeProject.hideBuildButton; + // } + // if (this.isDebugButtonHidden !== this.activeCMakeProject.hideDebugButton) { + // didChange = true; + // this.isDebugButtonHidden = this.activeCMakeProject.hideDebugButton; + // } + // if (this.isLaunchButtonHidden !== this.activeCMakeProject.hideLaunchButton) { + // didChange = true; + // this.isLaunchButtonHidden = this.activeCMakeProject.hideLaunchButton; + // } + // if (didChange) { + // await this.refresh(); + // } + // } + } + + public async hideBuildButton(isHidden: boolean) { + if (isHidden !== this.isBuildButtonHidden) { + if (this.activeCMakeProject) { + this.activeCMakeProject.hideBuildButton = isHidden; + } + this.isBuildButtonHidden = isHidden; + await this.refresh(); + } + } + + public async hideDebugButton(isHidden: boolean): Promise { + if (isHidden !== this.isDebugButtonHidden) { + if (this.activeCMakeProject) { + this.activeCMakeProject.hideDebugButton = isHidden; + } + this.isDebugButtonHidden = isHidden; + await this.refresh(); + } + } + + public async hideLaunchButton(isHidden: boolean): Promise { + if (isHidden !== this.isLaunchButtonHidden) { + if (this.activeCMakeProject) { + this.activeCMakeProject.hideLaunchButton = isHidden; + } + this.isLaunchButtonHidden = isHidden; + await this.refresh(); + } + } + + async setIsBusy(isBusy: boolean): Promise { + if (this.isBusy !== isBusy) { + this.isBusy = isBusy; + await this.refresh(); + } + } + +} + +class Node extends vscode.TreeItem { + + constructor(label?: string | vscode.TreeItemLabel) { + super(label ? label : ""); + } + + getTreeItem(): vscode.TreeItem { + return this; + } + + getChildren(): Node[] { + return []; + } + + async initialize(): Promise { + } + + async refresh(): Promise { + } + + convertToStopCommand(): void { + } + + convertToOriginalCommand(): void { + } +} + +class ConfigNode extends Node { + + private kit?: Kit; + private variant?: Variant; + private configPreset?: ConfigPreset; + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = localize('Configure', 'Configure'); + this.tooltip = this.label; + this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + this.contextValue = "configure"; + await this.InitializeChildren(); + } + + async InitializeChildren(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + if (!treeDataProvider.cmakeProject.useCMakePresets) { + this.kit = new Kit(); + await this.kit.initialize(); + this.variant = new Variant(); + await this.variant.initialize(); + } else { + this.configPreset = new ConfigPreset(); + await this.configPreset.initialize(); + } + } + + getChildren(): Node[] { + if (!treeDataProvider.cmakeProject) { + return []; + } + if (!treeDataProvider.cmakeProject.useCMakePresets) { + return [this.kit!, this.variant!]; + } else { + return [this.configPreset!]; + } + } + + convertToStopCommand(): void { + this.label = localize("configure.running", "Configure (Running)"); + const title: string = localize('Stop', 'Stop'); + this.tooltip = title; + this.contextValue = "stop"; + } + + convertToOriginalCommand(): Promise { + return this.initialize(); + } + +} + +class BuildNode extends Node { + + private buildTarget?: BuildTarget; + private buildPreset?: BuildPreset; + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = localize('Build', 'Build'); + this.tooltip = this.label; + this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + this.contextValue = 'build'; + await this.InitializeChildren(); + } + + async InitializeChildren(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + if (!treeDataProvider.cmakeProject.useCMakePresets) { + this.buildTarget = new BuildTarget(); + await this.buildTarget.initialize(); + } else { + this.buildPreset = new BuildPreset(); + await this.buildPreset.initialize(); + } + } + + getChildren(): Node[] { + if (!treeDataProvider.cmakeProject) { + return []; + } + if (!treeDataProvider.cmakeProject.useCMakePresets) { + return [this.buildTarget!]; + } else { + return [this.buildPreset!]; + } + } + + convertToStopCommand(): void { + this.label = localize("build.running", "Build (Running)"); + const title: string = localize('Stop', 'Stop'); + this.tooltip = title; + this.contextValue = "stop"; + } + + convertToOriginalCommand(): Promise { + return this.initialize(); + } + +} + +class TestNode extends Node { + + private testTarget?: TestTarget; + private testPreset?: TestPreset; + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = localize('Test', 'Test'); + this.tooltip = this.label; + this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + this.contextValue = 'test'; + await this.InitializeChildren(); + } + + async InitializeChildren(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + if (!treeDataProvider.cmakeProject.useCMakePresets) { + this.testTarget = new TestTarget(); + await this.testTarget.initialize(); + } else { + this.testPreset = new TestPreset(); + await this.testPreset.initialize(); + } + } + + getChildren(): Node[] { + if (!treeDataProvider.cmakeProject) { + return []; + } + if (!treeDataProvider.cmakeProject.useCMakePresets) { + return [this.testTarget!]; + } else { + return [this.testPreset!]; + } + } + +} + +class DebugNode extends Node { + + private target?: DebugTarget; + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = localize('Debug', 'Debug'); + this.tooltip = this.label; + this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + this.contextValue = 'debug'; + await this.InitializeChildren(); + } + + async InitializeChildren(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.target = new DebugTarget(); + await this.target.initialize(); + } + + getChildren(): Node[] { + if (!treeDataProvider.cmakeProject) { + return []; + } + return [this.target!]; + } + +} + +class LaunchNode extends Node { + + private launchTarget?: LaunchTarget; + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = localize('Launch', 'Launch'); + this.tooltip = this.label; + this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + this.contextValue = 'launch'; + await this.InitializeChildren(); + } + + async InitializeChildren(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.launchTarget = new LaunchTarget(); + await this.launchTarget.initialize(); + } + + getChildren(): Node[] { + if (!treeDataProvider.cmakeProject) { + return []; + } + return [this.launchTarget!]; + } + +} + +class ProjectNode extends Node { + + private project?: Project; + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = localize('Project', 'Project'); + this.tooltip = localize('active.project', 'Active project'); + this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + await this.InitializeChildren(); + } + + async InitializeChildren(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.project = new Project(); + await this.project!.initialize(); + } + + getChildren(): Node[] { + if (!treeDataProvider.cmakeProject) { + return []; + } + return [this.project!]; + } + +} + +class ConfigPreset extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject || !treeDataProvider.cmakeProject.useCMakePresets) { + return; + } + this.label = (treeDataProvider.cmakeProject.configurePreset?.displayName ?? treeDataProvider.cmakeProject.configurePreset?.name) || noConfigPresetSelected; + this.tooltip = 'Change Configure Preset'; + this.contextValue = 'configPreset'; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + } + + async refresh() { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = (treeDataProvider.cmakeProject.configurePreset?.displayName ?? treeDataProvider.cmakeProject.configurePreset?.name) || noConfigPresetSelected; + } +} + +class BuildPreset extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject || !treeDataProvider.cmakeProject.useCMakePresets) { + return; + } + this.label = (treeDataProvider.cmakeProject.buildPreset?.displayName ?? treeDataProvider.cmakeProject.buildPreset?.name) || noBuildPresetSelected; + if (this.label === preset.defaultBuildPreset.name) { + this.label = preset.defaultBuildPreset.displayName; + } + this.tooltip = 'Change Build Preset'; + this.contextValue = 'buildPreset'; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + } + + async refresh() { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = (treeDataProvider.cmakeProject.buildPreset?.displayName ?? treeDataProvider.cmakeProject.buildPreset?.name) || noBuildPresetSelected; + } +} + +class TestPreset extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject || !treeDataProvider.cmakeProject.useCMakePresets) { + return; + } + this.label = (treeDataProvider.cmakeProject.testPreset?.displayName ?? treeDataProvider.cmakeProject.testPreset?.name) || noTestPresetSelected; + if (this.label === preset.defaultTestPreset.name) { + this.label = preset.defaultTestPreset.displayName; + } + this.tooltip = 'Change Test Preset'; + this.contextValue = 'testPreset'; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + } + + async refresh() { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = (treeDataProvider.cmakeProject.testPreset?.displayName ?? treeDataProvider.cmakeProject.testPreset?.name) || noTestPresetSelected; + } +} + +class Kit extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.activeKit?.name || noKitSelected; + this.tooltip = "Change Kit"; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + this.contextValue = 'kit'; + } + + getTreeItem(): vscode.TreeItem { + if (!treeDataProvider.cmakeProject) { + return this; + } + this.label = treeDataProvider.cmakeProject.activeKit?.name || noKitSelected; + return this; + } +} + +class BuildTarget extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + const title: string = localize('set.build.target', 'Set Build Target'); + this.label = await treeDataProvider.cmakeProject.buildTargetName() || await treeDataProvider.cmakeProject.allTargetName; + this.tooltip = title; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + this.contextValue = 'buildTarget'; + } + + async refresh() { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = await treeDataProvider.cmakeProject.buildTargetName() || await treeDataProvider.cmakeProject.allTargetName; + } +} + +class TestTarget extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = "[All tests]"; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + // Set the contextValue to 'testTarget' when we implement setTestTarget to choose a target for a test. + } + + async refresh() { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = "[All tests]"; + } +} + +class DebugTarget extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.launchTargetName || await treeDataProvider.cmakeProject.allTargetName; + const title: string = localize('set.debug.target', 'Set debug target'); + this.tooltip = title; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + this.contextValue = 'debugTarget'; + } + + async refresh() { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.launchTargetName || await treeDataProvider.cmakeProject.allTargetName; + } +} + +class LaunchTarget extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.launchTargetName || await treeDataProvider.cmakeProject.allTargetName; + const title: string = localize('set.launch.target', 'Set Launch Target'); + this.tooltip = title; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + this.contextValue = 'launchTarget'; + } + + async refresh() { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.launchTargetName || await treeDataProvider.cmakeProject.allTargetName; + } +} + +class Project extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.folderName; + const title: string = localize('select.active.project', 'Select active project'); + this.tooltip = title; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + this.contextValue = 'activeProject'; + } + + async refresh() { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.folderName; + } +} +class Variant extends Node { + + async initialize(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.activeVariantName || "Debug"; + this.tooltip = "Set variant"; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + this.contextValue = 'variant'; + } + + async refresh(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + this.label = treeDataProvider.cmakeProject.activeVariantName || "Debug"; + } + +} diff --git a/test/unit-tests/config.test.ts b/test/unit-tests/config.test.ts index 6983b2f13..0b0211e71 100644 --- a/test/unit-tests/config.test.ts +++ b/test/unit-tests/config.test.ts @@ -60,6 +60,7 @@ function createConfig(conf: Partial): Configurat advanced: {}, visibility: "default" }, + useProjectStatusView: true, useCMakePresets: 'never', allowCommentsInPresetsFile: false, allowUnsupportedPresetsVersions: false, diff --git a/tsconfig.json b/tsconfig.json index 066550752..2b847f4b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,5 @@ "exclude": [ "node_modules", ".vscode-test", - "src/sideBar.ts" ] } \ No newline at end of file