├── .eslintrc.json ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── .vscodeignore ├── LICENSE.md ├── README.md ├── ReleaseNotes.md ├── images ├── demo_debug.gif ├── icon.png ├── icon_black.svg ├── icon_black_empty.svg ├── icon_full.svg ├── icon_small.png ├── screenshot.png └── test_codelens_screenshot.png ├── package-lock.json ├── package.json ├── releases ├── bluebazel-0.0.1.vsix ├── bluebazel-0.0.2.vsix ├── bluebazel-0.0.3.vsix ├── bluebazel-0.0.4.vsix ├── bluebazel-0.0.5.vsix ├── bluebazel-1.0.0.vsix ├── bluebazel-1.0.1.vsix ├── bluebazel-1.0.2.vsix ├── bluebazel-1.0.3.vsix ├── bluebazel-1.0.4.vsix ├── bluebazel-1.0.5.vsix └── bluebazel-1.0.6.vsix ├── src ├── controllers │ ├── bazel-controller.ts │ ├── bazel-target-operations-controller.ts │ ├── command-controller.ts │ ├── commands │ │ ├── bazel-commands.ts │ │ ├── bazel-target-operations-commands.ts │ │ ├── debug-commands.ts │ │ ├── multi-prop-tree-item-commands.ts │ │ ├── single-prop-tree-item-commands.ts │ │ ├── tree-data-provider-commands.ts │ │ └── user-commands.ts │ ├── target-controllers │ │ ├── any-action-controller.ts │ │ ├── bazel-target-controller-manager.ts │ │ ├── bazel-target-controller.ts │ │ ├── build-controller.ts │ │ ├── debug-controller.ts │ │ ├── run-controller.ts │ │ └── test-controller.ts │ ├── user-commands-controller.ts │ └── workspace-events-controller.ts ├── extension.ts ├── languages │ ├── language-plugin.ts │ ├── language-registry.ts │ └── plugins │ │ ├── cpp-language-plugin.ts │ │ ├── go-language-plugin.ts │ │ └── python-language-plugin.ts ├── models │ ├── bazel-action-manager.ts │ ├── bazel-environment.ts │ ├── bazel-target-manager.ts │ ├── bazel-target-multi-property.ts │ ├── bazel-target-property-history.ts │ ├── bazel-target-property.ts │ ├── bazel-target-state-manager.ts │ ├── bazel-target.ts │ ├── model-accessor.ts │ ├── model.ts │ └── workspace-state-manager.ts ├── services │ ├── bazel-parser.ts │ ├── bazel-rule-language-mapping.ts │ ├── bazel-service.ts │ ├── configuration-manager.ts │ ├── configuration-utils.ts │ ├── console.ts │ ├── env-vars-utils.ts │ ├── environment-service.ts │ ├── extension-utils.ts │ ├── file-storage-service.ts │ ├── file-watcher-service.ts │ ├── icon-service.ts │ ├── network-utils.ts │ ├── shell-service.ts │ ├── string-utils.ts │ ├── task-service.ts │ └── workspace-service.ts ├── test │ ├── runTest.ts │ └── suite │ │ ├── extension.test.ts │ │ ├── index.ts │ │ └── services │ │ ├── bazel-parser.test.ts │ │ └── bazel-service.test.ts └── ui │ ├── bazel-target-quick-pick-item.ts │ ├── bazel-target-quick-pick.ts │ ├── bazel-target-tree-provider.ts │ ├── code-lens-provider-utils.ts │ ├── code-lens-providers │ └── unified-code-lens-provider.ts │ ├── progress.ts │ ├── quick-pick.ts │ └── terminal.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended" 9 | ], 10 | "overrides": [ 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaVersion": "latest", 15 | "sourceType": "module", 16 | "project": "./tsconfig.json" 17 | }, 18 | "plugins": [ 19 | "@typescript-eslint" 20 | ], 21 | "rules": { 22 | "indent": [ 23 | "error", 24 | 4 25 | ], 26 | "linebreak-style": [ 27 | "error", 28 | "unix" 29 | ], 30 | "quotes": [ 31 | "error", 32 | "single" 33 | ], 34 | "semi": [ 35 | "error", 36 | "always" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | out 3 | .vscode-test 4 | .vscode/settings.json 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Run Extension", 9 | "type": "extensionHost", 10 | "request": "launch", 11 | "runtimeExecutable": "${execPath}", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/out/**/*.js" 17 | ], 18 | "preLaunchTask": "npm: watch", 19 | "sourceMaps": true 20 | }, 21 | { 22 | "name": "Run Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 29 | ], 30 | "outFiles": ["${workspaceFolder}/out/test/**/*.js"], 31 | "sourceMaps": true 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | out/test/** 3 | tsconfig.json 4 | webpack.config.js 5 | node_modules/** 6 | src/** 7 | .git/** 8 | .vscode/** 9 | *.ts 10 | *.js.map 11 | *.log 12 | *.yml 13 | *.json 14 | coverage/** 15 | test/** 16 | releases/** 17 | out/**/*.js 18 | out/**/*.js.map 19 | !out/main.js 20 | images/icon_full.svg 21 | images/icon_black.svg 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2024 NVIDIA Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # Blue Bazel - Changelog 2 | 3 | ## 0.0.1 4 | 5 | First release of Blue Bazel VS Code extension, which provides UI integration for bazel/dazel project building, running, debugging, and testing. 6 | 7 | ## 0.0.2 8 | 9 | Fix issue with executable paths not working because run configs were not applied to the query that retrieves the target path. 10 | 11 | ## 0.0.3 12 | 13 | Stop showing output pane for background tasks. Include file based settings for projects. Show progress bar for long-running tasks. 14 | 15 | ## 0.0.4 16 | 17 | Fix bug that caused empty test_arg to appear when there were no test args. Add ability to set environment before bazel commands. 18 | 19 | ## 0.0.5 20 | 21 | Fix bug that caused user to reload if settings changed for settings that did not require that. 22 | Fix issue of running query every time test button is pushed. 23 | Added copy buttons to args and to commands. 24 | 25 | ## 1.0.0 26 | 27 | First major release. 28 | Refactor entire codebase to address separation of concerns throughtout. 29 | Add capability to add multiple types of actions (not just build, run, test) such as query. 30 | Add capability to add multiple targets to each action. 31 | Add capability to debug golang and python targets. 32 | Add autocomplete (if available) to config and bazel args for target properties. 33 | Add quickpick for all targets. 34 | 35 | ## 1.0.1 36 | 37 | Add code lens actions to some language unit tests (run test and debug test). 38 | Fix bug that caused multiple refresh targets to fire when multiple BUILD files were changed at once (usually a git operation). 39 | Add option to not refresh targets automatically when BUILD files change as it is still experimental. 40 | Add optional timeout in milliseconds to refresh targets. 41 | 42 | ## 1.0.2 43 | 44 | Change loading of available targets so if cache exists, do no load targets. 45 | Make refreshing targets on workspace open optional. 46 | Fix regex for c/c++ test code lens provider. 47 | Add plugin support for languages. 48 | Add debug direct support for python. 49 | Python debugging with bazel run_under does not work because debugpy cannot work with os.execv. This is called when py_binary builds a wrapper around the src files in the py_binary. 50 | Allow for no reload when custom buttons are created. 51 | 52 | ## 1.0.3 53 | 54 | Change the way available targets are fetched. Now they can be fetched from BUILD files so that the Bazel engine is not tied up using query. The old query is maintained via a setting. 55 | Add a dialog before running clean command. 56 | Add capability to run and debug from main functions for languages that have support. 57 | 58 | ## 1.0.4 59 | 60 | Swap typescript in for awk when available targets are fetched. Awk, albeit much faster, is not as portable as the typescript solution. 61 | Remove shellscript from C++ language support. 62 | Clear the workspace state only on major version changes. 63 | 64 | ## 1.0.5 65 | 66 | Add unit tests for the bazel parser and service. 67 | Fix query of targets to be more accurate. 68 | Fix argument passing when debugging with C/C++ and GDB. 69 | 70 | ## 1.0.6 71 | 72 | Fix gdbserver not following child processes in C/C++ applications. 73 | Add support for bzlmod files. 74 | -------------------------------------------------------------------------------- /images/demo_debug.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/images/demo_debug.gif -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/images/icon.png -------------------------------------------------------------------------------- /images/icon_black_empty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 43 | file_type_bazel 45 | 50 | 55 | 62 | 67 | 73 | 79 | 84 | 90 | 94 | 98 | 102 | 106 | 107 | 111 | 115 | 119 | 123 | 124 | 128 | 132 | 133 | 135 | 136 | 138 | file_type_bazel 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /images/icon_full.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | file_type_blue_bazel 42 | 47 | 52 | 59 | 64 | 70 | 75 | 81 | 86 | 89 | 94 | 98 | 102 | 103 | 106 | 110 | 114 | 118 | 119 | 122 | 126 | 130 | 134 | 135 | 138 | 142 | 146 | 150 | 151 | 153 | 154 | 156 | file_type_blue_bazel 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /images/icon_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/images/icon_small.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/images/screenshot.png -------------------------------------------------------------------------------- /images/test_codelens_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/images/test_codelens_screenshot.png -------------------------------------------------------------------------------- /releases/bluebazel-0.0.1.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-0.0.1.vsix -------------------------------------------------------------------------------- /releases/bluebazel-0.0.2.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-0.0.2.vsix -------------------------------------------------------------------------------- /releases/bluebazel-0.0.3.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-0.0.3.vsix -------------------------------------------------------------------------------- /releases/bluebazel-0.0.4.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-0.0.4.vsix -------------------------------------------------------------------------------- /releases/bluebazel-0.0.5.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-0.0.5.vsix -------------------------------------------------------------------------------- /releases/bluebazel-1.0.0.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-1.0.0.vsix -------------------------------------------------------------------------------- /releases/bluebazel-1.0.1.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-1.0.1.vsix -------------------------------------------------------------------------------- /releases/bluebazel-1.0.2.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-1.0.2.vsix -------------------------------------------------------------------------------- /releases/bluebazel-1.0.3.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-1.0.3.vsix -------------------------------------------------------------------------------- /releases/bluebazel-1.0.4.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-1.0.4.vsix -------------------------------------------------------------------------------- /releases/bluebazel-1.0.5.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-1.0.5.vsix -------------------------------------------------------------------------------- /releases/bluebazel-1.0.6.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/bluebazel/f7381f2ac8b26d85878109d4b0e44ce9ddfd35a0/releases/bluebazel-1.0.6.vsix -------------------------------------------------------------------------------- /src/controllers/command-controller.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelController } from './bazel-controller'; 26 | import { BazelTargetOperationsController } from './bazel-target-operations-controller'; 27 | import { registerBazelCommands } from './commands/bazel-commands'; 28 | import { registerBazelTargetOperationsCommands } from './commands/bazel-target-operations-commands'; 29 | import { registerDebugCommands } from './commands/debug-commands'; 30 | import { registerMultiPropTreeItemCommands } from './commands/multi-prop-tree-item-commands'; 31 | import { registerSinglePropTreeItemCommands } from './commands/single-prop-tree-item-commands'; 32 | import { registerTreeDataProviderCommands } from './commands/tree-data-provider-commands'; 33 | import { registerUserCommands } from './commands/user-commands'; 34 | import { BazelTargetControllerManager } from './target-controllers/bazel-target-controller-manager'; 35 | import { DebugController } from './target-controllers/debug-controller'; 36 | import { UserCommandsController } from './user-commands-controller'; 37 | import { BazelTargetManager } from '../models/bazel-target-manager'; 38 | import { ConfigurationManager } from '../services/configuration-manager'; 39 | import { BazelTargetTreeProvider } from '../ui/bazel-target-tree-provider'; 40 | import * as vscode from 'vscode'; 41 | 42 | export function registerCommands(context: vscode.ExtensionContext, 43 | configurationManager: ConfigurationManager, 44 | userCommandsController: UserCommandsController, 45 | bazelController: BazelController, 46 | bazelTargetControllerManager: BazelTargetControllerManager, 47 | bazelTargetOpsController: BazelTargetOperationsController, 48 | bazelTargetManager: BazelTargetManager, 49 | bazelTreeDataProvider: BazelTargetTreeProvider 50 | ) { 51 | registerTreeDataProviderCommands(context); 52 | registerMultiPropTreeItemCommands(context, bazelTreeDataProvider); 53 | registerSinglePropTreeItemCommands(context, bazelTreeDataProvider); 54 | registerBazelCommands(context, bazelController); 55 | registerBazelTargetOperationsCommands(context, bazelTargetOpsController, bazelTargetManager); 56 | registerDebugCommands(context, bazelTargetControllerManager.getController('debug') as DebugController, bazelTargetManager); 57 | registerUserCommands(context, configurationManager, userCommandsController); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/controllers/commands/bazel-commands.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { ExtensionUtils } from '../../services/extension-utils'; 25 | import { BazelController } from '../bazel-controller'; 26 | import * as vscode from 'vscode'; 27 | 28 | 29 | 30 | export function registerBazelCommands(context: vscode.ExtensionContext, 31 | bazelController: BazelController) { 32 | 33 | const extensionName = ExtensionUtils.getExtensionName(context); 34 | 35 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.refreshTargets`, () => { 36 | bazelController.refreshAvailableTargets(); 37 | })); 38 | 39 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.format`, () => { 40 | bazelController.format(); 41 | })); 42 | 43 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.clean`, () => { 44 | bazelController.clean(); 45 | })); 46 | 47 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.buildCurrentFile`, () => { 48 | bazelController.buildSingle(); 49 | })); 50 | 51 | } -------------------------------------------------------------------------------- /src/controllers/commands/bazel-target-operations-commands.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { BazelTarget } from '../../models/bazel-target'; 25 | import { BazelTargetManager } from '../../models/bazel-target-manager'; 26 | import { ExtensionUtils } from '../../services/extension-utils'; 27 | import { BazelTargetCategory } from '../../ui/bazel-target-tree-provider'; 28 | import { BazelTargetOperationsController } from '../bazel-target-operations-controller'; 29 | import * as vscode from 'vscode'; 30 | 31 | export function registerBazelTargetOperationsCommands( 32 | context: vscode.ExtensionContext, 33 | bazelTargetOpsController: BazelTargetOperationsController, 34 | bazelTargetManager: BazelTargetManager, 35 | ) { 36 | 37 | 38 | const extensionName = ExtensionUtils.getExtensionName(context); 39 | 40 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.addTarget`, (targetCategory: BazelTargetCategory) => { 41 | bazelTargetOpsController.pickTarget(targetCategory.action); 42 | })); 43 | 44 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.addActionAndTarget`, () => { 45 | bazelTargetOpsController.pickTarget(); 46 | })); 47 | 48 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.removeTarget`, (target: BazelTarget) => { 49 | if (target) { 50 | bazelTargetOpsController.removeTarget(target); 51 | } 52 | })); 53 | 54 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.copyTarget`, (target: BazelTarget) => { 55 | if (target) { 56 | bazelTargetOpsController.copyTarget(target); 57 | } 58 | })); 59 | 60 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.copyCommand`, (target: BazelTarget) => { 61 | if (target) { 62 | bazelTargetOpsController.copyCommandToClipboard(target); 63 | } 64 | })); 65 | 66 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.pickTarget`, (target: BazelTarget) => { 67 | bazelTargetOpsController.pickTarget(target); 68 | })); 69 | 70 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.executeTarget`, (target: BazelTarget) => { 71 | bazelTargetOpsController.executeTarget(target); 72 | }), vscode.commands.registerCommand(`${extensionName}.executingTarget`, () => { 73 | // Do nothing 74 | })); 75 | 76 | // Make build, run, and test selected target commands public for hotkey binding 77 | ['build', 'run', 'test'].forEach(action => { 78 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.${action}`, () => { 79 | const selectedTarget = bazelTargetManager.getSelectedTarget(action); 80 | if (selectedTarget) { 81 | bazelTargetOpsController.executeTarget(selectedTarget); 82 | } 83 | })); 84 | }); 85 | } 86 | -------------------------------------------------------------------------------- /src/controllers/commands/debug-commands.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { BazelTarget } from '../../models/bazel-target'; 25 | import { BazelTargetManager } from '../../models/bazel-target-manager'; 26 | import { ExtensionUtils } from '../../services/extension-utils'; 27 | import { DebugController } from '../target-controllers/debug-controller'; 28 | import * as vscode from 'vscode'; 29 | 30 | 31 | export function registerDebugCommands(context: vscode.ExtensionContext, 32 | debugController: DebugController, 33 | bazelTargetManager: BazelTargetManager) { 34 | 35 | const extensionName = ExtensionUtils.getExtensionName(context); 36 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.debugTarget`, (target: BazelTarget) => { 37 | debugController.execute(target).catch(error => { 38 | vscode.window.showErrorMessage(`${error}`); 39 | }); 40 | })); 41 | 42 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.debug`, () => { 43 | const selectedTarget = bazelTargetManager.getSelectedTarget('run'); 44 | if (selectedTarget) { 45 | debugController.execute(selectedTarget).catch(error => { 46 | vscode.window.showErrorMessage(`${error}`); 47 | }); 48 | } 49 | })); 50 | 51 | } -------------------------------------------------------------------------------- /src/controllers/commands/multi-prop-tree-item-commands.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTargetMultiProperty, BazelTargetMultiPropertyItem } from '../../models/bazel-target-multi-property'; 26 | import { ExtensionUtils } from '../../services/extension-utils'; 27 | import { BazelTargetTreeProvider } from '../../ui/bazel-target-tree-provider'; 28 | import { showSimpleQuickPick } from '../../ui/quick-pick'; 29 | import * as vscode from 'vscode'; 30 | 31 | export function registerMultiPropTreeItemCommands(context: vscode.ExtensionContext, 32 | treeDataProvider: BazelTargetTreeProvider 33 | ) { 34 | const extensionName = ExtensionUtils.getExtensionName(context); 35 | 36 | context.subscriptions.push( 37 | vscode.commands.registerCommand(`${extensionName}.addToMultiPropTreeItem`, (property: BazelTargetMultiProperty) => { 38 | 39 | const loadQuickPickData = async (cancellationToken: vscode.CancellationToken): Promise => { 40 | try { 41 | const values = await property.getAvailableValues(cancellationToken); 42 | 43 | // If property should show history, add the history data to the values 44 | if (property.shouldShowHistory()) { 45 | const historyData: string[] = property.getHistory(); 46 | values.unshift(...historyData); 47 | } 48 | return values; 49 | } catch (error) { 50 | return Promise.reject(error); 51 | } 52 | }; 53 | 54 | showSimpleQuickPick(loadQuickPickData, (data: string) => { 55 | const values = property.toStringArray(); 56 | values.push(data); 57 | property.add(data); 58 | treeDataProvider.refresh(); 59 | }, `Loading items for ${property.label.toLowerCase()}...`); 60 | }), 61 | vscode.commands.registerCommand(`${extensionName}.editMultiPropTreeItem`, (item: BazelTargetMultiPropertyItem) => { 62 | vscode.window.showInputBox({ 63 | value: item.get() 64 | }).then(data => { 65 | if (data !== undefined) { 66 | if (data.replace(/\s/g, '') === '') { 67 | item.remove(); 68 | } else { 69 | item.update(data); 70 | } 71 | } 72 | treeDataProvider.refresh(); 73 | }); 74 | }), 75 | vscode.commands.registerCommand(`${extensionName}.removeMultiPropTreeItem`, (item: BazelTargetMultiPropertyItem) => { 76 | item.remove(); 77 | treeDataProvider.refresh(); 78 | }), 79 | vscode.commands.registerCommand(`${extensionName}.copyMultiPropTreeItem`, (item: BazelTargetMultiPropertyItem) => { 80 | vscode.env.clipboard.writeText(item.get()); 81 | }) 82 | ); 83 | } -------------------------------------------------------------------------------- /src/controllers/commands/single-prop-tree-item-commands.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTargetProperty } from '../../models/bazel-target-property'; 26 | import { ExtensionUtils } from '../../services/extension-utils'; 27 | import { BazelTargetTreeProvider } from '../../ui/bazel-target-tree-provider'; 28 | import { showSimpleQuickPick } from '../../ui/quick-pick'; 29 | import * as vscode from 'vscode'; 30 | 31 | export function registerSinglePropTreeItemCommands(context: vscode.ExtensionContext, treeDataProvider: BazelTargetTreeProvider) { 32 | const extensionName = ExtensionUtils.getExtensionName(context); 33 | context.subscriptions.push( 34 | vscode.commands.registerCommand(`${extensionName}.copySinglePropTreeItem`, (property: BazelTargetProperty) => { 35 | vscode.env.clipboard.writeText(property.get()); 36 | }), 37 | vscode.commands.registerCommand(`${extensionName}.editSinglePropTreeItem`, (property: BazelTargetProperty) => { 38 | const data: string[] = property.getHistory(); 39 | showSimpleQuickPick(data, (data: string) => { 40 | property.update(data); 41 | treeDataProvider.refresh(); 42 | }); 43 | }) 44 | ); 45 | } -------------------------------------------------------------------------------- /src/controllers/commands/tree-data-provider-commands.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { ExtensionUtils } from '../../services/extension-utils'; 26 | import * as vscode from 'vscode'; 27 | 28 | export function registerTreeDataProviderCommands(context: vscode.ExtensionContext) { 29 | const extensionName = ExtensionUtils.getExtensionName(context); 30 | vscode.commands.registerCommand(`${extensionName}.collapseAll`, () => { 31 | vscode.commands.executeCommand('workbench.actions.treeView.bluebazelView.collapseAll'); 32 | }); 33 | } -------------------------------------------------------------------------------- /src/controllers/commands/user-commands.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { ConfigurationManager, UserCustomButton } from '../../services/configuration-manager'; 26 | import { Console } from '../../services/console'; 27 | import { ExtensionUtils } from '../../services/extension-utils'; 28 | import { UserCommandsController } from '../user-commands-controller'; 29 | import * as vscode from 'vscode'; 30 | 31 | export function registerCustomButtons(context: vscode.ExtensionContext, 32 | configurationManager: ConfigurationManager, 33 | userCommandsController: UserCommandsController 34 | ): void { 35 | // Traverses through the configuration and registers commands with method names. 36 | const customButtons = configurationManager.getCustomButtons(); 37 | if (customButtons === undefined) { 38 | return; 39 | } 40 | customButtons.forEach(section => { 41 | const buttons = section.buttons; 42 | if (buttons !== undefined) { 43 | buttons.forEach(button => { 44 | try { 45 | const disposableCommand = vscode.commands.registerCommand(button.methodName, async (command: string) => { 46 | await userCommandsController.runCustomTask(command); 47 | }); 48 | context.subscriptions.push(disposableCommand); 49 | } catch (error) { 50 | Console.error(error); 51 | } 52 | }); 53 | } 54 | }); 55 | } 56 | 57 | export function registerUserCommands(context: vscode.ExtensionContext, 58 | configurationManager: ConfigurationManager, 59 | userCommandsController: UserCommandsController 60 | ): void { 61 | registerCustomButtons(context, configurationManager, userCommandsController); 62 | 63 | const extensionName = ExtensionUtils.getExtensionName(context); 64 | context.subscriptions.push(vscode.commands.registerCommand(`${extensionName}.customButton`, (button: UserCustomButton) => { 65 | const registeredCommand = button.methodName; 66 | vscode.commands.executeCommand(registeredCommand, button.command); 67 | })); 68 | 69 | } -------------------------------------------------------------------------------- /src/controllers/target-controllers/any-action-controller.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTargetController } from './bazel-target-controller'; 26 | import { BazelAction, BazelTarget } from '../../models/bazel-target'; 27 | import { BazelTargetPropertyHistory } from '../../models/bazel-target-property-history'; 28 | import { BazelTargetState, BazelTargetStateManager } from '../../models/bazel-target-state-manager'; 29 | import { ConfigurationManager } from '../../services/configuration-manager'; 30 | import { capitalizeFirstLetter, cleanAndFormat } from '../../services/string-utils'; 31 | import { TaskService } from '../../services/task-service'; 32 | import { showProgress } from '../../ui/progress'; 33 | import * as vscode from 'vscode'; 34 | 35 | 36 | export class AnyActionController implements BazelTargetController { 37 | private readonly quickPickHistory: Map; 38 | constructor(private readonly context: vscode.ExtensionContext, 39 | private readonly configurationManager: ConfigurationManager, 40 | private readonly taskService: TaskService, 41 | private readonly bazelTargetStateManager: BazelTargetStateManager 42 | ) { 43 | this.quickPickHistory = new Map(); 44 | } 45 | 46 | public async execute(target: BazelTarget): Promise { 47 | try { 48 | this.bazelTargetStateManager.setTargetState(target, BazelTargetState.Executing); 49 | const executable = this.configurationManager.getExecutableCommand(); 50 | 51 | await showProgress(`${capitalizeFirstLetter(target.action)}ing ${target.bazelPath}`, (cancellationToken) => { 52 | return this.taskService.runTask( 53 | `${target.action} ${target.bazelPath}`, // task name 54 | `${executable} ${target.action} ${target.bazelPath}`, 55 | this.configurationManager.isClearTerminalBeforeAction(), 56 | cancellationToken, 57 | target.id 58 | ); 59 | }); 60 | } catch (error) { 61 | return Promise.reject(error); 62 | } finally { 63 | this.bazelTargetStateManager.setTargetState(target, BazelTargetState.Idle); 64 | } 65 | } 66 | 67 | public async getExecuteCommand(target: BazelTarget): Promise { 68 | const executable = this.configurationManager.getExecutableCommand(); 69 | const args = target.getBazelArgs(); 70 | const configArgs = target.getConfigArgs(); 71 | const envVars = target.getEnvVars(); 72 | const command = cleanAndFormat( 73 | executable, 74 | target.action, 75 | args.toString(), 76 | configArgs.toString(), 77 | target.buildPath, 78 | envVars.toString() 79 | ); 80 | 81 | return `${command}\n`; 82 | } 83 | } -------------------------------------------------------------------------------- /src/controllers/target-controllers/bazel-target-controller-manager.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { AnyActionController } from './any-action-controller'; 26 | import { BazelTargetController } from './bazel-target-controller'; 27 | import { BuildController } from './build-controller'; 28 | import { DebugController } from './debug-controller'; 29 | import { RunController } from './run-controller'; 30 | import { TestController } from './test-controller'; 31 | import { BazelEnvironment } from '../../models/bazel-environment'; 32 | import { BazelTargetManager } from '../../models/bazel-target-manager'; 33 | import { BazelTargetStateManager } from '../../models/bazel-target-state-manager'; 34 | import { BazelService } from '../../services/bazel-service'; 35 | import { ConfigurationManager } from '../../services/configuration-manager'; 36 | import { ShellService } from '../../services/shell-service'; 37 | import { TaskService } from '../../services/task-service'; 38 | import { BazelController } from '../bazel-controller'; 39 | import * as vscode from 'vscode'; 40 | 41 | 42 | export class BazelTargetControllerManager { 43 | private controllers: Map = new Map(); 44 | 45 | constructor(context: vscode.ExtensionContext, 46 | configurationManager: ConfigurationManager, 47 | taskService: TaskService, 48 | shellService: ShellService, 49 | bazelService: BazelService, 50 | bazelController: BazelController, 51 | bazelEnvironment: BazelEnvironment, 52 | bazelTargetManager: BazelTargetManager, 53 | bazelTargetStateManager: BazelTargetStateManager 54 | ) { 55 | const buildController = new BuildController(context, 56 | configurationManager, 57 | taskService, 58 | bazelTargetManager, 59 | bazelTargetStateManager); 60 | 61 | const runController = new RunController(context, 62 | configurationManager, 63 | taskService, 64 | bazelService, 65 | buildController, 66 | bazelTargetStateManager); 67 | 68 | const testController = new TestController(context, 69 | configurationManager, 70 | taskService, 71 | bazelTargetStateManager); 72 | 73 | this.controllers.set('build', buildController); 74 | this.controllers.set('run', runController); 75 | 76 | this.controllers.set('test', testController); 77 | 78 | this.controllers.set('debug', new DebugController(context, 79 | configurationManager, 80 | taskService, 81 | bazelService, 82 | buildController, 83 | bazelTargetStateManager)); 84 | 85 | // Coverage operates very similar to test, so use test controller 86 | this.controllers.set('coverage', testController); 87 | 88 | this.controllers.set('*', new AnyActionController(context, 89 | configurationManager, 90 | taskService, 91 | bazelTargetStateManager)); 92 | } 93 | 94 | public getController(action: string): BazelTargetController | undefined { 95 | if (this.controllers.has(action)) 96 | return this.controllers.get(action); 97 | return this.controllers.get('*'); 98 | } 99 | } -------------------------------------------------------------------------------- /src/controllers/target-controllers/bazel-target-controller.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTarget } from '../../models/bazel-target'; 26 | 27 | export interface BazelTargetController { 28 | execute(target: BazelTarget): Promise; 29 | getExecuteCommand(target: BazelTarget): Promise; 30 | } -------------------------------------------------------------------------------- /src/controllers/target-controllers/build-controller.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTargetController } from './bazel-target-controller'; 26 | import { BazelTarget } from '../../models/bazel-target'; 27 | import { BazelTargetManager } from '../../models/bazel-target-manager'; 28 | import { BazelTargetState, BazelTargetStateManager } from '../../models/bazel-target-state-manager'; 29 | import { BAZEL_BIN } from '../../services/bazel-parser'; 30 | import { ConfigurationManager } from '../../services/configuration-manager'; 31 | import { EnvVarsUtils } from '../../services/env-vars-utils'; 32 | import { cleanAndFormat } from '../../services/string-utils'; 33 | import { TaskService } from '../../services/task-service'; 34 | import { showProgress } from '../../ui/progress'; 35 | import * as path from 'path'; 36 | import * as vscode from 'vscode'; 37 | 38 | 39 | 40 | export const BUILD_RUN_TARGET_STR = ''; 41 | 42 | export class BuildController implements BazelTargetController { 43 | constructor(private readonly context: vscode.ExtensionContext, 44 | private readonly configurationManager: ConfigurationManager, 45 | private readonly taskService: TaskService, 46 | private readonly bazelTargetManager: BazelTargetManager, 47 | private readonly bazelTargetStateManager: BazelTargetStateManager 48 | ) { } 49 | 50 | public async execute(target: BazelTarget): Promise { 51 | const actualTarget = this.getActualBuildTargetPath(target); 52 | if (!actualTarget) { 53 | vscode.window.showErrorMessage('Build failed. Could not find run target.'); 54 | return; 55 | } 56 | 57 | const buildCommand = await this.getExecuteCommand(target); 58 | if (!buildCommand) { 59 | vscode.window.showErrorMessage('Build failed. Could not find run target.'); 60 | return; 61 | } 62 | 63 | // Both the run and debug controllers can call build, 64 | // we don't want to change the state if it isn't idle. 65 | const shouldChangeState = this.bazelTargetStateManager.getTargetState(target) === BazelTargetState.Idle; 66 | try { 67 | if (shouldChangeState) { 68 | this.bazelTargetStateManager.setTargetState(target, BazelTargetState.Executing); 69 | } 70 | await showProgress(`Building ${actualTarget}`, (cancellationToken) => { 71 | return this.taskService.runTask(`${target.action} ${actualTarget}`, 72 | buildCommand, this.configurationManager.isClearTerminalBeforeAction(), cancellationToken, target.id); 73 | }); 74 | } catch (error) { 75 | return Promise.reject(error); 76 | } finally { 77 | if (shouldChangeState) { 78 | this.bazelTargetStateManager.setTargetState(target, BazelTargetState.Idle); 79 | } 80 | } 81 | } 82 | 83 | public async getExecuteCommand(target: BazelTarget): Promise { 84 | const actualTarget = this.getActualBuildTargetPath(target); 85 | if (!actualTarget) { 86 | return undefined; 87 | } 88 | 89 | const executable = this.configurationManager.getExecutableCommand(); 90 | const buildArgs = target.getBazelArgs().toString(); 91 | const configArgs = target.getConfigArgs().toString(); 92 | const buildEnvVars = EnvVarsUtils.toBuildEnvVars(target.getEnvVars().toStringArray()); 93 | // Clean and format the command by removing extra spaces and empty strings 94 | const command = cleanAndFormat( 95 | executable, 96 | 'build', 97 | buildArgs, 98 | configArgs, 99 | actualTarget, 100 | buildEnvVars 101 | ); 102 | 103 | return `${command}\n`; 104 | } 105 | 106 | private getActualBuildTargetPath(target: BazelTarget): string | undefined { 107 | let actualTarget = target.bazelPath; 108 | if (target.buildPath === BUILD_RUN_TARGET_STR) { 109 | // Find run target 110 | const runTarget = this.bazelTargetManager.getSelectedTarget('run'); 111 | if (runTarget !== undefined && 112 | typeof runTarget === 'object' && 113 | runTarget !== null && 114 | Object.keys(runTarget).includes('detail')) { 115 | actualTarget = path.relative(BAZEL_BIN, runTarget.buildPath); 116 | } else { 117 | return undefined; 118 | } 119 | } 120 | return actualTarget; 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /src/controllers/target-controllers/test-controller.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTargetController } from './bazel-target-controller'; 26 | import { BazelTarget } from '../../models/bazel-target'; 27 | import { BazelTargetState, BazelTargetStateManager } from '../../models/bazel-target-state-manager'; 28 | import { ConfigurationManager } from '../../services/configuration-manager'; 29 | import { EnvVarsUtils } from '../../services/env-vars-utils'; 30 | import { capitalizeFirstLetter, cleanAndFormat } from '../../services/string-utils'; 31 | import { TaskService } from '../../services/task-service'; 32 | import { showProgress } from '../../ui/progress'; 33 | import * as vscode from 'vscode'; 34 | 35 | 36 | export class TestController implements BazelTargetController { 37 | constructor(private readonly context: vscode.ExtensionContext, 38 | private readonly configurationManager: ConfigurationManager, 39 | private readonly taskService: TaskService, 40 | private readonly bazelTargetStateManager: BazelTargetStateManager 41 | ) { } 42 | 43 | public async execute(target: BazelTarget) { 44 | const testCommand = await this.getExecuteCommand(target); 45 | if (!testCommand) { 46 | vscode.window.showErrorMessage('Test failed. Could not get test target.'); 47 | return; 48 | } 49 | 50 | const taskLabel = `${target.action} ${target.bazelPath}`; 51 | 52 | return showProgress(`${capitalizeFirstLetter(target.action)}ing ${target.bazelPath}`, async (cancellationToken) => { 53 | try { 54 | this.bazelTargetStateManager.setTargetState(target, BazelTargetState.Executing); 55 | await this.taskService.runTask(taskLabel, testCommand, 56 | this.configurationManager.isClearTerminalBeforeAction(), 57 | cancellationToken, target.id); 58 | } catch (error) { 59 | return Promise.reject(error); 60 | } finally { 61 | this.bazelTargetStateManager.setTargetState(target, BazelTargetState.Idle); 62 | } 63 | }); 64 | } 65 | 66 | public async getExecuteCommand(target: BazelTarget): Promise { 67 | 68 | if (!target) { 69 | return undefined; 70 | } 71 | const executable = this.configurationManager.getExecutableCommand(); 72 | const bazelArgs = target.getBazelArgs(); 73 | const configArgs = target.getConfigArgs(); 74 | const envVars = EnvVarsUtils.toTestEnvVars(target.getEnvVars().toStringArray()); 75 | const testArgs = target.getRunArgs(); 76 | 77 | const bazelTarget = target.bazelPath; 78 | const command = cleanAndFormat( 79 | executable, 80 | 'test', 81 | bazelArgs.toString(), 82 | configArgs.toString(), 83 | envVars, 84 | bazelTarget, 85 | testArgs.toString() 86 | ); 87 | 88 | return `${command}\n`; 89 | } 90 | } -------------------------------------------------------------------------------- /src/controllers/workspace-events-controller.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { registerCustomButtons } from './commands/user-commands'; 25 | import { UserCommandsController } from './user-commands-controller'; 26 | import { ConfigurationManager } from '../services/configuration-manager'; 27 | import { ExtensionUtils } from '../services/extension-utils'; 28 | import { BazelTargetTreeProvider } from '../ui/bazel-target-tree-provider'; 29 | import * as vscode from 'vscode'; 30 | 31 | export class WorkspaceEventsController { 32 | constructor(private context: vscode.ExtensionContext, 33 | private readonly configurationManager: ConfigurationManager, 34 | private readonly userCommandsController: UserCommandsController, 35 | private readonly bazelTree: BazelTargetTreeProvider) { 36 | this.registerConfigurationChangeHandler(); 37 | } 38 | 39 | private registerConfigurationChangeHandler() { 40 | vscode.workspace.onDidChangeConfiguration((e) => { 41 | if (e.affectsConfiguration(ExtensionUtils.getExtensionName(this.context))) { 42 | registerCustomButtons(this.context, this.configurationManager, this.userCommandsController); 43 | this.bazelTree.refresh(); 44 | } 45 | }); 46 | } 47 | } -------------------------------------------------------------------------------- /src/languages/language-plugin.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { BazelTarget } from '../models/bazel-target'; 25 | import * as vscode from 'vscode'; 26 | 27 | 28 | export interface LanguagePlugin { 29 | supportedLanguages: string[]; 30 | 31 | getDebugRunUnderCommand(port: number): string; 32 | 33 | getDebugEnvVars(target: BazelTarget): string[] 34 | 35 | createDebugDirectLaunchConfig(target: BazelTarget, cancellationToken?: vscode.CancellationToken): Promise; 36 | 37 | createDebugAttachConfig(target: BazelTarget, port: number, cancellationToken?: vscode.CancellationToken): Promise; 38 | 39 | getCodeLensTestRegex(): RegExp; 40 | 41 | getCodeLensRunRegex(): RegExp; 42 | 43 | } -------------------------------------------------------------------------------- /src/languages/language-registry.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { LanguagePlugin } from './language-plugin'; 25 | import { CppLanguagePlugin } from './plugins/cpp-language-plugin'; 26 | import { GoLanguagePlugin } from './plugins/go-language-plugin'; 27 | import { PythonLanguagePlugin } from './plugins/python-language-plugin'; 28 | import { BazelEnvironment } from '../models/bazel-environment'; 29 | import { BazelService } from '../services/bazel-service'; 30 | import * as vscode from 'vscode'; 31 | 32 | 33 | export class LanguageRegistry { 34 | private static plugins: { [language: string]: LanguagePlugin } = {}; 35 | 36 | static registerPlugin(plugin: LanguagePlugin) { 37 | for (const language of plugin.supportedLanguages) { 38 | if (this.plugins[language]) { 39 | throw new Error(`Language plugin for '${language}' is already registered.`); 40 | } 41 | this.plugins[language] = plugin; 42 | } 43 | } 44 | 45 | static getPlugin(language: string | undefined): LanguagePlugin { 46 | if (!language || !this.plugins[language]) { 47 | throw new Error(`No language support for ${language}`); 48 | } 49 | return this.plugins[language]; 50 | } 51 | 52 | static getLanguages(): string[] { 53 | return Object.keys(this.plugins); 54 | } 55 | } 56 | 57 | export function registerLanguages(context: vscode.ExtensionContext, 58 | bazelService: BazelService, 59 | bazelEnvironment: BazelEnvironment) { 60 | 61 | LanguageRegistry.registerPlugin(new CppLanguagePlugin(context, bazelService, bazelEnvironment.getEnvVars())); 62 | LanguageRegistry.registerPlugin(new GoLanguagePlugin(context, bazelService, bazelEnvironment.getEnvVars())); 63 | LanguageRegistry.registerPlugin(new PythonLanguagePlugin(context, bazelService, bazelEnvironment.getEnvVars())); 64 | } 65 | -------------------------------------------------------------------------------- /src/languages/plugins/go-language-plugin.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { BazelTarget } from '../../models/bazel-target'; 25 | import { BazelService } from '../../services/bazel-service'; 26 | import { EnvVarsUtils } from '../../services/env-vars-utils'; 27 | import { isRemoteSession } from '../../services/network-utils'; 28 | import { LanguagePlugin } from '../language-plugin'; 29 | import * as path from 'path'; 30 | import * as vscode from 'vscode'; 31 | 32 | 33 | export class GoLanguagePlugin implements LanguagePlugin { 34 | public readonly supportedLanguages: string[]; 35 | 36 | constructor(private readonly context: vscode.ExtensionContext, 37 | private readonly bazelService: BazelService, 38 | private readonly setupEnvVars: string[] 39 | ) { 40 | this.supportedLanguages = ['go']; 41 | } 42 | 43 | public getDebugRunUnderCommand(port: number): string { 44 | return `dlv exec --headless --listen=:${port} --api-version=2`; 45 | } 46 | 47 | public getDebugEnvVars(target: BazelTarget): string[] { 48 | // This is necessary because bazel tests in go will call bzltestutils.Wrap and 49 | // spawn a child process which dlv is not connected to. Turn it off. 50 | return target.ruleType.includes('test') ? ['GO_TEST_WRAP=0'] : []; 51 | } 52 | 53 | public async createDebugDirectLaunchConfig(target: BazelTarget, _cancellationToken?: vscode.CancellationToken): Promise { 54 | const workingDirectory = '${workspaceFolder}'; 55 | const targetPath = target.buildPath;//await this.bazelService.getBazelTargetBuildPath(target, cancellationToken); 56 | const programPath = path.join(workingDirectory, targetPath); 57 | const envVars = EnvVarsUtils.listToObject(target.getEnvVars().toStringArray()); 58 | const args = target.getRunArgs().toString(); 59 | 60 | return { 61 | name: `${targetPath} (Direct)`, 62 | type: 'go', 63 | request: 'launch', 64 | mode: 'exec', 65 | program: programPath, 66 | args: args.length > 0 ? args.split(' ') : [], 67 | stopOnEntry: false, 68 | cwd: workingDirectory, 69 | env: {...EnvVarsUtils.listToObject(this.setupEnvVars), ...envVars}, 70 | console: 'integratedTerminal', 71 | substitutePath: [ 72 | { 73 | from: '${workspaceFolder}', 74 | to: '' 75 | } 76 | ] 77 | }; 78 | } 79 | 80 | public async createDebugAttachConfig(target: BazelTarget, 81 | port: number, 82 | _cancellationToken?: vscode.CancellationToken): Promise { 83 | const debugConfig = { 84 | name: `${target.label} (Attach)`, 85 | type: 'go', 86 | request: 'attach', 87 | mode: 'remote', 88 | host: '127.0.0.1', 89 | port: port, // Port where dlv is listening 90 | cwd: '${workspaceFolder}', 91 | trace: 'verbose', // Enable verbose logging for debugging 92 | showLog: true, 93 | } as vscode.DebugConfiguration; 94 | 95 | if (isRemoteSession()) { 96 | // Paths don't match up for vscode over ssh 97 | debugConfig.substitutePath = [ // This is necessary for test breakpoints to work 98 | { 99 | from: '${workspaceFolder}', 100 | to: '' 101 | } 102 | ]; 103 | } 104 | return debugConfig; 105 | } 106 | 107 | /** 108 | * Regex to match test functions in Go. 109 | * Example matches: 110 | * func TestFunctionName(t *testing.T) { 111 | * func TestAnotherFunction(t *testing.T) { 112 | */ 113 | public getCodeLensTestRegex(): RegExp { 114 | return /^func\s+(Test\w+)\(\w+\s+\*testing\.T\)/gm; 115 | } 116 | 117 | /** 118 | * Regex to match main function definitions in Go. 119 | * Example matches: 120 | * func main() { 121 | */ 122 | public getCodeLensRunRegex(): RegExp { 123 | return /^func\s+(main)\s*\(\s*\)\s*\{/gm; 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /src/languages/plugins/python-language-plugin.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { BazelTarget } from '../../models/bazel-target'; 25 | import { BazelService } from '../../services/bazel-service'; 26 | import { Console } from '../../services/console'; 27 | import { EnvVarsUtils } from '../../services/env-vars-utils'; 28 | import { LanguagePlugin } from '../language-plugin'; 29 | import * as fs from 'fs'; 30 | import * as path from 'path'; 31 | import * as vscode from 'vscode'; 32 | 33 | 34 | 35 | export class PythonLanguagePlugin implements LanguagePlugin { 36 | public readonly supportedLanguages: string[]; 37 | 38 | constructor(private readonly context: vscode.ExtensionContext, 39 | private readonly bazelService: BazelService, 40 | private readonly setupEnvVars: string[] 41 | ) { 42 | this.supportedLanguages = ['python']; 43 | } 44 | 45 | public getDebugRunUnderCommand(_port: number): string { 46 | throw new Error('Python debugpy does not support bazel debugging (try debugging directly)'); 47 | } 48 | 49 | public getDebugEnvVars(_target: BazelTarget): string[] { 50 | return []; 51 | } 52 | 53 | public async createDebugDirectLaunchConfig( 54 | target: BazelTarget, 55 | cancellationToken?: vscode.CancellationToken 56 | ): Promise { 57 | let pythonFile = ''; 58 | try { 59 | const runfiles = await this.bazelService.getRunfilesLocation(target, cancellationToken); 60 | pythonFile = this.findMainInRunfiles(runfiles); 61 | } catch (error) { 62 | return Promise.reject(error); 63 | } 64 | 65 | const args = target.getRunArgs().toString(); 66 | const envVars = EnvVarsUtils.listToObject(target.getEnvVars().toStringArray()); 67 | 68 | return { 69 | name: `${target.label} (Debug Python)`, 70 | type: 'python', 71 | request: 'launch', 72 | program: pythonFile, // Bazel-compiled runfile 73 | args: args.length > 0 ? args.split(' ') : [], 74 | pathMappings: [ 75 | { 76 | localRoot: '${workspaceFolder}', // Directory containing the local source 77 | remoteRoot: '.' // Remote Bazel runfiles directory 78 | } 79 | ], 80 | stopOnEntry: false, 81 | cwd: '${workspaceFolder}', 82 | env: { ...EnvVarsUtils.listToObject(this.setupEnvVars), ...envVars }, 83 | console: 'integratedTerminal', 84 | justMyCode: true 85 | } as vscode.DebugConfiguration; 86 | } 87 | 88 | public async createDebugAttachConfig(target: BazelTarget, 89 | port: number, 90 | _cancellationToken?: vscode.CancellationToken): Promise { 91 | return { 92 | name: `${target.label} (Attach)`, 93 | type: 'debugpy', 94 | request: 'attach', 95 | connect: { 96 | host: 'localhost', 97 | port: port // Port where debugpy is listening 98 | }, 99 | pathMappings: [ 100 | { 101 | localRoot: '${workspaceFolder}', 102 | remoteRoot: '.' 103 | } 104 | ], 105 | }; 106 | } 107 | 108 | /** 109 | * Regex to match test functions in Python. 110 | * Example matches: 111 | * def test_function_name(): 112 | * def test_another_function(param): 113 | */ 114 | public getCodeLensTestRegex(): RegExp { 115 | return /(?:^|\s)def\s+(test_\w+)\(/gm; 116 | } 117 | 118 | /** 119 | * Regex to match main function definitions in Python. 120 | * Example matches: 121 | * def main(): 122 | * def main(arg1, arg2): 123 | */ 124 | public getCodeLensRunRegex(): RegExp { 125 | return /(?:^|\s)def\s+(main)\s*\(\s*\)/gm; 126 | } 127 | 128 | private findMainInRunfiles(runfilesPath: string): string { 129 | const files = fs.readdirSync(runfilesPath); 130 | 131 | for (const file of files) { 132 | const fullPath = path.join(runfilesPath, file); 133 | const stat = fs.statSync(fullPath); 134 | if (stat.isDirectory()) { 135 | try { 136 | // Recursively search subdirectories 137 | const mainFile = this.findMainInRunfiles(fullPath); 138 | if (mainFile) { 139 | return mainFile; 140 | } 141 | } catch (error) { 142 | // Continue searching other branches 143 | } 144 | } else if (file.endsWith('.py')) { 145 | // Check if the file contains `if __name__ == "__main__"` 146 | const content = fs.readFileSync(fullPath, 'utf-8'); 147 | if (content.includes('if __name__ == "__main__"')) { 148 | Console.log(`Main function found in: ${fullPath}`); 149 | return fullPath; 150 | } 151 | } 152 | } 153 | 154 | // If no match found in this branch, throw an error 155 | throw new Error('No Python file with \'if __name__ == "__main__"\' found'); 156 | } 157 | 158 | 159 | } -------------------------------------------------------------------------------- /src/models/bazel-action-manager.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { BazelService } from '../services/bazel-service'; 25 | import * as vscode from 'vscode'; 26 | 27 | /** 28 | * Model for retrieving all possible commands in bazel. 29 | */ 30 | export class BazelActionManager { 31 | private actions: string[] = []; 32 | private actionsPromise: Promise; 33 | 34 | constructor(private context: vscode.ExtensionContext, 35 | private readonly bazelService: BazelService, 36 | ) { 37 | this.actionsPromise = this.loadActions(); 38 | } 39 | 40 | private async loadActions(): Promise { 41 | this.actions = await this.bazelService.fetchTargetActions(); 42 | return this.actions; 43 | } 44 | 45 | public async getActions(): Promise { 46 | return this.actionsPromise; 47 | } 48 | 49 | public async refreshActions() { 50 | this.actions = await this.bazelService.fetchTargetActions(); 51 | this.actionsPromise = Promise.resolve(this.actions); 52 | } 53 | } -------------------------------------------------------------------------------- /src/models/bazel-environment.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { ConfigurationManager } from '../services/configuration-manager'; 26 | import { EnvironmentService } from '../services/environment-service'; 27 | import * as vscode from 'vscode'; 28 | export class BazelEnvironment { 29 | private envVars: string[] = []; 30 | 31 | public static async create(context: vscode.ExtensionContext, 32 | configurationManager: ConfigurationManager, 33 | cancellationToken?: vscode.CancellationToken 34 | ): Promise { 35 | const instance = new BazelEnvironment(); 36 | try { 37 | instance.envVars = await this.loadEnvVars(context, configurationManager, cancellationToken); 38 | return instance; 39 | } catch (error) { 40 | return Promise.reject(error); 41 | } 42 | } 43 | 44 | private static async loadEnvVars(context: vscode.ExtensionContext, 45 | configurationManager: ConfigurationManager, 46 | cancellationToken?: vscode.CancellationToken 47 | ): Promise { 48 | const result = EnvironmentService.fetchSetupEnvironment(context, configurationManager.getSetupEnvironmentCommand(), cancellationToken); 49 | context.workspaceState.update('setupEnvVars', result); 50 | return result; 51 | } 52 | 53 | // Methods to manage environment variables 54 | public getEnvVars(): string[] { 55 | return this.envVars; 56 | } 57 | } -------------------------------------------------------------------------------- /src/models/bazel-target-multi-property.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTarget } from './bazel-target'; 26 | import { BazelTargetPropertyHistory } from './bazel-target-property-history'; 27 | import { v4 as uuidv4 } from 'uuid'; 28 | import * as vscode from 'vscode'; 29 | 30 | export class BazelTargetMultiPropertyItem { 31 | public readonly id: string; 32 | constructor(private readonly target: BazelTargetMultiProperty, 33 | public readonly label: string, 34 | id?: string 35 | ) { 36 | if (id === undefined) { 37 | this.id = uuidv4(); 38 | } else { 39 | this.id = id; 40 | } 41 | } 42 | 43 | public remove() { 44 | this.target.remove(this); 45 | } 46 | 47 | public update(value: string) { 48 | this.target.replace(value, this); 49 | } 50 | 51 | public get(): string { 52 | return this.label; 53 | } 54 | 55 | public toJSON() { 56 | return { 57 | label: this.label, 58 | id: this.id 59 | }; 60 | } 61 | } 62 | export class BazelTargetMultiProperty { 63 | public readonly id: string = ''; 64 | private readonly history: BazelTargetPropertyHistory; 65 | 66 | constructor( 67 | private readonly context: vscode.ExtensionContext, 68 | public readonly label: string, 69 | public readonly name: string, 70 | target: BazelTarget, 71 | private readonly toStringFn: (bazelTargetProperty: BazelTargetMultiProperty) => string, 72 | private readonly availableValuesFn: (cancellationToken: vscode.CancellationToken) => Promise = (): Promise => { return Promise.resolve([]); }, 73 | private readonly showHistory = true, 74 | ) { 75 | this.id = `${target.action}${name}For${target.id}`; 76 | this.history = new BazelTargetPropertyHistory(context, name, 10); 77 | } 78 | 79 | public clone(newTarget: BazelTarget): BazelTargetMultiProperty { 80 | const clonedProperty = new BazelTargetMultiProperty( 81 | this.context, 82 | this.label, 83 | this.name, 84 | newTarget, // Pass the new BazelTarget instance 85 | this.toStringFn, 86 | this.availableValuesFn, 87 | this.showHistory 88 | ); 89 | 90 | // Copy all items 91 | const values = this.get().map(item => new BazelTargetMultiPropertyItem(clonedProperty, item.label)); 92 | clonedProperty.update(values); 93 | 94 | return clonedProperty; 95 | } 96 | 97 | public add(value: string) { 98 | const values = this.get(); 99 | values.push(new BazelTargetMultiPropertyItem(this, value)); 100 | this.update(values); 101 | this.history.add(value); 102 | } 103 | 104 | public remove(item: BazelTargetMultiPropertyItem) { 105 | let values = this.get(); 106 | values = values.filter(it => it.id !== item.id); 107 | this.update(values); 108 | } 109 | 110 | public replace(value: string, item: BazelTargetMultiPropertyItem) { 111 | let values = this.get(); 112 | values = values.map(it => { 113 | if (it.id === item.id) { 114 | return new BazelTargetMultiPropertyItem(this, value, it.id); 115 | } 116 | return it; 117 | }); 118 | this.history.add(value); 119 | this.update(values); 120 | } 121 | 122 | public update(values: BazelTargetMultiPropertyItem[]) { 123 | this.context.workspaceState.update(this.id, values); 124 | } 125 | 126 | public get(): BazelTargetMultiPropertyItem[] { 127 | const storedValues = this.context.workspaceState.get(this.id) || []; 128 | 129 | // Re-create BazelTargetMultiPropertyItem instances, passing 'this' as the reference 130 | const values = storedValues.map(value => new BazelTargetMultiPropertyItem(this, value.label, value.id)); 131 | return values; // Return the re-instantiated items with references to 'this' 132 | } 133 | 134 | public shouldShowHistory(): boolean { 135 | return this.showHistory; 136 | } 137 | 138 | public getAvailableValues(cancellationToken: vscode.CancellationToken): Promise { 139 | return this.availableValuesFn(cancellationToken); 140 | } 141 | 142 | public getHistory(): string[] { 143 | return this.history.getHistory(); 144 | } 145 | 146 | public toStringArray(): string[] { 147 | const values = this.get(); 148 | const stringValues: string[] = []; 149 | values.forEach(item => { 150 | stringValues.push(item.get()); 151 | }); 152 | return stringValues; 153 | } 154 | 155 | public toString(): string { 156 | return this.toStringFn(this); 157 | } 158 | } 159 | 160 | -------------------------------------------------------------------------------- /src/models/bazel-target-property-history.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import * as vscode from 'vscode'; 26 | interface HistoryMap { 27 | [key: string]: string[] 28 | } 29 | 30 | export class BazelTargetPropertyHistory { 31 | 32 | private historyMap: HistoryMap = {}; 33 | private readonly DEFAULT_TARGET: string = 'DEFAULT_TARGET'; 34 | private readonly key: string; 35 | constructor(private readonly context: vscode.ExtensionContext, 36 | readonly name: string, 37 | readonly size: number) { 38 | 39 | this.key = `${name}History`; 40 | const res = this.context.workspaceState.get(this.key); 41 | if (res === undefined) { 42 | const newRes: HistoryMap = {}; 43 | this.historyMap = newRes; 44 | this.context.workspaceState.update(this.key, this.historyMap); 45 | } else { 46 | this.historyMap = res; 47 | } 48 | } 49 | 50 | public getFirstHistoryItem(target?: string): string { 51 | target = target || this.DEFAULT_TARGET; 52 | const result = this.historyMap[target]; 53 | if (result !== undefined && result.length > 0) { 54 | return result[0]; 55 | } 56 | return ''; 57 | } 58 | 59 | public getHistory(target?: string): string[] { 60 | target = target || this.DEFAULT_TARGET; 61 | if (this.historyMap[target] === undefined) { 62 | return []; 63 | } 64 | return this.historyMap[target]; 65 | } 66 | 67 | public add(value: string, target?: string) { 68 | target = target || this.DEFAULT_TARGET; 69 | let history = this.historyMap[target]; 70 | if (history === undefined) { 71 | history = []; 72 | } 73 | 74 | if (history.includes(value)) { 75 | // If this value is in the history, delete it, as we'll re-add it to the beginning. 76 | const index = history.indexOf(value); 77 | history.splice(index, 1); 78 | } 79 | 80 | history.unshift(value); 81 | // Remove the last element to keep history size reasonable. 82 | if (history.length > this.size) { 83 | history.pop(); 84 | } 85 | this.historyMap[target] = history; 86 | this.context.workspaceState.update(this.key, this.historyMap); 87 | } 88 | } -------------------------------------------------------------------------------- /src/models/bazel-target-property.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTarget } from './bazel-target'; 26 | import { BazelTargetPropertyHistory } from './bazel-target-property-history'; 27 | import * as vscode from 'vscode'; 28 | 29 | export class BazelTargetProperty { 30 | public readonly id: string = ''; 31 | private readonly history: BazelTargetPropertyHistory; 32 | 33 | constructor( 34 | private readonly context: vscode.ExtensionContext, 35 | public readonly label: string, 36 | public readonly name: string, 37 | target: BazelTarget, 38 | private readonly toStringFn: (bazelTargetProperty: BazelTargetProperty) => string 39 | ) { 40 | this.id = `${target.action}${name}For${target.id}`; 41 | this.history = new BazelTargetPropertyHistory(context, name, 10); 42 | } 43 | 44 | public clone(newTarget: BazelTarget): BazelTargetProperty { 45 | return new BazelTargetProperty( 46 | this.context, 47 | this.label, 48 | this.name, 49 | newTarget, // Pass the new BazelTarget instance 50 | this.toStringFn 51 | ); 52 | } 53 | 54 | public update(value: string) { 55 | this.history.add(value); 56 | this.context.workspaceState.update(this.id, value); 57 | } 58 | 59 | public get(): string { 60 | const value = this.context.workspaceState.get(this.id) || ''; 61 | return value; 62 | } 63 | 64 | public getHistory(): string[] { 65 | return this.history.getHistory(); 66 | } 67 | 68 | public toString(): string { 69 | return this.toStringFn(this); 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/models/bazel-target-state-manager.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTarget } from './bazel-target'; 26 | import { EventEmitter } from 'vscode'; 27 | 28 | export enum BazelTargetState { 29 | Idle = 'idle', 30 | Executing = 'executing', 31 | Debugging = 'debugging' 32 | } 33 | 34 | export class BazelTargetStateManager { 35 | private targetStateMap: Map = new Map(); 36 | 37 | // Event emitter to notify when target state changes 38 | private _onDidChangeTargetState: EventEmitter = new EventEmitter(); 39 | 40 | // Event that consumers can subscribe to 41 | public readonly onDidChangeTargetState = this._onDidChangeTargetState.event; 42 | 43 | // Method to set the state of a target 44 | public setTargetState(target: BazelTarget, state: BazelTargetState): void { 45 | this.targetStateMap.set(target.id, state); 46 | // Emit event whenever the state is set 47 | this._onDidChangeTargetState.fire(target); 48 | } 49 | 50 | // Method to get the state of a target 51 | public getTargetState(target: BazelTarget): BazelTargetState { 52 | return this.targetStateMap.get(target.id) || BazelTargetState.Idle; 53 | } 54 | } -------------------------------------------------------------------------------- /src/models/model-accessor.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { Model } from './model'; 25 | 26 | export class ModelAccessor { 27 | public static getString(model: Model): string { 28 | const value = model.get(); 29 | if (typeof value !== 'string') { 30 | return ''; 31 | } else { 32 | return value; 33 | } 34 | } 35 | 36 | public static getWithDefault(model: Model, defaultValue?: T) { 37 | const value = model.get(); 38 | if (value === undefined) { 39 | return defaultValue; 40 | } 41 | return value; 42 | } 43 | 44 | public static getStringArray(model: Model): string[] { 45 | const values = model.get(); 46 | if (!values) { 47 | return []; 48 | } 49 | return values; 50 | } 51 | } -------------------------------------------------------------------------------- /src/models/model.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | export interface Model { 26 | name: string; 27 | id: string; 28 | update(value: T): void; 29 | get(): T | undefined; 30 | } -------------------------------------------------------------------------------- /src/models/workspace-state-manager.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { Console } from '../services/console'; 26 | import { ExtensionUtils } from '../services/extension-utils'; 27 | import * as vscode from 'vscode'; 28 | 29 | 30 | export class WorkspaceStateManager { 31 | private versionChangeHappened = false; 32 | private majorVersionChangeHappened = false; 33 | 34 | constructor(private readonly context: vscode.ExtensionContext) { 35 | this.versionChangeHappened = this.checkVersionChange(); 36 | this.majorVersionChangeHappened = this.checkVersionChange(true); 37 | } 38 | 39 | 40 | private checkVersionChange(majorVersionOnly = false): boolean { 41 | const version = ExtensionUtils.getExtensionVersion(this.context); 42 | const oldVersion = this.context.workspaceState.get('version', ''); 43 | if (majorVersionOnly) { 44 | const majorOld = oldVersion.split('.')[0]; 45 | const majorNew = version.split('.')[0]; 46 | return majorOld !== majorNew; 47 | } 48 | return oldVersion !== version; 49 | } 50 | 51 | public versionChanged(): boolean { 52 | return this.versionChangeHappened; 53 | } 54 | 55 | public majorVersionChanged(): boolean { 56 | return this.majorVersionChangeHappened; 57 | } 58 | 59 | public refreshWorkspaceState() { 60 | if (this.majorVersionChanged()) { 61 | this.clearWorkspaceState(); 62 | this.context.workspaceState.update('version', ExtensionUtils.getExtensionVersion(this.context)); 63 | Console.info('Workspace state has been cleared due to major version bump.'); 64 | } 65 | } 66 | 67 | private clearWorkspaceState() { 68 | const keys = this.context.workspaceState.keys(); 69 | for (const key of keys) { 70 | this.context.workspaceState.update(key, undefined); 71 | } 72 | } 73 | 74 | public update(key: string, value: T) { 75 | this.context.workspaceState.update(key, value); 76 | } 77 | 78 | public get(key: string, defaultValue: T): T { 79 | return this.context.workspaceState.get(key, defaultValue); 80 | } 81 | } -------------------------------------------------------------------------------- /src/services/bazel-rule-language-mapping.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | export const languageMapping: Map = new Map([ 25 | // Go 26 | ['go_', 'go'], 27 | 28 | // Python 29 | ['py_', 'python'], 30 | 31 | // C and C++ 32 | ['c_', 'c'], 33 | ['cc_', 'cpp'], 34 | ['cpp_', 'cpp'], 35 | 36 | // Java 37 | ['java_', 'java'], 38 | 39 | // JavaScript and TypeScript 40 | ['js_', 'javascript'], 41 | ['ts_', 'typescript'], 42 | 43 | // Scala 44 | ['scala_', 'scala'], 45 | 46 | // Protocol Buffers 47 | ['proto_', 'protobuf'], 48 | 49 | // Shell/Bash 50 | ['sh_', 'shellscript'], 51 | 52 | // JSON 53 | ['json_', 'json'], 54 | 55 | // HTML and CSS 56 | ['html_', 'html'], 57 | ['css_', 'css'], 58 | 59 | // Ruby 60 | ['rb_', 'ruby'], 61 | 62 | // PHP 63 | ['php_', 'php'], 64 | 65 | // Rust 66 | ['rs_', 'rust'], 67 | 68 | // Swift 69 | ['swift_', 'swift'], 70 | 71 | // Kotlin 72 | ['kt_', 'kotlin'], 73 | ['kts_', 'kotlin'], 74 | 75 | // Lua 76 | ['lua_', 'lua'], 77 | 78 | // SQL 79 | ['sql_', 'sql'], 80 | 81 | // Markdown 82 | ['md_', 'markdown'], 83 | 84 | // XML 85 | ['xml_', 'xml'], 86 | 87 | // Dart 88 | ['dart_', 'dart'], 89 | 90 | // Haskell 91 | ['hs_', 'haskell'], 92 | 93 | // Erlang 94 | ['erl_', 'erlang'], 95 | ['hrl_', 'erlang'], 96 | 97 | // Clojure 98 | ['clj_', 'clojure'], 99 | ['cljs_', 'clojure'], 100 | 101 | // R 102 | ['r_', 'r'], 103 | 104 | // Visual Basic 105 | ['vb_', 'vb'], 106 | 107 | // CoffeeScript 108 | ['coffee_', 'coffeescript'], 109 | 110 | // F# 111 | ['fsharp_', 'fsharp'], 112 | 113 | // Groovy 114 | ['groovy_', 'groovy'], 115 | 116 | // YAML 117 | ['yaml_', 'yaml'], 118 | 119 | // PowerShell 120 | ['powershell_', 'powershell'], 121 | 122 | // Batch Scripting 123 | ['batch_', 'bat'], 124 | 125 | // Makefile 126 | ['make_', 'makefile'], 127 | 128 | // Dockerfile 129 | ['docker_', 'dockerfile'], 130 | 131 | // Racket 132 | ['rkt_', 'racket'], 133 | 134 | // Lisp and Scheme 135 | ['lisp_', 'lisp'], 136 | ['scheme_', 'scheme'], 137 | 138 | // Vala 139 | ['vala_', 'vala'], 140 | 141 | // Objective-C and Objective-C++ 142 | ['objc_', 'objective-c'], 143 | ['objcxx_', 'objective-cpp'], 144 | 145 | // Nim 146 | ['nim_', 'nim'], 147 | 148 | // Elixir 149 | ['elixir_', 'elixir'], 150 | 151 | // Haxe 152 | ['haxe_', 'haxe'], 153 | 154 | // Fortran 155 | ['fortran_', 'fortran'], 156 | ]); 157 | 158 | // Sort prefixes by length in descending order to prioritize longer, more specific prefixes 159 | export const sortedBazelRuleTypePrefixes: string[] = Array.from(languageMapping.keys()).sort((a, b) => b.length - a.length); 160 | 161 | export const extensionLanguageMapping: Map = new Map([ 162 | // Go 163 | ['.go', 'go'], 164 | 165 | // Python 166 | ['.py', 'python'], 167 | 168 | // C and C++ 169 | ['.c', 'c'], 170 | ['.cpp', 'cpp'], 171 | ['.cc', 'cpp'], 172 | ['.h', 'cpp'], 173 | 174 | // Java 175 | ['.java', 'java'], 176 | 177 | // JavaScript and TypeScript 178 | ['.js', 'javascript'], 179 | ['.jsx', 'javascript'], 180 | ['.ts', 'typescript'], 181 | ['.tsx', 'typescript'], 182 | 183 | // Scala 184 | ['.scala', 'scala'], 185 | 186 | // Protocol Buffers 187 | ['.proto', 'protobuf'], 188 | 189 | // Shell/Bash 190 | ['.sh', 'shellscript'], 191 | 192 | // JSON 193 | ['.json', 'json'], 194 | 195 | // HTML and CSS 196 | ['.html', 'html'], 197 | ['.css', 'css'], 198 | 199 | // Ruby 200 | ['.rb', 'ruby'], 201 | 202 | // PHP 203 | ['.php', 'php'], 204 | 205 | // Rust 206 | ['.rs', 'rust'], 207 | 208 | // Swift 209 | ['.swift', 'swift'], 210 | 211 | // Kotlin 212 | ['.kt', 'kotlin'], 213 | ['.kts', 'kotlin'], 214 | 215 | // Lua 216 | ['.lua', 'lua'], 217 | 218 | // SQL 219 | ['.sql', 'sql'], 220 | 221 | // Markdown 222 | ['.md', 'markdown'], 223 | 224 | // XML 225 | ['.xml', 'xml'], 226 | 227 | // Dart 228 | ['.dart', 'dart'], 229 | 230 | // Haskell 231 | ['.hs', 'haskell'], 232 | 233 | // Erlang 234 | ['.erl', 'erlang'], 235 | ['.hrl', 'erlang'], 236 | 237 | // Clojure 238 | ['.clj', 'clojure'], 239 | ['.cljs', 'clojure'], 240 | 241 | // R 242 | ['.r', 'r'], 243 | 244 | // Visual Basic 245 | ['.vb', 'vb'], 246 | 247 | // CoffeeScript 248 | ['.coffee', 'coffeescript'], 249 | 250 | // F# 251 | ['.fs', 'fsharp'], 252 | 253 | // Groovy 254 | ['.groovy', 'groovy'], 255 | 256 | // YAML 257 | ['.yaml', 'yaml'], 258 | ['.yml', 'yaml'], 259 | 260 | // PowerShell 261 | ['.ps1', 'powershell'], 262 | 263 | // Batch Scripting 264 | ['.bat', 'bat'], 265 | 266 | // Makefile 267 | ['Makefile', 'makefile'], 268 | 269 | // Dockerfile 270 | ['Dockerfile', 'dockerfile'], 271 | 272 | // Racket 273 | ['.rkt', 'racket'], 274 | 275 | // Lisp and Scheme 276 | ['.lisp', 'lisp'], 277 | ['.scm', 'scheme'], 278 | 279 | // Vala 280 | ['.vala', 'vala'], 281 | 282 | // Objective-C and Objective-C++ 283 | ['.m', 'objective-c'], 284 | ['.mm', 'objective-cpp'], 285 | 286 | // Nim 287 | ['.nim', 'nim'], 288 | 289 | // Elixir 290 | ['.ex', 'elixir'], 291 | ['.exs', 'elixir'], 292 | 293 | // Haxe 294 | ['.hx', 'haxe'], 295 | 296 | // Fortran 297 | ['.f90', 'fortran'], 298 | ['.f95', 'fortran'], 299 | ['.for', 'fortran'], 300 | ]); 301 | -------------------------------------------------------------------------------- /src/services/configuration-utils.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { Console } from './console'; 25 | import { ExtensionUtils } from './extension-utils'; 26 | import * as fs from 'fs'; 27 | import * as path from 'path'; 28 | import * as vscode from 'vscode'; 29 | import { workspace, WorkspaceConfiguration } from 'vscode'; 30 | 31 | export function getExtensionDefaultSettings(extensionName: string): JSON { 32 | const workspaceFolders = vscode.workspace.workspaceFolders; 33 | if (!workspaceFolders) { 34 | Console.error('No workspace folder found.'); 35 | return JSON.parse('{}'); 36 | } 37 | 38 | const defaultSettings = []; 39 | for (const folder of workspaceFolders) { 40 | const rootPath = folder.uri.fsPath; 41 | 42 | // Construct the path to the .vscode directory 43 | const defaultSettingsPath = path.join(rootPath, '.vscode', `${extensionName}.json`); 44 | 45 | // If this default settings file does not exist, 46 | // skip this workspace folder. 47 | if (!fs.existsSync(defaultSettingsPath)) { 48 | continue; 49 | } 50 | 51 | try { 52 | const fileContent = fs.readFileSync(defaultSettingsPath, 'utf8'); 53 | defaultSettings.push(JSON.parse(fileContent)); 54 | } catch (error) { 55 | Console.error('Failed to parse default settings', error); 56 | } 57 | } 58 | 59 | 60 | // Assign the array to a dictionary 61 | const contents = Object.assign({}, ...defaultSettings); 62 | const keyPrefix = `${extensionName}.`; 63 | // Remove the extension name from the front of every key 64 | Object.keys(contents).forEach((oldKey) => { 65 | // Remove the extension name from the prefix of the settings 66 | if (oldKey.includes(keyPrefix)) { 67 | const newKey = oldKey.replace(keyPrefix, ''); 68 | contents[newKey] = contents[oldKey]; 69 | } 70 | delete contents[oldKey]; 71 | }); 72 | 73 | // Return the settings as a key-value paired object 74 | return contents; 75 | } 76 | 77 | export class MergedConfiguration implements WorkspaceConfiguration { 78 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 79 | private defaultConfig: any; 80 | constructor(private readonly context: vscode.ExtensionContext, 81 | defaultConfig: JSON) { 82 | // Additional initialization if needed 83 | this.defaultConfig = defaultConfig; 84 | } 85 | 86 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 87 | readonly [key: string]: any; 88 | 89 | has(section: string): boolean { 90 | return this.getUserSettings().has(section) || section in this.defaultConfig; 91 | } 92 | 93 | inspect(section: string): { key: string; defaultValue?: T | undefined; globalValue?: T | undefined; workspaceValue?: T | undefined; workspaceFolderValue?: T | undefined; defaultLanguageValue?: T | undefined; globalLanguageValue?: T | undefined; workspaceLanguageValue?: T | undefined; workspaceFolderLanguageValue?: T | undefined; languageIds?: string[] | undefined; } | undefined { 94 | return this.getUserSettings().inspect(section); 95 | } 96 | 97 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 98 | update(section: string, value: any, configurationTarget?: boolean | vscode.ConfigurationTarget | undefined, overrideInLanguage?: boolean | undefined): Thenable { 99 | return this.getUserSettings().update(section, value, configurationTarget, overrideInLanguage); 100 | } 101 | 102 | get(section: string, defaultValue?: T): T | undefined { 103 | const settingValue = this.getUserSettings().get(section); 104 | const settingsInspection = this.getUserSettings().inspect(section); 105 | const isModifiedByUser = settingValue !== undefined && 106 | (settingsInspection?.globalValue !== undefined || 107 | settingsInspection?.workspaceFolderValue !== undefined || 108 | settingsInspection?.workspaceValue !== undefined); 109 | if (isModifiedByUser) { 110 | return settingValue; 111 | } else if (section in this.defaultConfig) { 112 | return this.defaultConfig[section] as T; 113 | } else if (defaultValue) { 114 | return defaultValue; 115 | } else { 116 | return settingValue; 117 | } 118 | } 119 | 120 | getUserSettings(): WorkspaceConfiguration { 121 | const extensionName = ExtensionUtils.getExtensionName(this.context); 122 | return workspace.getConfiguration(extensionName); 123 | } 124 | } -------------------------------------------------------------------------------- /src/services/console.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { ExtensionUtils } from './extension-utils'; 25 | import * as vscode from 'vscode'; 26 | 27 | export class Console { 28 | // Static instance for singleton pattern 29 | private static instance: Console; 30 | 31 | private readonly prefix: string; 32 | private constructor(context: vscode.ExtensionContext) { 33 | this.prefix = `[${ExtensionUtils.getExtensionName(context)}]`; 34 | } 35 | 36 | // Initialize the singleton instance with the context (to be called once in extension activate) 37 | public static initialize(context: vscode.ExtensionContext): void { 38 | if (!Console.instance) { 39 | Console.instance = new Console(context); 40 | } 41 | } 42 | 43 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 44 | public static log(message?: any, ...optionalParams: any[]) { 45 | console.log(Console.instance.prefix, message, ...optionalParams); 46 | } 47 | 48 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 49 | public static info(message?: any, ...optionalParams: any[]) { 50 | console.info(Console.instance.prefix, message, ...optionalParams); 51 | } 52 | 53 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 54 | public static warn(message?: any, ...optionalParams: any[]) { 55 | console.warn(Console.instance.prefix, message, ...optionalParams); 56 | } 57 | 58 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 59 | public static error(message?: any, ...optionalParams: any[]) { 60 | console.error(Console.instance.prefix, message, ...optionalParams); 61 | } 62 | 63 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 64 | public static debug(message?: any, ...optionalParams: any[]) { 65 | console.debug(Console.instance.prefix, message, ...optionalParams); 66 | } 67 | 68 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 69 | public static time(message?: any) { 70 | console.time(`${Console.instance.prefix} ${message}`); 71 | } 72 | 73 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 74 | public static timeEnd(message?: any) { 75 | console.timeEnd(`${Console.instance.prefix} ${message}`); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/services/env-vars-utils.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | export class EnvVarsUtils { 26 | /** 27 | * Converts a list of environement variables into a list of objects. 28 | * @param envVars A list of environment variables in the form of ['A=first', 'B=second', ...]. 29 | * @returns A list of objects in the form of [{name: 'A', value: 'first}, {name: 'B', value: 'second'}, ...]. 30 | */ 31 | public static listToArrayOfObjects(envVars: string[]): Array<{ name: string; value: string }> { 32 | const vars: Array<{ name: string; value: string }> = []; 33 | 34 | envVars.forEach((item) => { 35 | const index = item.indexOf('='); 36 | if (index !== -1) { 37 | const name = item.substring(0, index); // Everything before the first '=' is the name 38 | const value = item.substring(index + 1); // Everything after the first '=' is the value 39 | const nameValuePair = { 40 | name: name, 41 | value: value 42 | }; 43 | vars.push(nameValuePair); 44 | } 45 | }); 46 | 47 | return vars; 48 | } 49 | 50 | 51 | /** 52 | * Converts a list of environment variables into an object literal or a map. 53 | * @param envVars A list of environment variables in the form of ['A=first', 'B=second', ...]. 54 | * @returns An object literal or map in the form of {A: 'first', B: 'second', ...}. 55 | */ 56 | public static listToObject(envVars: string[]): { [key: string]: string } { 57 | const envVariables: { [key: string]: string } = {}; 58 | 59 | envVars.forEach((item) => { 60 | const index = item.indexOf('='); 61 | if (index !== -1) { 62 | const key = item.substring(0, index); 63 | const value = item.substring(index + 1); // Capture everything after the first '=' as the value 64 | envVariables[key] = value; 65 | } 66 | }); 67 | 68 | return envVariables; 69 | } 70 | 71 | /** 72 | * Converts a list of environment variables into bazel speific build environment variables. 73 | * @param envVars A list of environment variables in the form of ['A=first', 'B=second', ...]. 74 | * @returns A concatenated string of all the environment variables suitable for bazel build args 75 | * in the form of '--action_env=A=first --action_env=B=second --action_env=...'. 76 | */ 77 | public static toBuildEnvVars(envVars: string[]): string { 78 | let vars = ''; 79 | const set = envVars; 80 | set.forEach((value) => { 81 | vars += `--action_env=${value} `; 82 | }); 83 | return vars; 84 | } 85 | 86 | /** 87 | * Converts a list of environment variables into run environment variables. 88 | * @param envVars A list of environment variables in the form of ['A=first', 'B=second', ...]. 89 | * @returns A concatenated string of all the environment variables suitable for run args 90 | * in the form of 'export A=first && export B=second && export ...'. 91 | */ 92 | public static toRunEnvVars(envVars: string[]): string { 93 | let vars = ''; 94 | const set = envVars; 95 | set.forEach((value) => { 96 | vars += `export ${value} && `; 97 | }); 98 | 99 | return vars; 100 | } 101 | 102 | /** 103 | * Converts a list of environment variables into bazel speific test environment variables. 104 | * @param envVars A list of environment variables in the form of ['A=first', 'B=second', ...]. 105 | * @returns A concatenated string of all the environment variables suitable for bazel test args 106 | * in the form of '--test_env=A=first --test_env=B=second --test_env=...'. 107 | */ 108 | public static toTestEnvVars(envVars: string[]): string { 109 | let vars = ''; 110 | const set = envVars; 111 | set.forEach((value) => { 112 | vars += `--test_env=${value} `; 113 | }); 114 | return vars; 115 | } 116 | } -------------------------------------------------------------------------------- /src/services/environment-service.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { Console } from './console'; 26 | import { ExtensionUtils } from './extension-utils'; 27 | import { ShellService } from './shell-service'; 28 | import { WorkspaceService } from './workspace-service'; 29 | import * as vscode from 'vscode'; 30 | 31 | 32 | export class EnvironmentService { 33 | 34 | public static async fetchSetupEnvironment( 35 | context: vscode.ExtensionContext, 36 | envSetupCommand: string, 37 | cancellationToken?: vscode.CancellationToken 38 | ): Promise { 39 | const extName = ExtensionUtils.getExtensionName(context); 40 | const envDelimiter = `---${extName} setup---`; 41 | 42 | if (envSetupCommand) { 43 | try { 44 | // Run the setup command, ensuring it prints the environment variables 45 | const result = await ShellService.run( 46 | `${envSetupCommand} && echo ${envDelimiter} && env`, 47 | WorkspaceService.getInstance().getWorkspaceFolder().uri.path, 48 | {}, 49 | cancellationToken 50 | ); 51 | 52 | // Remove everything before the delimiter, then split the output by newlines 53 | const env = result.stdout 54 | .replace(new RegExp(`[\\s\\S]*?${envDelimiter}\n`, 'g'), '') 55 | .split('\n'); 56 | 57 | const envArray: string[] = []; 58 | let currentVariable = ''; 59 | 60 | for (const line of env) { 61 | // Look for the first '=' to avoid issues with values containing '=' 62 | const index = line.indexOf('='); 63 | if (index !== -1) { 64 | if (currentVariable) { 65 | envArray.push(currentVariable); // Push previous variable 66 | } 67 | currentVariable = line; // Start new variable 68 | } else if (currentVariable) { 69 | // Handle case where the variable is spread over multiple lines (unlikely for environment variables) 70 | currentVariable += `\n${line}`; 71 | } 72 | } 73 | 74 | // Push the last variable 75 | if (currentVariable) { 76 | envArray.push(currentVariable); 77 | } 78 | 79 | return envArray; 80 | } catch (error) { 81 | // Log the error for debugging purposes 82 | Console.error('Error fetching setup environment:', error); 83 | return Promise.reject(error); 84 | } 85 | } 86 | 87 | // Return an empty array if no setup command is provided 88 | return []; 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/services/extension-utils.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import * as vscode from 'vscode'; 25 | 26 | /** 27 | * Utility class for retrieving extension-specific information. 28 | */ 29 | export class ExtensionUtils { 30 | /** 31 | * Gets the name of the extension dynamically. 32 | * @param context The extension context. 33 | * @returns The extension name. 34 | */ 35 | public static getExtensionName(context: vscode.ExtensionContext): string { 36 | return context.extension.packageJSON.name; 37 | } 38 | 39 | public static getExtensionDisplayName(context: vscode.ExtensionContext): string { 40 | return context.extension.packageJSON.displayName; 41 | } 42 | 43 | public static getPublisherName(context: vscode.ExtensionContext): string { 44 | return context.extension.packageJSON.publisher; 45 | } 46 | 47 | public static getExtensionVersion(context: vscode.ExtensionContext): string { 48 | return context.extension.packageJSON.version; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/services/file-watcher-service.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import * as vscode from 'vscode'; 26 | 27 | export class FileWatcherService { 28 | private watchers: vscode.FileSystemWatcher[] = []; 29 | private allAffectedFiles = new Set(); // Preserve all affected files globally 30 | 31 | constructor(private context: vscode.ExtensionContext) {} 32 | 33 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 34 | private static debounce void>(func: T, wait: number): T { 35 | let timeout: NodeJS.Timeout; 36 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 37 | return ((...args: any[]) => { 38 | clearTimeout(timeout); 39 | timeout = setTimeout(() => func(...args), wait); 40 | }) as T; 41 | } 42 | 43 | /** 44 | * Adds a watcher for a specific file pattern and fires a single event with affected file names when any matching files are created, updated, or deleted. 45 | * @param pattern Glob pattern to match files 46 | * @param onFilesChanged Callback triggered when any files matching the pattern are created, changed, or deleted. 47 | */ 48 | public watch( 49 | pattern: string, 50 | onFilesChanged: (affectedFiles: string[]) => void 51 | ): void { 52 | // Debounced callback to process the collected file paths 53 | const processEvents = FileWatcherService.debounce(() => { 54 | if (this.allAffectedFiles.size > 0) { 55 | onFilesChanged(Array.from(this.allAffectedFiles)); // Pass all files 56 | this.allAffectedFiles.clear(); // Clear after processing 57 | } 58 | }, 500); 59 | 60 | const watcher = vscode.workspace.createFileSystemWatcher(pattern); 61 | 62 | // Collect affected files for each event and ensure they persist across debounce calls 63 | const addAffectedFile = (uri: vscode.Uri) => { 64 | this.allAffectedFiles.add(uri.fsPath); // Add to global set 65 | processEvents(); // Trigger the debounced callback 66 | }; 67 | 68 | watcher.onDidCreate(addAffectedFile, this.context.subscriptions); 69 | watcher.onDidChange(addAffectedFile, this.context.subscriptions); 70 | watcher.onDidDelete(addAffectedFile, this.context.subscriptions); 71 | 72 | this.watchers.push(watcher); 73 | this.context.subscriptions.push(watcher); 74 | } 75 | 76 | /** 77 | * Disposes of all file watchers to release resources. 78 | */ 79 | public dispose(): void { 80 | this.watchers.forEach((watcher) => watcher.dispose()); 81 | this.watchers = []; 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/services/icon-service.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import * as fs from 'fs'; 26 | import * as os from 'os'; 27 | import * as path from 'path'; 28 | import * as vscode from 'vscode'; 29 | 30 | type IconDefinition = { 31 | iconPath?: string; 32 | fontCharacter?: string; 33 | fontColor?: string; 34 | fontSize?: string; 35 | fontId?: string; 36 | }; 37 | 38 | type IconTheme = { 39 | iconDefinitions: { [key: string]: IconDefinition }; 40 | fileExtensions?: { [key: string]: string }; 41 | fileNames?: { [key: string]: string }; 42 | languageIds?: { [key: string]: string }; 43 | }; 44 | 45 | /** 46 | * Service to handle icon theme parsing and retrieval. 47 | */ 48 | export class IconService { 49 | private iconTheme: IconTheme | null = null; 50 | private iconThemePath: string | undefined; 51 | 52 | constructor() { 53 | this.loadIconTheme(); 54 | } 55 | 56 | /** 57 | * Load and parse the active icon theme. 58 | */ 59 | private async loadIconTheme() { 60 | const iconThemeId = vscode.workspace.getConfiguration('workbench').get('iconTheme') as string | undefined; 61 | if (!iconThemeId) { 62 | return Promise.reject(new Error('No icon theme found')); 63 | } 64 | 65 | // Define potential paths for finding the active icon theme 66 | const searchPaths = [ 67 | path.join(vscode.env.appRoot, 'extensions'), // Built-in extensions in VSCode app root 68 | path.join(os.homedir(), '.vscode', 'extensions'), // User's extensions folder 69 | path.join(os.homedir(), '.vscode-server', 'extensions'), // VSCode server for remote 70 | path.join(os.homedir(), '.vscode-oss', 'extensions') // OSS version of VSCode 71 | ]; 72 | 73 | // Try to find the theme in each search path 74 | 75 | for (const searchPath of searchPaths) { 76 | this.iconThemePath = this.findIconThemeFile(searchPath, iconThemeId); 77 | if (this.iconThemePath) { 78 | break; 79 | } 80 | } 81 | 82 | if (!this.iconThemePath) { 83 | return Promise.reject(new Error('No icon theme path found')); 84 | } 85 | 86 | try { 87 | // Read and parse the icon theme JSON file 88 | const themeContent = await fs.promises.readFile(this.iconThemePath, 'utf-8'); 89 | this.iconTheme = JSON.parse(themeContent) as IconTheme; 90 | } catch (error) { 91 | return Promise.reject(error); 92 | } 93 | } 94 | 95 | /** 96 | * Get the icon path for a given language. 97 | * @param language The language ID (e.g., 'typescript', 'javascript'). 98 | * @returns The icon path or undefined if not found. 99 | */ 100 | public getIcon(language?: string): string | undefined { 101 | if (!this.iconTheme) { 102 | return undefined; 103 | } 104 | 105 | const iconPath = path.dirname(this.iconThemePath || ''); 106 | 107 | if (language !== undefined) { 108 | // Check for the language ID icon 109 | const iconId = this.iconTheme.languageIds?.[language]; 110 | if (iconId && this.iconTheme.iconDefinitions[iconId]) { 111 | return path.join(iconPath, this.iconTheme.iconDefinitions[iconId].iconPath || ''); 112 | } 113 | } else { 114 | // Check for the special case where language is not defined 115 | const folderIconId = this.iconTheme.iconDefinitions?._folder; 116 | if (folderIconId) { 117 | return path.join(iconPath, this.iconTheme.iconDefinitions['_folder'].iconPath || ''); 118 | } 119 | } 120 | 121 | // Fallback to default file icon 122 | const defaultIcon = this.iconTheme.iconDefinitions['_file']; 123 | return path.join(iconPath, defaultIcon?.iconPath || ''); 124 | } 125 | 126 | /** 127 | * Recursively finds the icon theme file in a given directory. 128 | * @param dirPath The directory to search in. 129 | * @param themeId The ID of the theme to look for. 130 | * @returns The path to the icon theme file if found, otherwise undefined. 131 | */ 132 | private findIconThemeFile(dirPath: string, themeId: string): string | undefined { 133 | if (!fs.existsSync(dirPath)) { 134 | return undefined; 135 | } 136 | 137 | const files = fs.readdirSync(dirPath, { withFileTypes: true }); 138 | for (const file of files) { 139 | const fullPath = path.join(dirPath, file.name); 140 | 141 | if (file.isDirectory()) { 142 | // Only continue searching if the themeId is in the directory path 143 | if (fullPath.includes(themeId)) { 144 | const iconThemeFile = this.findIconThemeFile(fullPath, themeId); 145 | if (iconThemeFile) { 146 | return iconThemeFile; 147 | } 148 | } 149 | } else if ( 150 | file.isFile() && 151 | file.name.includes('icon-theme') && 152 | file.name.endsWith('.json') 153 | ) { 154 | return fullPath; 155 | } 156 | } 157 | return undefined; 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/services/network-utils.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import * as net from 'net'; 26 | import * as vscode from 'vscode'; 27 | 28 | export async function getAvailablePort(cancellationToken?: vscode.CancellationToken): Promise { 29 | return new Promise((resolve, reject) => { 30 | const server = net.createServer(); 31 | // Handle cancellation 32 | if (cancellationToken) { 33 | const disposable = cancellationToken.onCancellationRequested(() => { 34 | server.close(); 35 | disposable.dispose(); 36 | reject(new Error('Operation cancelled')); 37 | }); 38 | } 39 | 40 | // Let the OS assign an available port by listening on port 0 41 | server.listen(0, '127.0.0.1', () => { 42 | const address = server.address(); 43 | if (address && typeof address === 'object') { 44 | const port = address.port; 45 | server.close((err) => { 46 | if (err) { 47 | reject(err); 48 | } else { 49 | resolve(port); 50 | } 51 | }); 52 | } else { 53 | reject(new Error('Failed to get server address')); 54 | } 55 | }); 56 | 57 | server.on('error', (err) => { 58 | reject(err); 59 | }); 60 | }); 61 | } 62 | 63 | function checkPortAvailable(port: number, host: string): Promise { 64 | return new Promise((resolve, reject) => { 65 | // Create a server instead of a client connection 66 | const server = net.createServer(); 67 | 68 | server.once('error', (err: NodeJS.ErrnoException) => { 69 | server.close(); 70 | 71 | if (err.code === 'EADDRINUSE') { 72 | // Port is in use, which means the debug server is likely running 73 | resolve(); 74 | } else { 75 | reject(err); 76 | } 77 | }); 78 | 79 | server.once('listening', () => { 80 | // If we can listen, the port is free (debug server isn't running yet) 81 | server.close(); 82 | reject(new Error('Port is not in use')); 83 | }); 84 | 85 | // Try to bind to the port 86 | server.listen(port, host); 87 | }); 88 | } 89 | 90 | 91 | export async function waitForPort( 92 | port: number, 93 | cancellationToken: vscode.CancellationToken, 94 | pollingIntervalMs = 50, 95 | host = '127.0.0.1', 96 | ): Promise { 97 | 98 | while (!cancellationToken.isCancellationRequested) { 99 | try { 100 | await checkPortAvailable(port, host); 101 | return; // Port is listening 102 | } catch (error) { 103 | // Port is not available yet, wait for the interval 104 | await new Promise((resolve) => setTimeout(resolve, pollingIntervalMs)); 105 | } 106 | } 107 | 108 | // If the cancellation was requested, throw an error or simply return 109 | throw new Error(`Waiting for port ${port} was cancelled.`); 110 | } 111 | 112 | export function isRemoteSession(): boolean { 113 | return vscode.env.remoteName !== undefined; 114 | } 115 | -------------------------------------------------------------------------------- /src/services/shell-service.ts: -------------------------------------------------------------------------------- 1 | 2 | //////////////////////////////////////////////////////////////////////////////////// 3 | // MIT License 4 | // 5 | // Copyright (c) 2021-2024 NVIDIA Corporation 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | //////////////////////////////////////////////////////////////////////////////////// 25 | 26 | import * as child from 'child_process'; 27 | import * as vscode from 'vscode'; 28 | 29 | export interface ProcessOutput { 30 | stdout: string, 31 | stderr: string, 32 | exitCode: number 33 | } 34 | 35 | export class ShellService { 36 | 37 | constructor(private readonly workspaceFolder: vscode.WorkspaceFolder, 38 | private readonly outputChannel: vscode.OutputChannel, 39 | private readonly setupEnvVars: {[key: string]: string} 40 | ) { } 41 | 42 | public static async run( 43 | cmd: string, 44 | cwd: string, 45 | setupEnvVars: { [key: string]: string }, 46 | cancellationToken?: vscode.CancellationToken, 47 | outputChannel?: vscode.OutputChannel 48 | ): Promise { 49 | return new Promise((resolve, reject) => { 50 | const execOptions: child.ExecOptions = { 51 | cwd: cwd, 52 | shell: 'bash', 53 | maxBuffer: Number.MAX_SAFE_INTEGER, 54 | windowsHide: false, 55 | env: { ...process.env, ...setupEnvVars } 56 | }; 57 | 58 | const proc = child.exec(cmd, execOptions); 59 | 60 | let stdout = ''; 61 | let stderr = ''; 62 | 63 | // Capture stdout line-by-line 64 | proc.stdout?.on('data', (data: string) => { 65 | stdout += data; 66 | const lines = data.split('\n'); // Split the incoming data into lines 67 | lines.forEach(line => { 68 | if (outputChannel) { 69 | outputChannel.appendLine(line); // Append each line to the output channel 70 | } 71 | }); 72 | }); 73 | 74 | // Capture stderr line-by-line 75 | proc.stderr?.on('data', (data: string) => { 76 | stderr += data; 77 | const lines = data.split('\n'); // Split the incoming data into lines 78 | lines.forEach(line => { 79 | if (outputChannel) { 80 | outputChannel.appendLine(line); // Append each line to the output channel 81 | } 82 | }); 83 | }); 84 | 85 | proc.on('close', (code) => { 86 | if (code !== 0) { 87 | reject(new Error(`Error running ${cmd} exited with code: ${code}`)); 88 | } 89 | resolve({ stdout: stdout.trim(), stderr: stderr.trim(), exitCode: code || 0 }); 90 | }); 91 | 92 | // Handle process errors 93 | proc.on('error', (err) => { 94 | reject(err); 95 | }); 96 | 97 | // Handle cancellation 98 | if (cancellationToken) { 99 | cancellationToken.onCancellationRequested(() => { 100 | proc.kill(); 101 | reject(new Error(`${cmd} cancelled.`)); 102 | }); 103 | } 104 | }); 105 | } 106 | 107 | public async runShellCommand(cmd: string, cancellationToken?: vscode.CancellationToken): Promise<{ stdout: string, stderr: string }> { 108 | return ShellService.run(cmd, this.workspaceFolder.uri.path, this.setupEnvVars, cancellationToken, this.outputChannel); 109 | } 110 | } -------------------------------------------------------------------------------- /src/services/string-utils.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | export function cleanAndFormat(...args: string[]): string { 26 | return args 27 | .filter(arg => arg.trim() !== '') // Remove empty or whitespace-only strings 28 | .map(arg => arg.trim()) // Trim spaces from each string 29 | .join(' '); // Join the cleaned parts with a single space 30 | } 31 | 32 | export function capitalizeFirstLetter(str: string): string { 33 | if (str === undefined) { 34 | return ''; 35 | } 36 | 37 | if (str.length < 1) { 38 | return str; 39 | } 40 | return str.charAt(0).toUpperCase() + str.slice(1); 41 | } 42 | 43 | export function createHashFromIds(elements: { id: string }[]): string { 44 | // Combine all IDs into a single string 45 | const combinedIds = elements.map(element => element.id).join(','); 46 | 47 | // Generate a simple hash using a checksum approach 48 | let hash = 0; 49 | for (let i = 0; i < combinedIds.length; i++) { 50 | const char = combinedIds.charCodeAt(i); 51 | hash = (hash << 5) - hash + char; // Bitwise operations for hashing 52 | hash |= 0; // Convert to 32-bit integer 53 | } 54 | 55 | // Convert the hash to a hexadecimal string 56 | return hash.toString(16); 57 | } -------------------------------------------------------------------------------- /src/services/task-service.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { EnvVarsUtils } from './env-vars-utils'; 26 | import { ExtensionUtils } from './extension-utils'; 27 | import { clearTerminal } from '../ui/terminal'; 28 | import * as vscode from 'vscode'; 29 | 30 | 31 | export class CustomTaskProvider implements vscode.TaskProvider { 32 | static readonly type = 'bluebazelTask'; 33 | private tasks: vscode.Task[] = []; 34 | 35 | provideTasks(): vscode.Task[] { 36 | return this.tasks; 37 | } 38 | 39 | resolveTask(task: vscode.Task): vscode.Task | undefined { 40 | const definition = task.definition as { type: string; label: string }; 41 | if (definition.type === CustomTaskProvider.type) { 42 | return task; 43 | } 44 | return undefined; 45 | } 46 | } 47 | 48 | export class TaskService { 49 | 50 | constructor(private readonly context: vscode.ExtensionContext, 51 | private readonly workspaceFolder: vscode.WorkspaceFolder, 52 | private readonly setupEnvVars: string[]) { 53 | const customTaskProvider = vscode.tasks.registerTaskProvider(CustomTaskProvider.type, new CustomTaskProvider()); 54 | context.subscriptions.push(customTaskProvider); 55 | } 56 | 57 | 58 | public async runTask(taskName: string, 59 | command: string, 60 | clearTerminalFirst: boolean, 61 | cancellationToken?: vscode.CancellationToken, 62 | id = '', 63 | envVars: { [key: string]: string } = {}, 64 | executionType: 'shell' | 'process' = 'shell', 65 | resolveOn: 'onDidStartTask' | 'onDidEndTask' = 'onDidEndTask', 66 | problemMatcher = '$gcc') { 67 | const workspaceFolder = this.workspaceFolder; 68 | 69 | const envVarsObj = { ...EnvVarsUtils.listToObject(this.setupEnvVars), ...envVars }; 70 | let execution: vscode.ShellExecution | vscode.ProcessExecution | vscode.CustomExecution; 71 | if (executionType === 'shell') { 72 | execution = new vscode.ShellExecution(command, { 73 | cwd: workspaceFolder.uri.path, env: envVarsObj, 74 | executable: '/bin/bash', // Ensure bash is used as the shell 75 | shellArgs: ['-c'] }); 76 | } else { 77 | const args = command.split(' '); 78 | execution = new vscode.ProcessExecution(args[0], args.slice(1), { cwd: workspaceFolder.uri.path, env: envVarsObj }); 79 | } 80 | 81 | // const taskType = `${CustomTaskProvider.type}-${taskName}-${id}`; // Dynamically set the task type 82 | const taskType = CustomTaskProvider.type; 83 | const definition = { 84 | type: taskType, 85 | label: taskName, 86 | id: id 87 | }; 88 | const task = new vscode.Task( 89 | definition, 90 | workspaceFolder, 91 | taskName, 92 | ExtensionUtils.getExtensionDisplayName(this.context), 93 | execution, 94 | problemMatcher // Adjust this as necessary 95 | ); 96 | 97 | task.presentationOptions = { 98 | reveal: vscode.TaskRevealKind.Always, 99 | panel: vscode.TaskPanelKind.Dedicated 100 | }; 101 | if (clearTerminalFirst) { 102 | clearTerminal(); 103 | } 104 | 105 | const taskExecution = await vscode.tasks.executeTask(task); 106 | 107 | return new Promise((resolve, reject) => { 108 | // if (resolveOn === 'onDidEndTask') { 109 | const disposable = vscode.tasks.onDidEndTask(e => { 110 | if (e.execution === taskExecution) { 111 | disposable.dispose(); 112 | resolve(e.execution); 113 | } 114 | }); 115 | // } 116 | if (resolveOn === 'onDidStartTask') { 117 | const disposable = vscode.tasks.onDidStartTask(e => { 118 | if (e.execution === taskExecution) { 119 | disposable.dispose(); 120 | resolve(e.execution); 121 | } 122 | }); 123 | } 124 | 125 | cancellationToken?.onCancellationRequested(() => { 126 | taskExecution.terminate(); 127 | reject(new Error(`${taskName} cancelled.`)); 128 | }); 129 | }); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/services/workspace-service.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import * as fs from 'fs'; 26 | import * as path from 'path'; 27 | import * as vscode from 'vscode'; 28 | 29 | export class WorkspaceService { 30 | // Static instance for singleton pattern 31 | private static instance: WorkspaceService; 32 | 33 | // Cached workspace folder 34 | private workspaceFolder: vscode.WorkspaceFolder; 35 | 36 | // Private constructor to prevent direct instantiation 37 | private constructor() { 38 | this.workspaceFolder = this.initializeWorkspaceFolder(); 39 | } 40 | 41 | // Static method to get the singleton instance 42 | public static getInstance(): WorkspaceService { 43 | if (!WorkspaceService.instance) { 44 | WorkspaceService.instance = new WorkspaceService(); 45 | } 46 | return WorkspaceService.instance; 47 | } 48 | 49 | // Method to initialize and cache the workspace folder 50 | private initializeWorkspaceFolder(): vscode.WorkspaceFolder { 51 | const paths = vscode.workspace.workspaceFolders; 52 | if (paths && paths.length > 0) { 53 | return paths[0]; 54 | } else { 55 | vscode.window.showErrorMessage('Could not find workspace.'); 56 | return { 57 | uri: vscode.Uri.file('.'), 58 | index: 0, 59 | name: 'none' 60 | }; 61 | } 62 | } 63 | 64 | // Public method to get the cached workspace folder 65 | public getWorkspaceFolder(): vscode.WorkspaceFolder { 66 | return this.workspaceFolder; 67 | } 68 | 69 | public async getSubdirectoryPaths(root = '') { 70 | const all = '/' + path.join('/', root, '...'); 71 | const absoluteRoot = path.join(WorkspaceService.getInstance().getWorkspaceFolder().uri.path, root); 72 | 73 | return fs.promises.readdir(absoluteRoot, { withFileTypes: true }).then((data) => { 74 | const res: string[] = new Array(data.length + 1); 75 | let index = 0; 76 | res[index++] = all; 77 | // The first entry is always all. 78 | return Promise.all(data.map(async (element) => { 79 | if (element.isDirectory() && element.name[0] !== '.') { 80 | res[index++] = '/' + path.join('/', root, element.name); 81 | } 82 | })).then(() => { return res.slice(0, index); }); 83 | }); 84 | } 85 | } -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import * as path from 'path'; 26 | import { runTests } from 'vscode-test'; 27 | 28 | 29 | async function main() { 30 | try { 31 | // The folder containing the Extension Manifest package.json 32 | // Passed to `--extensionDevelopmentPath` 33 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 34 | 35 | // The path to test runner 36 | // Passed to --extensionTestsPath 37 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 38 | 39 | // Download VS Code, unzip it and run the integration test 40 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 41 | } catch (err) { 42 | console.error('Failed to run tests'); 43 | process.exit(1); 44 | } 45 | } 46 | 47 | main(); 48 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import * as assert from 'assert'; 25 | import * as vscode from 'vscode'; 26 | 27 | suite('Extension E2E Tests', () => { 28 | suiteSetup(async () => { 29 | const extension = vscode.extensions.getExtension('NVIDIA.bluebazel'); 30 | assert.notStrictEqual(extension, undefined); 31 | await extension?.activate(); 32 | }); 33 | 34 | test('Commands are registered', () => { 35 | // Test if commands are correctly registered 36 | const extension = vscode.extensions.getExtension('NVIDIA.bluebazel'); 37 | assert.notStrictEqual(extension, undefined); 38 | if (extension === undefined) { 39 | return false; 40 | } 41 | vscode.commands.getCommands(true).then((registeredCommands: string[]) => { 42 | const expectedCommands = extension.packageJSON.commands; 43 | for (const cmd of expectedCommands) { 44 | assert.ok(registeredCommands.includes(cmd), `Command '${cmd}' is not registered.`); 45 | } 46 | }); 47 | }); 48 | 49 | }); -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import * as glob from 'glob'; 26 | import * as Mocha from 'mocha'; 27 | import * as path from 'path'; 28 | 29 | export function run(): Promise { 30 | // Create the mocha test 31 | const mocha = new Mocha({ 32 | ui: 'tdd', 33 | color: true 34 | }); 35 | 36 | const testsRoot = path.resolve(__dirname, '..'); 37 | 38 | return new Promise((c, e) => { 39 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 40 | if (err) { 41 | return e(err); 42 | } 43 | 44 | // Add files to the test suite 45 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 46 | 47 | try { 48 | // Run the mocha test 49 | mocha.run(failures => { 50 | if (failures > 0) { 51 | e(new Error(`${failures} tests failed.`)); 52 | } else { 53 | c(); 54 | } 55 | }); 56 | } catch (err) { 57 | console.error(err); 58 | e(err); 59 | } 60 | }); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /src/test/suite/services/bazel-parser.test.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { BAZEL_BIN, BazelParser } from '../../../services/bazel-parser'; 25 | import * as assert from 'assert'; 26 | import * as fsPromises from 'fs/promises'; 27 | import * as os from 'os'; 28 | import * as path from 'path'; 29 | import * as vscode from 'vscode'; 30 | 31 | async function createTempDir(): Promise { 32 | const tempDir = path.join(os.tmpdir(), 'bazel-test-'); 33 | return await fsPromises.mkdtemp(tempDir); 34 | } 35 | 36 | async function createBazelFiles( 37 | tempDir: string, 38 | workspacePaths: string[] = [], 39 | buildFilePaths: { [dir: string]: string } = {} 40 | ): Promise<{ workspace: string[]; build: string[] }> { 41 | const workspaceFiles: string[] = []; 42 | const buildFiles: string[] = []; 43 | 44 | for (const workspace of workspacePaths) { 45 | const workspacePath = path.join(tempDir, workspace); 46 | await fsPromises.mkdir(path.dirname(workspacePath), { recursive: true }); 47 | await fsPromises.writeFile(workspacePath, '# WORKSPACE file'); 48 | workspaceFiles.push(workspacePath); 49 | } 50 | 51 | for (const [dir, content] of Object.entries(buildFilePaths)) { 52 | const buildFilePath = path.join(tempDir, dir, 'BUILD'); 53 | await fsPromises.mkdir(path.dirname(buildFilePath), { recursive: true }); 54 | await fsPromises.writeFile(buildFilePath, content); 55 | buildFiles.push(buildFilePath); 56 | } 57 | 58 | return { workspace: workspaceFiles, build: buildFiles }; 59 | } 60 | 61 | suite('BazelParser', () => { 62 | let tempDir: string; 63 | 64 | setup(async () => { 65 | tempDir = await createTempDir(); 66 | }); 67 | 68 | teardown(async () => { 69 | await fsPromises.rm(tempDir, { recursive: true, force: true }); 70 | }); 71 | 72 | test('should find all workspace and build files', async () => { 73 | const { workspace, build } = await createBazelFiles(tempDir, ['WORKSPACE'], { 74 | 'src': ` 75 | py_binary( 76 | name = "test_target", 77 | srcs = ["test.py"], 78 | ) 79 | `, 80 | }); 81 | 82 | const result = await BazelParser.findBazelBuildFiles(tempDir); 83 | 84 | assert.deepStrictEqual(result.workspace.sort(), workspace.sort()); 85 | assert.deepStrictEqual(result.build.sort(), build.sort()); 86 | }); 87 | 88 | test('should parse build files with correct bazel paths', async () => { 89 | const workspaceFile = 'WORKSPACE'; 90 | const buildContent = ` 91 | cc_library( 92 | name = "test_lib", 93 | srcs = ["lib.cpp"], 94 | ) 95 | 96 | py_binary( 97 | name = "test_bin", 98 | srcs = ["main.py"], 99 | ) 100 | `; 101 | const { build } = await createBazelFiles(tempDir, [workspaceFile], { 'src': buildContent }); 102 | 103 | const result = await BazelParser.parseBazelBuildFileTargets(build[0], tempDir); 104 | 105 | assert.strictEqual(result.targets.length, 2); 106 | 107 | assert.deepStrictEqual(result.targets[0], { 108 | name: 'test_lib', 109 | ruleType: 'cc_library', 110 | srcExtensions: ['.cpp'], 111 | bazelPath: '//src:test_lib', 112 | buildPath: path.join(BAZEL_BIN, 'src', 'test_lib'), 113 | }); 114 | 115 | assert.deepStrictEqual(result.targets[1], { 116 | name: 'test_bin', 117 | ruleType: 'py_binary', 118 | srcExtensions: ['.py'], 119 | bazelPath: '//src:test_bin', 120 | buildPath: path.join(BAZEL_BIN, 'src', 'test_bin'), 121 | }); 122 | }); 123 | 124 | test('should handle multiple workspace files correctly', async () => { 125 | const { workspace, build } = await createBazelFiles(tempDir, ['root/WORKSPACE', 'subdir/WORKSPACE'], { 126 | 'subdir/src': ` 127 | py_binary( 128 | name = "sub_target", 129 | srcs = ["sub_main.py"], 130 | ) 131 | `, 132 | }); 133 | 134 | const resultWithoutPackage = await BazelParser.parseAllBazelBuildFilesTargets(tempDir, path.join(tempDir, 'root'), '.*', false); 135 | 136 | assert.strictEqual(resultWithoutPackage.length, 1); 137 | 138 | const result = await BazelParser.parseAllBazelBuildFilesTargets(tempDir, path.join(tempDir, 'root'), '.*', true); 139 | 140 | assert.strictEqual(result.length, 2); 141 | assert.strictEqual(result[0].bazelPath, '//src:sub_target'); 142 | assert.strictEqual(result[0].buildPath, path.join(BAZEL_BIN, 'src', 'sub_target')); 143 | }); 144 | 145 | test('should ignore directories without WORKSPACE or BUILD files', async () => { 146 | const result = await BazelParser.findBazelBuildFiles(tempDir); 147 | 148 | assert.deepStrictEqual(result, { workspace: [], build: [] }); 149 | }); 150 | 151 | test('should support cancellation tokens', async () => { 152 | const cancellationToken = new vscode.CancellationTokenSource(); 153 | cancellationToken.cancel(); 154 | 155 | const task = BazelParser.parseAllBazelBuildFilesTargets(tempDir, tempDir, '.*', true, cancellationToken.token); 156 | 157 | await assert.rejects(task, /cancelled/); 158 | }); 159 | 160 | test('should parse srcs and target names correctly', async () => { 161 | const buildContent = ` 162 | cc_library( 163 | name = "test_target", 164 | srcs = ["test.cpp", "utils.cpp"], 165 | ) 166 | `; 167 | 168 | const { build } = await createBazelFiles(tempDir, ['WORKSPACE'], { 'src': buildContent }); 169 | 170 | const result = await BazelParser.parseBazelBuildFileTargets(build[0], tempDir); 171 | 172 | assert.strictEqual(result.targets.length, 1); 173 | assert.strictEqual(result.targets[0].name, 'test_target'); 174 | assert.deepStrictEqual(result.targets[0].srcExtensions, ['.cpp', '.cpp']); 175 | }); 176 | }); 177 | -------------------------------------------------------------------------------- /src/test/suite/services/bazel-service.test.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { BazelTarget } from '../../../models/bazel-target'; 25 | import { BazelService } from '../../../services/bazel-service'; 26 | import { ConfigurationManager } from '../../../services/configuration-manager'; 27 | import { ShellService } from '../../../services/shell-service'; 28 | import { WorkspaceService } from '../../../services/workspace-service'; 29 | import * as assert from 'assert'; 30 | import * as fs from 'fs'; 31 | import * as sinon from 'sinon'; 32 | import * as vscode from 'vscode'; 33 | 34 | 35 | suite('BazelService Tests', () => { 36 | let bazelService: BazelService; 37 | let mockContext: vscode.ExtensionContext; 38 | let mockConfigurationManager: sinon.SinonStubbedInstance; 39 | let mockShellService: sinon.SinonStubbedInstance; 40 | let mockWorkspaceService: sinon.SinonStubbedInstance; 41 | 42 | setup(() => { 43 | // Mock dependencies 44 | mockContext = { 45 | workspaceState: { 46 | get: (key: string) => undefined, 47 | update: async (key: string, value: unknown) => Promise.resolve(), 48 | }, 49 | } as unknown as vscode.ExtensionContext; 50 | mockConfigurationManager = sinon.createStubInstance(ConfigurationManager); 51 | mockShellService = sinon.createStubInstance(ShellService); 52 | mockWorkspaceService = sinon.createStubInstance(WorkspaceService); 53 | 54 | // Initialize BazelService 55 | bazelService = new BazelService(mockContext, mockConfigurationManager, mockShellService); 56 | console.log(bazelService); 57 | }); 58 | 59 | teardown(() => { 60 | sinon.restore(); 61 | }); 62 | 63 | test('fetchTargetActions should return actions requiring a target', async () => { 64 | const actions = await bazelService.fetchTargetActions(); 65 | assert.deepStrictEqual(actions, [ 66 | 'aquery', 67 | 'build', 68 | 'coverage', 69 | 'cquery', 70 | 'mobile-install', 71 | 'print_action', 72 | 'query', 73 | 'run', 74 | 'test' 75 | ]); 76 | }); 77 | 78 | test('formatBazelTargetFromPath should correctly format a target path', () => { 79 | const targetPath = 'bazel-bin/src/test_target'; 80 | const result = BazelService.formatBazelTargetFromPath(targetPath); 81 | assert.strictEqual(result, '//src:test_target'); 82 | }); 83 | 84 | test('fetchAllTargets should call the appropriate fetch method', async () => { 85 | // Mock methods 86 | mockConfigurationManager.shouldFetchTargetsUsingQuery.returns(false); 87 | sinon.stub(bazelService, 'fetchAllTargetsFromBuildFiles').resolves([]); 88 | sinon.stub(bazelService, 'fetchAllTargetsFromQuery').resolves([]); 89 | 90 | // Call fetchAllTargets 91 | await bazelService.fetchAllTargets(); 92 | 93 | // Ensure the correct method was called 94 | sinon.assert.calledOnce(bazelService.fetchAllTargetsFromBuildFiles as sinon.SinonStub); 95 | sinon.assert.notCalled(bazelService.fetchAllTargetsFromQuery as sinon.SinonStub); 96 | }); 97 | 98 | test('fetchAllTargetsByAction should categorize targets correctly', async () => { 99 | // Mock fetchAllTargets 100 | sinon.stub(bazelService, 'fetchAllTargets').resolves([ 101 | { 102 | label: 'test_target', 103 | ruleType: 'cc_test', 104 | bazelPath: '//src:test_target', 105 | buildPath: '/bazel-bin/src/test_target', 106 | } as BazelTarget, 107 | ]); 108 | 109 | const result = await bazelService.fetchAllTargetsByAction(); 110 | 111 | // Verify categorization 112 | assert.strictEqual(result.get('test')?.length, 1); 113 | assert.strictEqual(result.get('build')?.length, 1); 114 | assert.strictEqual(result.get('run')?.length, 1); 115 | }); 116 | 117 | test('fetchAllTargetsFromQuery should parse query results', async () => { 118 | // Mock ShellService results 119 | mockShellService.runShellCommand.resolves({ 120 | stdout: 'cc_test package //src:test_target\ncc_binary package //src:test_binary', 121 | stderr: '', 122 | }); 123 | 124 | const result = await bazelService.fetchAllTargetsFromQuery(); 125 | assert.strictEqual(result.length, 5); 126 | 127 | // Verify target parsing 128 | assert.strictEqual(result[0].label, 'test_target'); 129 | assert.strictEqual(result[0].ruleType, 'cc_test'); 130 | assert.strictEqual(result[0].bazelPath, '//src:test_target'); 131 | assert.strictEqual(result[0].buildPath, '/bazel-bin/src/test_target'); 132 | }); 133 | 134 | test('findWorkspaceRoot should find the correct workspace directory', () => { 135 | // Mock filesystem structure 136 | const mockFs = sinon.stub(fs, 'existsSync'); 137 | mockFs.withArgs(sinon.match(/WORKSPACE/)).returns(true); 138 | 139 | const result = BazelService['findWorkspaceRoot']('/mock/path/to/project'); 140 | assert.strictEqual(result, '/mock/path/to/project'); 141 | 142 | mockFs.restore(); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /src/ui/bazel-target-quick-pick-item.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import { BazelTarget } from '../models/bazel-target'; 26 | import * as vscode from 'vscode'; 27 | 28 | export interface BazelTargetQuickPickItem extends vscode.QuickPickItem { 29 | target?: BazelTarget; 30 | } -------------------------------------------------------------------------------- /src/ui/code-lens-provider-utils.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { UnifiedCodeLensProvider } from './code-lens-providers/unified-code-lens-provider'; 25 | import { BazelService } from '../services/bazel-service'; 26 | import * as vscode from 'vscode'; 27 | 28 | export function registerCodeLensProviders(context: vscode.ExtensionContext, bazelService: BazelService) { 29 | const testProvider = new UnifiedCodeLensProvider(context, bazelService); 30 | 31 | vscode.languages.registerCodeLensProvider({ language: 'go', scheme: 'file' }, testProvider); 32 | vscode.languages.registerCodeLensProvider({ language: 'cpp', scheme: 'file' }, testProvider); 33 | vscode.languages.registerCodeLensProvider({ language: 'c', scheme: 'file' }, testProvider); 34 | vscode.languages.registerCodeLensProvider({ language: 'python', scheme: 'file' }, testProvider); 35 | } -------------------------------------------------------------------------------- /src/ui/code-lens-providers/unified-code-lens-provider.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import { LanguageRegistry } from '../../languages/language-registry'; 25 | import { BazelAction, BazelTarget } from '../../models/bazel-target'; 26 | import { BazelService } from '../../services/bazel-service'; 27 | import { Console } from '../../services/console'; 28 | import { ExtensionUtils } from '../../services/extension-utils'; 29 | import * as vscode from 'vscode'; 30 | 31 | enum PatternType { 32 | Run, 33 | Test 34 | } 35 | interface Pattern { 36 | type: PatternType; 37 | language: string; 38 | regex: RegExp; 39 | } 40 | 41 | export class UnifiedCodeLensProvider implements vscode.CodeLensProvider { 42 | 43 | private readonly regexPatterns: Pattern[]; 44 | 45 | constructor( 46 | private readonly context: vscode.ExtensionContext, 47 | private readonly bazelService: BazelService 48 | ) { 49 | this.regexPatterns = []; 50 | const languages = LanguageRegistry.getLanguages(); 51 | const testRegexes = languages.map(language => { 52 | try { 53 | return { 54 | language: language, 55 | type: PatternType.Test, 56 | regex: LanguageRegistry.getPlugin(language).getCodeLensTestRegex() 57 | }; 58 | } catch (error) { 59 | return undefined; 60 | } 61 | }).filter(Boolean) as Pattern[]; 62 | 63 | const runRegexes = languages.map(language => { 64 | try { 65 | return { 66 | language: language, 67 | type: PatternType.Run, 68 | regex: LanguageRegistry.getPlugin(language).getCodeLensRunRegex() 69 | }; 70 | } catch (error) { 71 | return undefined; 72 | } 73 | }).filter(Boolean) as Pattern[]; 74 | 75 | this.regexPatterns.push(...testRegexes); 76 | this.regexPatterns.push(...runRegexes); 77 | } 78 | 79 | public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] { 80 | // Match the correct regex based on the document's language 81 | const codeLenses: vscode.CodeLens[] = []; 82 | 83 | this.regexPatterns 84 | .filter(pattern => pattern.language === document.languageId) 85 | .forEach(pattern => { 86 | const lenses = this.processRegexPattern(document, pattern, token); 87 | codeLenses.push(...lenses); 88 | }); 89 | 90 | return codeLenses; 91 | } 92 | 93 | private processRegexPattern(document: vscode.TextDocument, pattern: Pattern, _token: vscode.CancellationToken): vscode.CodeLens[] { 94 | const text = document.getText(); 95 | const language = document.languageId; // Detect the language of the document 96 | const codeLenses: vscode.CodeLens[] = []; 97 | 98 | const extensionName = ExtensionUtils.getExtensionName(this.context); 99 | const extensionDisplayName = ExtensionUtils.getExtensionDisplayName(this.context); 100 | 101 | const { regex } = pattern; 102 | let match; 103 | 104 | 105 | let action: BazelAction = 'run'; 106 | if (pattern.type === PatternType.Run) { 107 | action = 'run'; 108 | } else if (pattern.type === PatternType.Test) { 109 | action = 'test'; 110 | } 111 | 112 | while ((match = regex.exec(text)) !== null) { 113 | let functionName = ''; 114 | if (language === 'cpp' || language === 'c') { 115 | // Combine FixtureName and TestName for C++/C tests 116 | const fixtureName = match[2]; 117 | const testName = match[3]; 118 | functionName = `${fixtureName}.${testName}`; 119 | } else { 120 | // Use captured test function name for other languages 121 | functionName = match[1] || match[2]; 122 | } 123 | const line = document.lineAt(document.positionAt(match.index).line); 124 | const targets = BazelService.extractBazelTargetsAssociatedWithSourceFile(document.fileName); 125 | 126 | if (targets.length === 0) { 127 | continue; 128 | } 129 | 130 | Console.info(`Installing code lens provider for ${action} on ${functionName}...`); 131 | 132 | const realTargets = targets.map(target => { 133 | return new BazelTarget(this.context, this.bazelService, target.label, target.bazelPath, target.buildPath, action, target.ruleType); 134 | }); 135 | 136 | const target = realTargets[0]; 137 | 138 | // Modify run arguments for the specific function 139 | if (pattern.type === PatternType.Test) { 140 | target.getBazelArgs().add(`--test_filter=${functionName}`); 141 | } 142 | codeLenses.push( 143 | new vscode.CodeLens(line.range, { 144 | title: `${extensionName} ${action}`, 145 | tooltip: extensionDisplayName, 146 | command: `${extensionName}.executeTarget`, 147 | arguments: [target], 148 | }) 149 | ); 150 | 151 | const debugTarget = target.clone(true); 152 | debugTarget.getBazelArgs().add('--compilation_mode=dbg'); 153 | 154 | codeLenses.push( 155 | new vscode.CodeLens(line.range, { 156 | title: `${extensionName} debug`, 157 | tooltip: extensionDisplayName, 158 | command: `${extensionName}.debugTarget`, 159 | arguments: [debugTarget], 160 | }) 161 | ); 162 | } 163 | return codeLenses; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/ui/progress.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import * as vscode from 'vscode'; 25 | 26 | function createSpinnerUpdater(updateCallback: (spinner: string) => void) { 27 | const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; 28 | let spinnerIndex = 0; 29 | let interval: NodeJS.Timeout; 30 | 31 | const start = () => { 32 | interval = setInterval(() => { 33 | const spinner = spinnerFrames[spinnerIndex]; 34 | updateCallback(spinner); 35 | spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length; 36 | }, 100); 37 | }; 38 | 39 | const stop = () => { 40 | if (interval) clearInterval(interval); 41 | }; 42 | 43 | return { start, stop }; 44 | } 45 | 46 | async function showProgressWindow( 47 | title: string, 48 | longMethodPromise: Promise, 49 | cancellationSource: vscode.CancellationTokenSource 50 | ): Promise { 51 | return new Promise((resolve, reject) => { 52 | vscode.window.withProgress( 53 | { 54 | location: vscode.ProgressLocation.Notification, 55 | title: title, 56 | cancellable: true, 57 | }, 58 | async (progress, cancellationToken) => { 59 | const disposable = cancellationToken.onCancellationRequested(() => { 60 | disposable.dispose(); 61 | cancellationSource.cancel(); // Propagate cancellation 62 | }); 63 | 64 | const spinnerUpdater = createSpinnerUpdater((spinner) => { 65 | progress.report({ increment: undefined, message: `${spinner}` }); 66 | }); 67 | 68 | try { 69 | spinnerUpdater.start(); 70 | const result = await longMethodPromise; 71 | spinnerUpdater.stop(); 72 | progress.report({ increment: undefined, message: 'Finished' }); 73 | resolve(result); 74 | } catch (error) { 75 | progress.report({ increment: undefined, message: 'Cancelled' }); 76 | reject(error); 77 | } finally { 78 | spinnerUpdater.stop(); 79 | } 80 | } 81 | ); 82 | }); 83 | } 84 | 85 | export async function showProgressStatus( 86 | title: string, 87 | longMethodPromise: Promise, 88 | cancellationSource: vscode.CancellationTokenSource 89 | ): Promise { 90 | const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); 91 | 92 | statusBarItem.text = `$(sync~spin) ${title}`; 93 | 94 | const cancelCommand = `${title}.cancel`; 95 | // Register the cancel command 96 | const disposable = vscode.commands.registerCommand(cancelCommand, () => { 97 | disposable.dispose(); 98 | cancellationSource.cancel(); // Trigger cancellation 99 | }); 100 | 101 | try { 102 | // Configure the status bar item 103 | statusBarItem.tooltip = 'Click to cancel'; 104 | statusBarItem.command = cancelCommand; 105 | statusBarItem.show(); 106 | 107 | // Await the long-running task 108 | const result = await longMethodPromise; 109 | 110 | statusBarItem.text = `${title} Completed`; 111 | setTimeout(() => statusBarItem.dispose(), 2000); 112 | 113 | return result; 114 | } catch (error) { 115 | statusBarItem.text = `${title} Cancelled`; 116 | setTimeout(() => statusBarItem.dispose(), 2000); 117 | throw error; 118 | } finally { 119 | statusBarItem.dispose(); 120 | disposable.dispose(); 121 | } 122 | } 123 | 124 | export async function showProgress( 125 | title: string, 126 | longMethod: (token: vscode.CancellationToken) => Promise, 127 | cancellationSource?: vscode.CancellationTokenSource, 128 | quiet = false 129 | ): Promise { 130 | 131 | if (cancellationSource === undefined) { 132 | cancellationSource = new vscode.CancellationTokenSource(); 133 | } 134 | 135 | const longMethodPromise = longMethod(cancellationSource.token); 136 | 137 | 138 | // Start both the status bar and progress window 139 | const statusPromise = showProgressStatus(title, longMethodPromise, cancellationSource); 140 | const promises = [statusPromise]; 141 | if (!quiet) { 142 | const progressWindowPromise = showProgressWindow(title, longMethodPromise, cancellationSource); 143 | promises.push(progressWindowPromise); 144 | } 145 | // Ensure that cancellation or completion of one cancels/cleans up the other 146 | try { 147 | const result = await Promise.all(promises); 148 | return result[0]; // Return the actual result 149 | } catch (error) { 150 | cancellationSource.cancel(); // Ensure both are canceled 151 | throw error; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/ui/quick-pick.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | 25 | import * as vscode from 'vscode'; 26 | 27 | 28 | // Overload signatures 29 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 30 | export function showSimpleQuickPick(quickPickData: string[], onChange: (data: any) => void): void; 31 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 32 | export function showSimpleQuickPick(loadQuickPickData: (cancellationToken: vscode.CancellationToken) => Promise, onChange: (data: any) => void, loadingLabel: string): void; 33 | 34 | // Actual implementation 35 | export function showSimpleQuickPick( 36 | quickPickDataOrLoader: string[] | ((cancellationToken: vscode.CancellationToken) => Promise), 37 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 38 | onChange: (data: any) => void, 39 | loadingLabel = 'Loading...' 40 | ): void { 41 | // Create the QuickPick with a loading message 42 | const quickPick = vscode.window.createQuickPick(); 43 | const cancellationTokenSource = new vscode.CancellationTokenSource(); 44 | quickPick.placeholder = loadingLabel; 45 | quickPick.items = [{ label: `$(sync~spin) ${loadingLabel}` }]; 46 | quickPick.ignoreFocusOut = true; 47 | 48 | // Show the QuickPick immediately 49 | quickPick.show(); 50 | 51 | // Determine if quickPickDataOrLoader is an array or a function returning a Promise 52 | let loadQuickPickData: Promise; 53 | if (Array.isArray(quickPickDataOrLoader)) { 54 | // If it's an array, wrap it in a resolved promise 55 | loadQuickPickData = Promise.resolve(quickPickDataOrLoader); 56 | } else { 57 | // Otherwise, it's a function, so call it to get the promise 58 | loadQuickPickData = quickPickDataOrLoader(cancellationTokenSource.token); 59 | } 60 | 61 | // Load the actual data asynchronously 62 | loadQuickPickData.then(quickPickData => { 63 | quickPick.placeholder = ''; 64 | // Once data is loaded, populate the QuickPick items 65 | const quickItems: vscode.QuickPickItem[] = [{ label: '' }]; 66 | quickPickData.forEach(arg => { 67 | if (arg !== undefined && arg.trim().length > 0) { 68 | quickItems.push({ label: arg }); 69 | } 70 | }); 71 | 72 | // Set the loaded items in the QuickPick 73 | quickPick.items = quickItems; 74 | 75 | // Handle value change (updating the first item with user input) 76 | quickPick.onDidChangeValue(value => { 77 | quickItems[0].label = value; 78 | quickPick.items = quickItems; 79 | }); 80 | 81 | // Handle selection change 82 | quickPick.onDidChangeSelection(items => { 83 | const item = items[0]; 84 | quickPick.value = item.label; 85 | quickPick.hide(); 86 | vscode.window.showInputBox({ value: item.label }).then(data => { 87 | if (data !== undefined) { 88 | onChange(data); 89 | } 90 | }); 91 | }); 92 | 93 | quickPick.onDidHide(() => { 94 | cancellationTokenSource.cancel(); 95 | cancellationTokenSource.dispose(); 96 | quickPick.dispose(); 97 | }); 98 | 99 | }).catch(err => { 100 | // Handle any errors in loading the data 101 | vscode.window.showErrorMessage(`Error loading data: ${err}`); 102 | quickPick.hide(); 103 | }); 104 | } 105 | -------------------------------------------------------------------------------- /src/ui/terminal.ts: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // MIT License 3 | // 4 | // Copyright (c) 2021-2024 NVIDIA Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////////// 24 | import * as vscode from 'vscode'; 25 | 26 | export async function clearTerminal() { 27 | await vscode.commands.executeCommand('workbench.action.terminal.clear'); 28 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2019", 5 | "outDir": "out", 6 | "lib": [ 7 | "ES2019" 8 | ], 9 | "sourceMap": true, 10 | "strict": true, 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | ".vscode-test" 15 | ] 16 | } 17 | --------------------------------------------------------------------------------