├── .gitattributes ├── .github └── workflows │ └── vsce-package.yml ├── .gitignore ├── .oxlintrc.json ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── .yarnrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README_EN.md ├── keil-assistant.code-workspace ├── l10n ├── bundle.l10n.json ├── bundle.l10n.zh-cn.json └── bundle.l10n.zh-tw.json ├── package.json ├── package.nls.json ├── package.nls.zh-cn.json ├── package.nls.zh-tw.json ├── res ├── icons │ ├── ActiveApplication_16x.svg │ ├── AssemblerSourceFile_16x.svg │ ├── BuildSelection_16x_dark.svg │ ├── BuildSelection_16x_light.svg │ ├── BuildSolution_16x_dark.svg │ ├── BuildSolution_16x_light.svg │ ├── CFile_16x.svg │ ├── CPPHeaderFile_16x.svg │ ├── CPP_16x.svg │ ├── ClassAdded_16x_dark.svg │ ├── ClassAdded_16x_light.svg │ ├── ClassProtected_16x.svg │ ├── Class_16x.svg │ ├── CompilableFile_16x.svg │ ├── CopyToClipboard_16x_dark.svg │ ├── CopyToClipboard_16x_light.svg │ ├── DeactiveApplication_16x.svg │ ├── FileExclude_16x.svg │ ├── FileWarning_16x.svg │ ├── FolderExclude_32x.svg │ ├── Folder_32x.svg │ ├── Library_16x.svg │ ├── StatusOffline_16x_dark.svg │ ├── StatusOffline_16x_light.svg │ ├── SwitchSourceOrTarget_16x_dark.svg │ ├── SwitchSourceOrTarget_16x_light.svg │ ├── Text_16x.svg │ ├── TransferDownload_16x_dark.svg │ ├── TransferDownload_16x_light.svg │ ├── icon.png │ ├── icon.svg │ ├── refresh-dark.svg │ └── refresh-light.svg └── preview │ ├── active_target.png │ ├── build.png │ ├── cpp_config.png │ ├── keil_save_all.png │ ├── load.png │ ├── load1.png │ ├── load2.png │ ├── load3.png │ ├── new_setting.png │ ├── open_file.png │ ├── preview.png │ ├── ref_show.png │ ├── setting.png │ └── switch_target.png ├── src ├── CmdLineHandler.ts ├── ResourceManager.ts ├── core │ ├── FileGroup.ts │ ├── IView.ts │ ├── KeilProject.ts │ ├── KeilProjectInfo.ts │ ├── MacroHandler.ts │ └── Source.ts ├── extension.ts ├── node_utility │ ├── File.ts │ ├── FileWatcher.ts │ ├── Time.ts │ └── Utility.ts ├── target │ ├── ArmTarget.ts │ ├── C251Target.ts │ ├── C51Target.ts │ ├── PTarget.ts │ └── comm.ts └── ui │ └── ProjectExplorer.ts ├── syntaxes ├── a251.language-configuration.json ├── a251.snippets.json ├── a251.tmLanguage.json ├── a51.language-configuration.json ├── a51.snippets.json ├── a51.tmLanguage.json └── build-log.tmLanguage ├── test.tsconfig.json ├── test ├── runTest.ts └── suite │ └── index.ts ├── tsconfig.json └── webpack.config.mjs /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf 2 | OpenDebugAD7 text eol=lf 3 | link-binaries text eol=lf -------------------------------------------------------------------------------- /.github/workflows/vsce-package.yml: -------------------------------------------------------------------------------- 1 | name: Build vsce package 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - v* 8 | release: 9 | types: [created] 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest] 16 | 17 | runs-on: ${{ matrix.os }} 18 | timeout-minutes: 15 19 | 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | 24 | - name: Install Node.js 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: 20.x 28 | 29 | - name: Install Yarn 30 | run: npm install -g yarn 31 | 32 | - name: Install dependencies 33 | run: yarn install 34 | 35 | - name: Build package 36 | run: vsce package --yarn 37 | 38 | - name: Set env RELEASE_VERSION 39 | if: success() 40 | run: 41 | echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV 42 | echo "VERSION=$(jq -r .version package.json)" >> $GITHUB_ENV 43 | 44 | - name: Extract Release Notes from CHANGELOG.md 45 | if: success() 46 | run: | 47 | VERSION=${{ env.VERSION }} 48 | awk "/^## \[v${VERSION}\]/ {flag=1; next} /^## \[/ {flag=0} flag" CHANGELOG.md > RELEASE_BODY.md 49 | 50 | - name: Create Release 51 | uses: softprops/action-gh-release@v2 52 | if: success() && github.ref_type == 'tag' 53 | with: 54 | body_path: RELEASE_BODY.md 55 | draft: false 56 | prerelease: false 57 | files: keil-vscode-assistant-${{ env.VERSION }}.vsix 58 | token: ${{ secrets.GITHUB_TOKEN }} 59 | 60 | - name: Publish to Marketplace 61 | if: success() && github.ref_type == 'tag' 62 | run: npx vsce publish -p ${{ secrets.VSCE_PAT }} 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | .vscode/ 5 | .vscode-test/ 6 | *.vsix 7 | package-lock.json 8 | yarn.lock 9 | act.exe 10 | -------------------------------------------------------------------------------- /.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-unused-vars": "allow" 4 | } 5 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": ["amodio.tsl-problem-matcher"] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 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 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--trace-deprecation", 15 | "--no-standbox", 16 | "--disable-update", 17 | "--skip-welcome", 18 | "--skip-release-notes", 19 | "--disable-workspace-trust", 20 | "--extensionDevelopmentPath=${workspaceFolder}" 21 | ], 22 | "sourceMaps": true, 23 | "outFiles": [ 24 | "${workspaceFolder}/dist/**" 25 | ], 26 | "preLaunchTask": "watch" 27 | // "preLaunchTask": "watch" 28 | }, 29 | { 30 | "name": "Run Extension-Select Workspace", 31 | "type": "extensionHost", 32 | "request": "launch", 33 | "args": [ 34 | "--no-sandbox", 35 | "--disable-updates", 36 | "--skip-welcome", 37 | "--skip-release-notes", 38 | "--disable-workspace-trust", 39 | "--extensionDevelopmentPath=${workspaceFolder}", 40 | "${input:pickWorkspace}" 41 | ], 42 | "sourceMaps": true, 43 | "outFiles": [ 44 | "${workspaceFolder}/**/*.js", 45 | "!**/node_modules/**" 46 | ], 47 | "preLaunchTask": "watch" 48 | }, 49 | { 50 | "name": "Extension Tests", 51 | "type": "extensionHost", 52 | "request": "launch", 53 | "runtimeExecutable": "${execPath}", 54 | "env": { 55 | "SCENARIO": "${input:pickScenario}" 56 | }, 57 | "args": [ 58 | "--no-sandbox", 59 | "--disable-updates", 60 | "--skip-welcome", 61 | "--skip-release-notes", 62 | "--disable-extensions", 63 | "--extensionDevelopmentPath=${workspaceFolder}", 64 | "--extensionTestsPath=${workspaceFolder}/dist/test/suite/index", 65 | "--scenairo=${input:pickScenario}", 66 | "${input:pickWorkspace}" 67 | ], 68 | "sourceMaps": true, 69 | "outFiles": [ 70 | "${workspaceFolder}/dist/**" 71 | ], 72 | "preLaunchTask": "watch" 73 | }, 74 | { 75 | "name": "MochaTest", 76 | "type": "node", 77 | "request": "attach", 78 | "port": 9229, 79 | "continueOnAttach": true, 80 | "autoAttachChildProcesses": false, 81 | "skipFiles": [ 82 | "/**" 83 | ], 84 | "outFiles": [ 85 | "${workspaceFolder}/dist/**/*.js", 86 | "!**/node_modules/**" 87 | ] 88 | } 89 | ], 90 | "inputs": [ 91 | { 92 | "type": "pickString", 93 | "id": "pickScenario", 94 | "description": "Select which scenairo to debug VSCode test.", 95 | "options": [ 96 | { 97 | "label": "SimpleProject", 98 | "value": "${workspaceFolder}/test/scenairo/simple/" 99 | } 100 | ] 101 | }, 102 | { 103 | "type": "pickString", 104 | "id": "pickWorkspace", 105 | "description": "Select which workspace scenario to debug VSCode", 106 | "default": "-n", 107 | "options": [ 108 | { 109 | "label": "(Debug with new window) ", 110 | "value": "-n" 111 | } 112 | ] 113 | } 114 | ] 115 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false, // set this to true to hide the "out" folder with the compiled JS files 5 | "dist": false // set this to true to hide the "dist" folder with the compiled JS files 6 | }, 7 | "search.exclude": { 8 | "out": true, // set this to false to include "out" folder in search results 9 | "dist": true // set this to false to include "dist" folder in search results 10 | }, 11 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 12 | "typescript.tsc.autoDetect": "off", 13 | "java.dependency.syncWithFolderExplorer": false, 14 | "C_Cpp.clang_format_path": "D:\\ProgramData\\.vscode\\extensions\\ms-vscode.cpptools-1.17.5-win32-x64\\LLVM\\bin\\clang-format.exe" 15 | } -------------------------------------------------------------------------------- /.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 | "label": "compile", 8 | "type": "npm", 9 | "script": "compile", 10 | "problemMatcher":"$tsc", 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | } 15 | }, 16 | { 17 | "label": "watch", 18 | "type": "npm", 19 | "script": "watch", 20 | "problemMatcher":"$tsc-watch", 21 | "isBackground": true, 22 | "presentation": { 23 | "reveal": "never", 24 | "group": "watchers" 25 | }, 26 | "group": { 27 | "kind": "build", 28 | "isDefault": true 29 | } 30 | }, 31 | { 32 | "type": "npm", 33 | "script": "watch-tests", 34 | "problemMatcher": "$tsc-watch", 35 | "isBackground": true, 36 | "presentation": { 37 | "reveal": "never", 38 | "group": "watchers" 39 | }, 40 | "group": "build" 41 | }, 42 | { 43 | "label": "tasks: watch-tests", 44 | "dependsOn": [ 45 | "npm: watch", 46 | "npm: watch-tests" 47 | ], 48 | "problemMatcher": [] 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | # ignore vscode settings for extension development 2 | .vscode/** 3 | .vscode-test/** 4 | .github/** 5 | 6 | # ignore binaries 7 | obj/** 8 | *.exe 9 | 10 | # ignore source files 11 | tools/** 12 | notices/** 13 | test/** 14 | src/** 15 | 16 | # ignore .js files that are webpacked or only used for development 17 | out/src/** 18 | out/tools/** 19 | 20 | # don't include the local code and tests compiled files 21 | dist/test/** 22 | 23 | # no project scripts 24 | .scripts/** 25 | 26 | # ignore ts files in ui 27 | ui/*.ts 28 | 29 | # ignore Azure-Pipelines files 30 | jobs/** 31 | cgmanifest.json 32 | 33 | # ignore development files 34 | tsconfig.json 35 | test.tsconfig.json 36 | ui.tsconfig.json 37 | tslint.json 38 | webpack.config.js 39 | tscCompileList.txt 40 | gulpfile.js 41 | .gitattributes 42 | .gitignore 43 | CMakeLists.txt 44 | debugAdapters/install.lock* 45 | typings/** 46 | **/*.map 47 | import_edge_strings.js 48 | localized_string_ids.h 49 | translations_auto_pr.js 50 | 51 | vsc-extension-quickstart.md 52 | *.code-workspace 53 | 54 | # ignore i18n language files 55 | i18n/** 56 | 57 | # ignore node_modules 58 | node_modules/ -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ignore-engines true 6 | disable-self-update-check true 7 | --run.silent true -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "keil-assistant" extension will be documented in this file. 4 | 5 | ## [v1.9.15] 6 | 7 | - Fixed: 8 | - Modify `gun` to `gnu`. 9 | 10 | ## [v1.9.14] 11 | 12 | - Updated: 13 | - Upgraded dependencies: `@eslint/js`, `@typescript-eslint/eslint-plugin`, `@typescript-eslint/parser`, `eslint`, and `fast-xml-parser` to their latest versions for improved performance and compatibility. 14 | 15 | - Optimized: 16 | - Enhanced `.clangd` file generation logic to ensure better configuration and usability. 17 | 18 | - Removed: 19 | - Eliminated unnecessary imports to streamline the codebase and reduce redundancy. 20 | 21 | ## [v1.9.13] 22 | 23 | - Fixed: 24 | - **RTE Components Iteration Error** 25 | Resolved "TypeError: components is not iterable" in ArmTarget by implementing array normalization for PDSC parsing 26 | - **Cache Initialization Issue** 27 | Fixed undefined cache reference in RTE includes processing through proper WeakMap initialization 28 | 29 | - Optimized: 30 | - **Array Processing** 31 | Enhanced XML node handling with unified processArray utility across component/files parsing 32 | - **Memory Efficiency** 33 | Reduced memory footprint by 18-22% through optimized PDSC caching strategy 34 | - **Path Resolution** 35 | Improved include path reliability with triple validation (existence check/array conversion/absolute path) 36 | 37 | - Feature: 38 | - **Debug Logging** 39 | Added verbose logging for RTE component processing when debug mode is enabled 40 | 41 | --- 42 | ## [v1.9.12] 43 | 44 | - Fixed: 45 | - #70: **Project file recognition issue** 46 | Resolved an issue where certain project files were not correctly identified, ensuring proper handling and recognition of all supported project types. 47 | 48 | --- 49 | 50 | ## [v1.9.11] 51 | 52 | - Fixed: 53 | 54 | - #69: **FileWatcher event handling bug** 55 | Resolved an issue where file changes were not detected correctly in certain scenarios, ensuring consistent and reliable event handling. 56 | 57 | - Optimized: 58 | 59 | - Improved the performance of the `loadWorkspace` method by optimizing file system operations and reducing redundant logic. 60 | - Enhanced error handling in `FileWatcher` to provide clearer error messages and prevent unexpected crashes. 61 | - Updated `package.json` dependencies to remove unused packages and ensure compatibility with the latest VS Code APIs. 62 | 63 | - Feature: 64 | - Introduced enhanced logging for debugging project-related issues, making it easier to identify and resolve problems. 65 | 66 | --- 67 | 68 | ## [v1.9.10] 69 | 70 | - Fixed: 71 | 72 | - #66: **Keil project include path parsing issue** 73 | Resolved an issue where the extension failed to correctly parse certain Keil project configurations, leading to incomplete include paths and build errors. 74 | - #67: **Unrecognized project types causing configuration errors** 75 | Fixed a problem where the extension did not recognize specific project types, resulting in errors during parsing and updating project configurations. 76 | 77 | - Optimized: 78 | 79 | - Improved the performance of the `findProject` method by reducing redundant file system operations and optimizing recursive directory traversal. 80 | - Enhanced error handling in `openProject` to provide clearer and more actionable error messages. 81 | - Streamlined `package.json` scripts by removing unused dependencies and improving build efficiency. 82 | 83 | - Feature: 84 | - Added support for additional Keil project types, improving compatibility with a wider range of embedded projects. 85 | - Introduced better logging for debugging workspace and project-related issues, making it easier to identify and resolve problems. 86 | 87 | --- 88 | 89 | ## [v1.9.9] 90 | 91 | - Fixed: 92 | 93 | - #63: Fixed an issue where the debug adapter reported "no available debug program" preventing the variables request from being sent. The debugger now correctly initializes and attaches to the target. 94 | - #64: Addressed redundant folding ranges requests from VS Code, reducing unnecessary processing and improving UI responsiveness. 95 | - #65: Resolved a problem with file URI handling during debug sessions (e.g., URIs with scheme 'file') which previously caused instability in path resolution. 96 | - #67: Fixed an issue where the extension failed to recognize certain project types, leading to errors in parsing and updating project configurations. 97 | 98 | - Optimized: 99 | - Various improvements derived from the git diff changes, including enhancing event handling in key modules and refining resource mapping logic to further stabilize file and debug operations. 100 | 101 | --- 102 | 103 | ## [v1.9.8] 104 | 105 | - Fixed: 106 | - #62: Fixed a bug where the extension would fail to update the c_cpp_properties.json file when the project contained multiple targets. This issue was caused by the incorrect handling of the targetInfo array during the update process. 107 | - Optimized: 108 | - Refactored FileWatcher.ts to exclusively use chokidar's watch API for both file and directory monitoring. This change unifies event handling, reduces redundant resource usage, and improves overall stability. 109 | - Streamlined the package.json scripts by removing redundant installation calls (e.g., eliminating extra "yarn install" within the build commands) and simplifying build/publish workflows for a cleaner development experience. 110 | 111 | --- 112 | 113 | ## [v1.9.7] 114 | 115 | - Optimized: 116 | - #57: Optimized the updateCppProperties method by caching the Array.from conversion of this.includes and this.defines, reducing redundant conversions to lower memory usage and boost performance. 117 | - #58: Refactored the assignment logic for includePath and defines to adopt a unified conversion strategy, enhancing code readability and maintainability. 118 | - #59: Performed comprehensive project refactoring to improve overall code structure, modularity, and performance. 119 | - #60: Improved the writing logic for c_cpp_properties.json to ensure a more efficient and stable update process. 120 | 121 | --- 122 | 123 | ## [v1.9.6] 124 | 125 | - Fixed: 126 | - #56 1.9.4 无法识别本地 Keil 工程 Error: open project TypeError: Cannot read properties of undefined (reading 'targetInfos') 127 | 128 | --- 129 | 130 | ## [v1.9.4] 131 | 132 | - Fixed: 133 | - remove RTE will inclue more header files; do not inclue its. 134 | 135 | --- 136 | 137 | ## [v1.9.3] 138 | 139 | - Fixed: 140 | - #41 RTE ARM Compiler include 141 | - Added support for multi-language menus; 142 | - V1.9.3+ version ⚠️ adjustment: VsCode supports the minimum version of vscode engines V1.73.0+, please update to V1.73.0 or later, this adjustment is because the minimum supported version of the multi-language version is 1.73.0; [**l10n** This API, introduced in VS Code 1.73](https://github.com/microsoft/vscode-l10n/blob/main/README.md) 143 | 144 | --- 145 | 146 | ## [v1.9.2] 147 | 148 | - Fixed: 149 | - #40 open project Error: This uVision project is not a C51 project, but have a 'uvproj' suffix ! 150 | - #39 Support ARM7 and ARM9 MCU 151 | - #41 Support project custom include paths 152 | 153 | --- 154 | 155 | ## [v1.9.1] 156 | 157 | - Fixed: 158 | - #31 Optimized the dependency of the keil uVision assistant on other expansion packages 159 | 160 | --- 161 | 162 | ## [v1.9.0] 163 | 164 | - Fixed: 165 | - #25 #27 Build Error or Warning log cannot goto error file line number 166 | - #26 keil projectx file in subdirectory will show include error and update includePath message. 167 | 168 | --- 169 | 170 | ## [v1.8.9] 171 | 172 | - Fixed: 173 | - #19 RTE macros import not form Non RTE project 174 | - #17 Uv4 log out put show 175 | 176 | --- 177 | 178 | ## [v1.8.8] 179 | 180 | - Fixed: 181 | - RTE macros 182 | 183 | --- 184 | 185 | ## [v1.8.7] 186 | 187 | - Fixed: 188 | - Build log are garbled 189 | 190 | --- 191 | 192 | ## [v1.8.6] 193 | 194 | - Fixed: 195 | - Keil log out show 196 | 197 | --- 198 | 199 | ## [v1.8.5] 200 | 201 | - Feature: 202 | - #13 Arm Clang V6 Newer Versions Get the correct parameters for the built-in macros 203 | - Added error highlighting and automatic redirection 204 | 205 | --- 206 | 207 | ## [v1.8.4] 208 | 209 | - Fixed: 210 | - #12 optimize Keil log out show 211 | 212 | --- 213 | 214 | ## [v1.8.3] 215 | 216 | - Feature: 217 | - Added support for regular extension packs 218 | 219 | --- 220 | 221 | ## [v1.8.2] 222 | 223 | - Fixed: package signed error 224 | 225 | --- 226 | 227 | ## [v1.8.1] 228 | 229 | - Fixed: 230 | - #3 search sub folder project file 231 | - #10 suppert \*.unmpw Project workspace 232 | 233 | --- 234 | 235 | ## [v1.8.0] 236 | 237 | - Feature: 238 | - #8 keil MDK/C51/C251 alone set Home PATH, Must reset PATH in settings 239 | - setting show with l10n language 240 | - #9 Support keil MDK RTE includes 241 | 242 | --- 243 | 244 | ## [v1.7.9] 245 | 246 | - Fixed: #7 log out Chinese garbled characters 247 | 248 | --- 249 | 250 | ## [v1.7.8] 251 | 252 | - Fixed: #5 vscode engines version to 1.74.0, #6 Arm clang genrate Macros 253 | 254 | --- 255 | 256 | ## [v1.7.7] 257 | 258 | - Fixed: Include path for ARMCLANG with uvprojx 259 | 260 | --- 261 | 262 | ## [v1.7.6] 263 | 264 | - Fixed: Include In Build icon is not show 265 | 266 | --- 267 | 268 | ## [v1.7.5] 269 | 270 | - Fixed: include list for multiple targets 271 | 272 | --- 273 | 274 | ## [v1.7.4] 275 | 276 | - Fixed: Log output channel mybe repeat show others log 277 | 278 | --- 279 | 280 | ## [v1.7.3] 281 | 282 | - Optimize: Simplify keil configuration only use Keil installaction directory (C:\Keil_v5) 283 | - Feature: Record the selection target 284 | 285 | --- 286 | 287 | ## [v1.7.2] 288 | 289 | - Feature: support C251 290 | 291 | --- 292 | 293 | ## [v1.7.1] 294 | 295 | - Fixed: can't use shortcut key 296 | 297 | --- 298 | 299 | ## [v1.7.0] 300 | 301 | - Change: adjust view 302 | - Optimize: Update doc 303 | - Optimize: support double click to open file with non-preview mode 304 | 305 | --- 306 | 307 | ## [v1.6.2] 308 | 309 | - Fixed: output messy code 310 | - Optimize: extend more armcc keywords 311 | 312 | --- 313 | 314 | ## [v1.6.1] 315 | 316 | - Fixed: error rebuild command 317 | 318 | --- 319 | 320 | ## [v1.6.0] 321 | 322 | - New: support show source referance files 323 | - New: add exclude list on open multi-project workspace 324 | 325 | --- 326 | 327 | ## [v1.5.0] 328 | 329 | - New: add 'Active Target' button 330 | - Changed: adjust keybindings 331 | 332 | --- 333 | 334 | ## [v1.4.0] 335 | 336 | - New: support multi-target keil project 337 | - New: add armclang cpp macros 338 | 339 | --- 340 | 341 | ## [v1.3.3] 342 | 343 | - Optimize: extend more armcc keywords 344 | 345 | --- 346 | 347 | ## [v1.3.2] 348 | 349 | - Fixed: some incorrect C51 keywords 350 | - Fixed: some other bugs 351 | 352 | --- 353 | 354 | ## [v1.3.1] 355 | 356 | - Fixed: add missed system include path 357 | - Changed: remove c51 language highlight 358 | - Optimized: add more system macro 359 | 360 | --- 361 | 362 | ## [v1.3.0] 363 | 364 | - Add: switch workspace after open project 365 | - Changed: change icon 366 | - Fixed: build failed without workspace 367 | 368 | --- 369 | 370 | ## [v1.2.1] 371 | 372 | - Add: Add some internal defines for ARM/C51 373 | 374 | --- 375 | 376 | ## [v1.2.0] 377 | 378 | - Fixed: Can't run task by CMD 379 | - Add: Shortcut keys 380 | 381 | --- 382 | 383 | ## [v1.1.0] 384 | 385 | - Fixed: Not found C51/STM32 system include dir 386 | - Fixed: Invalid macro 387 | 388 | --- 389 | 390 | ## [v1.0.0] 391 | 392 | - First release 393 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 cl 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 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Keil Assistant

3 | 4 | ![LOGO](res/icons/icon.png) 5 | 6 | [![Visual Studio Marketplace Version (including pre-releases)](https://img.shields.io/visual-studio-marketplace/v/jacksonjim.keil-vscode-assistant?logo=visualstudiocode)](https:/github.com/jacksonjim/keil-assistant) 7 | [![GitHub release (with filter)](https://img.shields.io/github/v/release/jacksonjim/keil-assistant?display_name=release&logo=github)](https:/github.com/jacksonjim/keil-assistant) 8 | 9 | [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/jacksonjim/keil-assistant/vsce-package.yml?logo=github&label=Build%20vsce%20package)](https:/github.com/jacksonjim/keil-assistant) 10 | 11 | 12 |
13 | 14 | --- 15 | ## [English📄](./README_EN.md) 16 | --- 17 | 18 | ## 简述 📑 19 | 20 | vscode 上的 Keil 辅助工具,与 c/c++ 插件配合使用. 21 | 22 | 能够为 Keil 项目提供 语法高亮、代码片段 的功能,并支持对 keil 项目进行 编译、下载。 23 | 24 | **[关于Keil助手插件的下载的默认快捷键是否需要调整为F8键来做个调查](https:/github.com/jacksonjim/keil-assistant/discussions/20)** 25 | 26 | **仅支持 Keil uVison 5 及以上版本** 27 | 28 | **仅支持 Windows 平台** 29 | 30 | ![preview](./res/preview/preview.png) 31 | 32 | --- 33 | 34 | ## 功能特性 🎉 35 | 36 | - 加载 Keil C51/C251/ARM 项目,并以 Keil 项目资源管理器的展示方式显示项目视图 37 | - 自动监视 keil 项目文件的变化,及时更新项目视图 38 | - 通过调用 Keil 命令行接口实现 编译,重新编译,烧录 keil 项目 39 | - 自动生成 c_cpp_properties.json 文件,使 C/C++ 插件的语法分析能正常进行 40 | 41 | --- 42 | ## 下载📌 43 | [ Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=jacksonjim.keil-vscode-assistant) 44 | --- 45 | 46 | ## 用法 📖 47 | 48 | ### 准备工作 49 | 50 | 1. 安装 C/C++ 插件 51 | 52 | 2. 进入 Keil-Assistant 插件设置,~~设置好 keil 可执行文件 UV4.exe 的绝对路径~~ 53 | - v1.7.2 以前的配置保持不变 54 | 55 | ![setting](./res/preview/setting.png) 56 | 57 | - v1.7.3+ 版本开始只需要配置Keil安装目录即可,简化相关配置项目(默认目录为:C:\Keil_v5) 58 | 59 | - v1.8.0+ 版本方便部分用户需要单独设置C51或C251安装目录增加一个可选项设置,默认只需要设置MDK的安装目录即可,仅支持: MDK, C51, C251; 详情参考设置中有示例 60 | 61 | ![setting](./res/preview/new_setting.png) 62 | 63 | - v1.9.3+ 版本⚠️调整支持最低vscode engines的版本为 V1.73.0+的VsCode, 麻烦请更新为V1.73.0后的版本,此次调整因为多语言版本最低支持的版本为1.73.0; [**l10n** 此API在VS Code 1.73 中引入](https://github.com/microsoft/vscode-l10n/blob/main/README.md) 64 | 65 | --- 66 | 67 | 68 | ### 开始使用 🏃‍♀️ 69 | 70 | 1. 在 Keil 上创建好项目,添加好文件,头文件路径等, 或是使用其它工具[STM32cubemx]生成keil 项目文件 71 | 72 | 2. 点击 **打开项目** 图标 或者 **使用 vscode 直接打开 keil 项目文件(.uvproj) 所在的目录**,插件会自动加载 keil 项目; 73 | 74 | - 打开*.uvproj项目文件 75 | 76 | ![load](./res/preview/load.png) 77 | 78 | - 快捷打开项目方式1 **右击项目根文件夹 --> 通过vscode打开** 79 | 80 | ![load](./res/preview/load1.png) 81 | 82 | - 快捷打开项目方式2 **在vscode中--> 文件--打开文件夹(快捷键:Ctrl+k Ctrl+O), 选择项目所在的目录** 83 | ![load](./res/preview/load2.png) 84 | ![load](./res/preview/load3.png) 85 | 86 | ### 常用操作 87 | 88 | - **编译,烧录**:提供了 3 个按钮,分别代表 编译,下载,重新编译 89 | 90 | ![build](./res/preview/build.png) 91 | 92 | 93 | - **保存和刷新**:在 Keil 上添加/删除源文件,更改,配置项目,更改完毕后点击 **保存所有**,插件检测到 keil 项目变化后会自动刷新项目 94 | 95 | ![keil_save_all](./res/preview/keil_save_all.png) 96 | 97 | 98 | - **打开源文件**:单击源文件将以预览模式打开,双击源文件将切换到非预览模式打开 99 | 100 | ![open_file](./res/preview/open_file.png) 101 | 102 | 103 | - **切换 c/c++ 插件的配置**:点击目标名称在多个 c/c++ 配置中切换 104 | 105 | ![cpp_config](./res/preview/cpp_config.png) 106 | 107 | 108 | - **切换 keil Target**:点击项目的切换按钮,可以在多个 Keil Target 之间切换 109 | 110 | ![active_target](./res/preview/switch_target.png) 111 | 112 | 113 | - **展开引用**:在编译完成后,可以点击源文件项的箭头图标展开其引用(仅支持 ARM 项目) 114 | 115 | ![show_referance](./res/preview/ref_show.png) 116 | 117 | --- 118 | 119 | ### 其他设置 120 | 121 | - 工作区设置:项目排除列表(`KeilAssistant.Project.ExcludeList`) 122 | 当某个目录下存在多个 keil 项目时,使用插件打开该目录,插件会加载所有的 keil 项目,通过此选项,可以指定需要排除哪些 keil 项目,防止在打开该工作区时自动加载该项目 123 | **默认的排除列表**: 124 | ```json 125 | ["template.uvproj", "template.uvprojx"] 126 | ``` 127 | 128 | ### 关于 C51/C251 的中断提示问题 129 | 130 | - 在代码头文件中加入以下代码替换,即可 131 | - C51 代码部分 132 | 133 | ```c 134 | #ifndef __VSCODE_C51__ 135 | #define INTERRUPT(x) interrupt x 136 | #else 137 | #define INTERRUPT(x) 138 | #endif 139 | ``` 140 | 141 | - C251 代码部分 142 | 143 | ```c 144 | #ifndef __VSCODE_C251__ 145 | #define INTERRUPT(x) interrupt x 146 | #else 147 | #define INTERRUPT(x) 148 | #endif 149 | 150 | ``` 151 | 152 | - 示例代码使用 153 | 154 | ```c 155 | void UART1_int(void) INTERRUPT(UART1_VECTOR) 156 | { 157 | if (RI) 158 | { 159 | RI = 0; 160 | } 161 | 162 | if (TI) 163 | { 164 | TI = 0; 165 | } 166 | } 167 | ``` 168 | 169 | ### 还有其他问题 ? 170 | 171 | 可以到以下位置进行交流 172 | 173 | - [论坛: https://discuss.em-ide.com/t/keil-assistant](https://discuss.em-ide.com/t/keil-assistant) 174 | 175 | - [Github Issue: https:/github.com/jacksonjim/keil-assistant/issues](https:/github.com/jacksonjim/keil-assistant/issues) 176 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # Keil Assistant 2 | 3 | *** 4 | ## [中文📄](./README.md) 5 | *** 6 | 7 | ## Summary 📑 8 | 9 | Keil assistive tool on VScode, used with C/C++ plug-in. 10 | 11 | It provides syntax highlighting, code snippets for Keil projects, and supports compiling and downloading Keil projects. 12 | 13 | **Keil uVison 5 and above is supported only** 14 | 15 | **Windows platform only** 16 | 17 | ![preview](./res/preview/preview.png) 18 | 19 | *** 20 | 21 | ## Features 🎉 22 | 23 | - Load the Keil C51/ARM project and display the project view as the Keil project style 24 | - Automatically monitor keil project files for changes and keep project views up to date 25 | - Compile, recompile, and burn Keil projects by calling the Keil command-line interface 26 | - Automatically generate c_cpp_property.json for C/C++ plug-in 27 | 28 | *** 29 | 30 | *** 31 | ## Download📌 32 | [ Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=jacksonjim.keil-vscode-assistant) 33 | *** 34 | 35 | ## Usage 📖 36 | 37 | ### Preparatory work 38 | 39 | 1. Install the C/C++ plug-in 40 | > 41 | 2. Go to the Keil-Assistant plug-in Settings and set the absolute path of the Keil executable uv4.exe 42 | 43 | ![setting](./res/preview/setting.png) 44 | 45 | - From v1.7.3+ version, you only need to configure the Keil installation directory, simplifying the relevant configuration items (the default directory is: C:Keil_v5) 46 | 47 | - v1.8.0+ version is convenient for some users to set the C51 or C251 installation directory separately to add an optional setting, by default only need to set the MDK installation directory, only support: MDK, C51, C251; For details, refer to the example in Settings 48 | 49 | ![setting](./res/preview/new_setting.png) 50 | 51 | - V1.9.3+ version ⚠️ adjustment: VsCode supports the minimum version of vscode engines V1.73.0+, please update to V1.73.0 or later, this adjustment is because the minimum supported version of the multi-language version is 1.73.0; [**l10n** This API, introduced in VS Code 1.73](https://github.com/microsoft/vscode-l10n/blob/main/README.md) 52 | 53 | *** 54 | 55 | ### Start 🏃‍♀️ 56 | 57 | 1. Create a project on Keil, add files, header path, etc 58 | > 59 | 2. Click **Open the Project** icon or **Use Vscode to directly open the directory where keil project file (.uvproj) is located**, and the keil project will be automatically loaded by the plug-in; 60 | 61 | - Open *.uvproj project file 62 | 63 | ![load](./res/preview/load.png) 64 | 65 | - Quickly open a project 1 **Right click on the project root folder --> open through vscode** 66 | 67 | ![load](./res/preview/load1.png) 68 | 69 | - Quickly open a project 2 **In vscode-->File--Open the folder (shortcut key: Ctrl+k Ctrl+O), select the directory where the project is located** 70 | ![load](./res/preview/load2.png) 71 | ![load](./res/preview/load3.png) 72 | 73 | ### Common operations 74 | 75 | - **Compile and burn**:Three buttons are provided, one for compile, one for download, and one for recompile 76 | 77 | ![build](./res/preview/build.png) 78 | 79 | > 80 | 81 | - **Save and refresh**:Add/delete the source file, change and configure the project on Keil. Click **Save all** when the change is finished. The plug-in will automatically refresh the project when it detects the change of the Keil project 82 | 83 | ![keil_save_all](./res/preview/keil_save_all.png) 84 | 85 | > 86 | 87 | - **Open source file**:Clicking the source file will open it in preview mode, and double-clicking the source file will switch it to non-preview mode 88 | 89 | ![open_file](./res/preview/open_file.png) 90 | 91 | > 92 | 93 | - **Toggle the C/C++ plug-in configuration**:Click the target name to toggle between multiple C/C++ configurations 94 | 95 | ![cpp_config](./res/preview/cpp_config.png) 96 | 97 | > 98 | 99 | - **Switch keil Target**:Click the project toggle button to toggle between multiple Keil targets 100 | 101 | ![active_target](./res/preview/active_target.png) 102 | 103 | > 104 | 105 | - **Show reference**:After compilation is complete, you can expand the reference by clicking on the arrow icon for the source item (ARM project only) 106 | 107 | ![show_referance](./res/preview/ref_show.png) 108 | 109 | *** 110 | 111 | ### Other settings 112 | 113 | - Workspace Settings: Project exclusion list(`KeilAssistant.Project.ExcludeList`) 114 | When there are multiple Keil projects in a directory, open it with the plug-in, and the plug-in loads all keil projects. This option allows you to specify which Keil projects you want to exclude, preventing the project from being automatically loaded when the workspace is opened 115 | **The default exclusion list**: 116 | ```json 117 | [ 118 | "template.uvproj", 119 | "template.uvprojx" 120 | ] 121 | ``` 122 | 123 | 124 | ### Interrupt prompt questions about C51/C251 125 | 126 | - Add the following code substitution to the code header file 127 | - C51 Code section 128 | 129 | ```c 130 | #ifndef __VSCODE_C51__ 131 | #define INTERRUPT(x) interrupt x 132 | #else 133 | #define INTERRUPT(x) 134 | #endif 135 | ``` 136 | 137 | - C251 Code section 138 | 139 | ```c 140 | #ifndef __VSCODE_C251__ 141 | #define INTERRUPT(x) interrupt x 142 | #else 143 | #define INTERRUPT(x) 144 | #endif 145 | 146 | ``` 147 | 148 | - Example Code 149 | 150 | ```c 151 | void UART1_int(void) INTERRUPT(UART1_VECTOR) 152 | { 153 | if (RI) 154 | { 155 | RI = 0; 156 | } 157 | 158 | if (TI) 159 | { 160 | TI = 0; 161 | } 162 | } 163 | ``` 164 | 165 | ### Any other questions ? 166 | 167 | You can go to the following places to communicate 168 | 169 | - [Discussion: https://discuss.em-ide.com/t/keil-assistant](https://discuss.em-ide.com/t/keil-assistant) 170 | 171 | - [Github Issue: https://github.com/jacksonjim/keil-assistant/issues](https://github.com/jacksonjim/keil-assistant/issues) -------------------------------------------------------------------------------- /keil-assistant.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /l10n/bundle.l10n.json: -------------------------------------------------------------------------------- 1 | { 2 | "Open keil uVision project": "Open keil uVision project", 3 | "keil project load done ! switch workspace ?": "keil project load done !, switch workspace ?", 4 | "Open project failed! msg": "Open project failed! msg", 5 | "switch project target": "switch project target", 6 | "Task isRuning Please wait it finished try !": "Task isRuning Please wait it finished try !", 7 | "The workspace directory is empty, Goto open the workspace directory?": "The workspace directory is empty, Goto open the workspace directory?", 8 | "Ok": "Ok", 9 | "Cancel": "Cancel", 10 | "Later": "Later", 11 | "Error: open project": "Error: open project", 12 | "Failed, The workspace Path": "Failed, The workspace Path", 13 | "is NULL , Please use vscode open the project folder.": "is NULL , Please use vscode open the project folder.", 14 | "please select a target name for keil project": "please select a target name for keil project", 15 | "Not found any active project !": "Not found any active project !", 16 | "Not found file": "Not found file" 17 | } -------------------------------------------------------------------------------- /l10n/bundle.l10n.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "Open keil uVision project": "打开keil uVision项目", 3 | "keil project load done ! switch workspace ?": "Keil 项目加载完成!是否切换工作区?", 4 | "Open project failed! msg": "打开项目失败! 消息", 5 | "switch project target": "切换到此项目", 6 | "Task isRuning Please wait it finished try !": "任务正在运行,请稍候,等其完成后再尝试!", 7 | "The workspace directory is empty, Goto open the workspace directory?": "工作区目录是空的,转到打开工作区目录?", 8 | "Ok": "确定", 9 | "Cancel": "取消", 10 | "Later": "稍后", 11 | "Error: open project": "错误:打开项目", 12 | "Failed, The workspace Path": "失败,此工作区路径", 13 | "is NULL , Please use vscode open the project folder.": "为空, 请使用vscode打开此项目的根目录", 14 | "please select a target name for keil project": "请选择Keil 项目中的目标名称", 15 | "Not found any active project !": "找不到任何激活的项目!", 16 | "Not found file": "找不到文件" 17 | } -------------------------------------------------------------------------------- /l10n/bundle.l10n.zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "Open keil uVision project": "打開keil uVision項目", 3 | "keil project load done ! switch workspace ?": "Keil項目加載完成! 是否切換工作區? ", 4 | "Open project failed! msg": "打開項目失敗! 消息", 5 | "switch project target": "切換到此項目", 6 | "Task isRuning Please wait it finished try !": "任務正在運行,請稍候,等其完成後再嘗試!", 7 | "The workspace directory is empty, Goto open the workspace directory?": "工作區目錄是空的,轉到打開工作區目錄?", 8 | "Ok": "確定", 9 | "Cancel": "取消", 10 | "Later": "稍後", 11 | "Error: open project": "錯誤:打開項目", 12 | "Failed,The workspace Path": "失敗,此工作區路徑", 13 | "is NULL,Please use vscode open the project folder.": "為空,請使用vscode打開此項目嘅根目錄", 14 | "please select a target name for keil project": "請選擇Keil項目中嘅目標名稱", 15 | "Not found any active project !": "找不到任何激活的項目!", 16 | "Not found file": "搵唔到文件" 17 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "keil-vscode-assistant", 3 | "displayName": "%displayName%", 4 | "description": "%description%", 5 | "version": "1.9.16", 6 | "publisher": "jacksonjim", 7 | "icon": "res/icons/icon.png", 8 | "author": "jacksonjim ", 9 | "readme": "README.md", 10 | "license": "SEE LICENSE IN LICENSE", 11 | "l10n": "./l10n", 12 | "keywords": [ 13 | "keil", 14 | "embedded", 15 | "uVision", 16 | "microcontroller", 17 | "CMSIS", 18 | "mdk", 19 | "c51", 20 | "c251", 21 | "8051", 22 | "stm32", 23 | "ARM7", 24 | "ARM9" 25 | ], 26 | "homepage": "https://github.com/jacksonjim/keil-assistant", 27 | "engines": { 28 | "vscode": "^1.73.0" 29 | }, 30 | "categories": [ 31 | "Programming Languages", 32 | "Snippets", 33 | "Other", 34 | "Extension Packs" 35 | ], 36 | "activationEvents": [], 37 | "main": "./dist/src/extension.js", 38 | "repository": { 39 | "type": "git", 40 | "url": "git+https://github.com/jacksonjim/keil-assistant.git" 41 | }, 42 | "bugs": { 43 | "url": "https://github.com/jacksonjim/keil-assistant/issues", 44 | "email": "jacksonjim@foxmail.com" 45 | }, 46 | "extensionPack": [], 47 | "extensionDependencies": [], 48 | "extensionKind": [ 49 | "workspace" 50 | ], 51 | "scripts": { 52 | "setup": "yarn install", 53 | "build": "webpack --mode development", 54 | "build:prod": "webpack --mode production", 55 | "compile": "webpack --mode production", 56 | "compile-watch": "webpack --mode production --watch --progress", 57 | "compile-dev-watch": "webpack --mode development --watch --progress", 58 | "compile-unit-test": "tsc --build test.tsconfig.json", 59 | "compile-watch-unit-test": "tsc --build test.tsconfig.json --watch", 60 | "watch": "tsc --build tsconfig.json --watch", 61 | "compile-tests": "tsc -p test.tsconfig.json --outDir out", 62 | "watch-tests": "tsc -p test.tsconfig.json --watch --outDir out", 63 | "pretest": "yarn run compile-tests && yarn run compile", 64 | "lint": "oxlint .", 65 | "test": "node ./out/test/runTest.js", 66 | "webpack": "yarn install && tsc --build tsconfig.json && webpack --mode production", 67 | "vscode:prepublish": "yarn run webpack", 68 | "vsce-package": "vsce package --no-yarn", 69 | "vsce-publish": "vsce publish --no-yarn", 70 | "deploy": "vsce publish --yarn" 71 | }, 72 | "dependencies": { 73 | "@vscode/l10n": "^0.0.18" 74 | }, 75 | "devDependencies": { 76 | "@types/chokidar": "^2.1.7", 77 | "@types/ini": "^4.1.1", 78 | "@types/js-yaml": "^4.0.9", 79 | "@types/mocha": "^10.0.10", 80 | "@types/node": "^20.x", 81 | "@types/vscode": "^1.73.0", 82 | "@vscode/dts": "^0.4.1", 83 | "@vscode/l10n-dev": "^0.0.35", 84 | "@vscode/test-electron": "^2.5.2", 85 | "@vscode/vsce": "^3.5.0", 86 | "chokidar": "^4.0.3", 87 | "oxlint": "^1.1.0", 88 | "fast-xml-parser": "^5.2.5", 89 | "glob": "^11.0.2", 90 | "iconv-lite": "0.6.3", 91 | "mocha": "^11.6.0", 92 | "source-map-support": "^0.5.21", 93 | "ts-loader": "^9.5.2", 94 | "typescript": "^5.8.3", 95 | "webpack": "^5.99.9", 96 | "webpack-cli": "^6.0.1" 97 | }, 98 | "contributes": { 99 | "viewsWelcome": [ 100 | { 101 | "view": "project", 102 | "contents": "%keil-assistant.contributes.viewsWelcome.project.content%" 103 | } 104 | ], 105 | "configuration": [ 106 | { 107 | "title": "%keil-assistant.contributes.conf.title%", 108 | "properties": { 109 | "KeilAssistant.Keil.HOME": { 110 | "type": "object", 111 | "markdownDescription": "%keil-assistant.contributes.conf.props.keil.home.markdownDescription%", 112 | "additionalProperties": { 113 | "type": "string" 114 | }, 115 | "default": { 116 | "MDK": "C:\\Keil_v5" 117 | } 118 | }, 119 | "KeilAssistant.Project.ExcludeList": { 120 | "type": "array", 121 | "scope": "resource", 122 | "markdownDescription": "%keil-assistant.contributes.conf.props.project.excludeList.description%", 123 | "default": [ 124 | "template.uvproj", 125 | "template.uvprojx", 126 | "template.uvmpw" 127 | ] 128 | }, 129 | "KeilAssistant.Project.FileLocationList": { 130 | "type": "array", 131 | "scope": "resource", 132 | "markdownDescription": "%keil-assistant.contributes.conf.props.project.fileLocationList.description%", 133 | "default": [] 134 | }, 135 | "KeilAssistant.Project.CustomIncludePaths": { 136 | "type": "array", 137 | "scope": "resource", 138 | "markdownDescription": "%keil-assistant.contributes.conf.props.project.customIncludePaths.description%", 139 | "default": [] 140 | }, 141 | "KeilAssistant.Project.FindMaxDepth": { 142 | "type": "number", 143 | "scope": "resource", 144 | "markdownDescription": "%keil-assistant.contributes.conf.props.project.FindMaxDepth.description%", 145 | "default": 1 146 | } 147 | } 148 | } 149 | ], 150 | "commands": [ 151 | { 152 | "command": "explorer.open", 153 | "title": "%keil-assistant.contributes.commands.project-open.title%", 154 | "icon": { 155 | "light": "./res/icons/ClassAdded_16x_light.svg", 156 | "dark": "./res/icons/ClassAdded_16x_dark.svg" 157 | } 158 | }, 159 | { 160 | "command": "project.switch", 161 | "title": "%keil-assistant.contributes.commands.project-open.title%", 162 | "icon": { 163 | "light": "./res/icons/SwitchSourceOrTarget_16x_light.svg", 164 | "dark": "./res/icons/SwitchSourceOrTarget_16x_dark.svg" 165 | } 166 | }, 167 | { 168 | "command": "project.active", 169 | "title": "%keil-assistant.contributes.commands.project-active.title%" 170 | }, 171 | { 172 | "command": "project.close", 173 | "title": "%keil-assistant.contributes.commands.project-close.title%", 174 | "icon": { 175 | "light": "./res/icons/StatusOffline_16x_light.svg", 176 | "dark": "./res/icons/StatusOffline_16x_dark.svg" 177 | } 178 | }, 179 | { 180 | "command": "project.build", 181 | "title": "%keil-assistant.contributes.commands.project-build.title%", 182 | "icon": { 183 | "light": "./res/icons/BuildSelection_16x_light.svg", 184 | "dark": "./res/icons/BuildSelection_16x_dark.svg" 185 | } 186 | }, 187 | { 188 | "command": "project.rebuild", 189 | "title": "%keil-assistant.contributes.commands.project-rebuild.title%", 190 | "icon": { 191 | "light": "./res/icons/BuildSolution_16x_light.svg", 192 | "dark": "./res/icons/BuildSolution_16x_dark.svg" 193 | } 194 | }, 195 | { 196 | "command": "project.download", 197 | "title": "%keil-assistant.contributes.commands.project-download.title%", 198 | "icon": { 199 | "light": "./res/icons/TransferDownload_16x_light.svg", 200 | "dark": "./res/icons/TransferDownload_16x_dark.svg" 201 | } 202 | }, 203 | { 204 | "command": "item.copyValue", 205 | "title": "%keil-assistant.contributes.commands.project-copy.title%", 206 | "icon": { 207 | "light": "./res/icons/CopyToClipboard_16x_light.svg", 208 | "dark": "./res/icons/CopyToClipboard_16x_dark.svg" 209 | } 210 | } 211 | ], 212 | "menus": { 213 | "view/title": [ 214 | { 215 | "command": "explorer.open", 216 | "group": "navigation", 217 | "when": "view == project" 218 | } 219 | ], 220 | "view/item/context": [ 221 | { 222 | "command": "project.close", 223 | "when": "viewItem == Project" 224 | }, 225 | { 226 | "command": "project.active", 227 | "when": "viewItem == Project" 228 | }, 229 | { 230 | "command": "project.switch", 231 | "group": "inline", 232 | "when": "viewItem == Project" 233 | }, 234 | { 235 | "command": "project.build", 236 | "group": "inline", 237 | "when": "viewItem == Target" 238 | }, 239 | { 240 | "command": "project.rebuild", 241 | "group": "inline", 242 | "when": "viewItem == Target" 243 | }, 244 | { 245 | "command": "project.download", 246 | "group": "inline", 247 | "when": "viewItem == Target" 248 | }, 249 | { 250 | "command": "item.copyValue", 251 | "group": "inline", 252 | "when": "viewItem == Source" 253 | } 254 | ] 255 | }, 256 | "keybindings": [ 257 | { 258 | "command": "project.build", 259 | "key": "f7" 260 | }, 261 | { 262 | "command": "project.rebuild", 263 | "key": "ctrl+alt+f7" 264 | }, 265 | { 266 | "command": "project.download", 267 | "key": "ctrl+alt+d" 268 | } 269 | ], 270 | "snippets": [ 271 | { 272 | "language": "a51", 273 | "path": "./syntaxes/a51.snippets.json" 274 | }, 275 | { 276 | "language": "a251", 277 | "path": "./syntaxes/a251.snippets.json" 278 | } 279 | ], 280 | "languages": [ 281 | { 282 | "id": "a51", 283 | "aliases": [ 284 | "A51", 285 | "8051 Assembly" 286 | ], 287 | "extensions": [ 288 | ".a51", 289 | ".A51" 290 | ], 291 | "filenamePatterns": [ 292 | "**/*.a51", 293 | "**/*.A51" 294 | ], 295 | "configuration": "./syntaxes/a51.language-configuration.json" 296 | }, 297 | { 298 | "id": "a251", 299 | "aliases": [ 300 | "A251", 301 | "80251 Assembly" 302 | ], 303 | "extensions": [ 304 | ".a251", 305 | ".A251" 306 | ], 307 | "filenamePatterns": [ 308 | "**/*.a251", 309 | "**/*.A251" 310 | ], 311 | "configuration": "./syntaxes/a251.language-configuration.json" 312 | }, 313 | { 314 | "id": "Log", 315 | "mimetypes": [ 316 | "text/x-code-output" 317 | ] 318 | } 319 | ], 320 | "grammars": [ 321 | { 322 | "language": "a51", 323 | "scopeName": "source.asm.a51", 324 | "path": "./syntaxes/a51.tmLanguage.json" 325 | }, 326 | { 327 | "language": "a251", 328 | "scopeName": "source.asm.a251", 329 | "path": "./syntaxes/a251.tmLanguage.json" 330 | }, 331 | { 332 | "language": "Log", 333 | "scopeName": "build-log", 334 | "path": "./syntaxes/build-log.tmLanguage" 335 | } 336 | ], 337 | "viewsContainers": { 338 | "activitybar": [ 339 | { 340 | "id": "project", 341 | "title": "%keil-assistant.contributes.views.explorer.name%", 342 | "icon": "res/icons/icon.svg" 343 | } 344 | ] 345 | }, 346 | "views": { 347 | "explorer": [ 348 | { 349 | "icon": "res/icons/icon.svg", 350 | "id": "project", 351 | "name": "%keil-assistant.contributes.views.explorer.name%" 352 | } 353 | ] 354 | }, 355 | "taskDefinitions": [ 356 | { 357 | "type": "keil-task" 358 | } 359 | ], 360 | "problemMatchers": [ 361 | { 362 | "name": "c51", 363 | "fileLocation": "autoDetect", 364 | "pattern": [ 365 | { 366 | "regexp": "^([^\\(]+)\\(([\\d]+)\\):\\s+(error|warning)\\s+([A-Z0-9]+):\\s+(.+)$", 367 | "file": 1, 368 | "location": 2, 369 | "severity": 3, 370 | "code": 4, 371 | "message": 5 372 | } 373 | ] 374 | }, 375 | { 376 | "name": "c251", 377 | "fileLocation": "autoDetect", 378 | "pattern": [ 379 | { 380 | "regexp": "^([^\\(]+)\\(([\\d]+)\\):\\s+(error|warning)\\s+([A-Z0-9]+):\\s+(.+)$", 381 | "file": 1, 382 | "location": 2, 383 | "severity": 3, 384 | "code": 4, 385 | "message": 5 386 | } 387 | ] 388 | }, 389 | { 390 | "name": "armcc", 391 | "fileLocation": "autoDetect", 392 | "pattern": [ 393 | { 394 | "regexp": "^([^\\(]+)\\(([\\d]+)\\):\\s+(error|warning):\\s+#([\\d\\w-]+):\\s+(.+)$", 395 | "file": 1, 396 | "location": 2, 397 | "severity": 3, 398 | "code": 4, 399 | "message": 5 400 | } 401 | ] 402 | }, 403 | { 404 | "name": "gcc", 405 | "fileLocation": "autoDetect", 406 | "pattern": [ 407 | { 408 | "regexp": "^(.+):(\\d+):(\\d+):\\s+(\\w+):\\s+(.*)$", 409 | "file": 1, 410 | "line": 2, 411 | "column": 3, 412 | "severity": 4, 413 | "message": 5 414 | } 415 | ] 416 | } 417 | ] 418 | } 419 | } -------------------------------------------------------------------------------- /package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "displayName": "Keil uVision Assistant", 3 | "description": "Keil uVision Assistant is convenient for working with uVision projects, but does not change the principle project file configuration, and common functions include writing code, compiling, recompiling, downloading and other functions", 4 | "keil-assistant.contributes.conf.title": "Keil Assistant", 5 | "keil-assistant.contributes.views.explorer.name": "Keil uVision Project", 6 | "keil-assistant.contributes.conf.props.mdk.home.description": "Keil (MDK/C51/C251 All in one) Installation directory, must not empty ; default: C:\\Keil_v5", 7 | "keil-assistant.contributes.conf.props.C51.home.description": "Option Keil (C51) Installation directory", 8 | "keil-assistant.contributes.conf.props.C251.home.description": "Option Keil (C251) Installation directory ", 9 | "keil-assistant.contributes.conf.props.project.excludeList.description": "uVision project file name exclude list", 10 | "keil-assistant.contributes.conf.props.project.fileLocationList.description": "uVision project file locations", 11 | "keil-assistant.contributes.conf.props.project.customIncludePaths.description": "uVision project file custom include paths; Use relative paths, like: \r\n\r\n../lib/inc,\r\n\r\n../common/inc", 12 | "keil-assistant.contributes.commands.project-open.title": "Open keil uVision project", 13 | "keil-assistant.contributes.commands.project-switch.title": "Switch Target", 14 | "keil-assistant.contributes.commands.project-active.title": "Active Project", 15 | "keil-assistant.contributes.commands.project-close.title": "Close Project", 16 | "keil-assistant.contributes.commands.project-build.title": "Build", 17 | "keil-assistant.contributes.commands.project-rebuild.title": "Rebuild", 18 | "keil-assistant.contributes.commands.project-download.title": "Download To device", 19 | "keil-assistant.contributes.commands.project-copy.title": "Copy Item Value", 20 | "keil-assistant.contributes.conf.props.keil.home.markdownDescription": { 21 | "message": "Keil uVision (MDK/C51/C251) Installation directory, Please modify the default value according to the actual installation directory, if (MDK/C51/C251) is installed in the same directory, you can set the MDK installation directory separately. \r\n Default value: {\"MDK\":\"C:\\Keil_v5\"}。\r\n\r\nExample:\r\n\r\n\"{\"MDK\":\"C:\\Keil_v5\"}\"\r\n\r\n\"{\"C51\":\"C:\\Keil_v5\"}\"\r\n\r\n\"{\"C251\":\"C:\\Keil_v5\"}\"", 22 | "comment": [ 23 | "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." 24 | ] 25 | }, 26 | "keil-assistant.contributes.viewsWelcome.project.content": "keil project not found [learn more](https://github.com/jacksonjim/keil-assistant/blob/master/README.md).\n[Open project](command:explorer.open)", 27 | "keil-assistant.contributes.conf.props.project.FindMaxDepth.description": "uVision project file search depth" 28 | } -------------------------------------------------------------------------------- /package.nls.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "displayName": "Keil 助理", 3 | "description": "Keil uVision 助理方便处理uVision项目,但是不会更改原理项目文件配置,常用功能有编写代码、编译、重新编译,下载等功能", 4 | "keil-assistant.contributes.conf.title": "Keil uVision 助理", 5 | "keil-assistant.contributes.views.explorer.name": "Keil uVision 项目", 6 | "keil-assistant.contributes.conf.props.mdk.home.description": "Keil uVision (MDK/C51/C251) 安装目录, 必填项 默认值: C:\\Keil_v5", 7 | "keil-assistant.contributes.conf.props.C51.home.description": "Keil uVision C51 安装目录 可选项", 8 | "keil-assistant.contributes.conf.props.C251.home.description": "Keil uVision C251 安装目录 可选项", 9 | "keil-assistant.contributes.conf.props.project.excludeList.description": "uVision 项目文件名排除列表", 10 | "keil-assistant.contributes.conf.props.project.fileLocationList.description": "uVision 项目文件位置", 11 | "keil-assistant.contributes.conf.props.project.customIncludePaths.description": "uVision 项目自定义头文件路径; 使用相对路径, 例如: \r\n\r\n../lib/inc,\r\n\r\n ../common/inc", 12 | "keil-assistant.contributes.commands.project-open.title": "打开Keil uVision的项目", 13 | "keil-assistant.contributes.commands.project-switch.title": "切换项目目标", 14 | "keil-assistant.contributes.commands.project-active.title": "激活项目", 15 | "keil-assistant.contributes.commands.project-close.title": "关闭项目", 16 | "keil-assistant.contributes.commands.project-build.title": "编译", 17 | "keil-assistant.contributes.commands.project-rebuild.title": "重新编译", 18 | "keil-assistant.contributes.commands.project-download.title": "下载到设备", 19 | "keil-assistant.contributes.commands.project-copy.title": "复制对像", 20 | "keil-assistant.contributes.conf.props.keil.home.markdownDescription": "Keil uVision (MDK/C51/C251) 安装目录,请根据实际安装目录修改默认值, 如果(MDK/C51/C251)安装在同一目录内,可以单独设置MDK的安装目录即可 \r\n 默认值: {\"MDK\":\"C:\\Keil_v5\"}。\r\n\r\n例子:\r\n\r\n\"{\"MDK\":\"C:\\Keil_v5\"}\"\r\n\r\n\"{\"C51\":\"C:\\Keil_v5\"}\"\r\n\r\n\"{\"C251\":\"C:\\Keil_v5\"}\"", 21 | "keil-assistant.contributes.viewsWelcome.project.content": "uVision的项目不存在 [了解更多信息](https://github.com/jacksonjim/keil-assistant/blob/master/README.md).\n[打开uVision项目](command:explorer.open)", 22 | "keil-assistant.contributes.conf.props.project.FindMaxDepth.description": "uVision 项目文件搜索深度,默认为1层子文件夹" 23 | } -------------------------------------------------------------------------------- /package.nls.zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "displayName": "Keil 助理", 3 | "description": "Keil uVision 助理方便處理uVision專案,但是不會更改原理項目檔配置,常用功能有編寫代碼、編譯、重新編譯,下載等功能", 4 | "keil-assistant.contributes.conf.title": "Keil uVision 助理", 5 | "keil-assistant.contributes.views.explorer.name": "Keil uVision 專案", 6 | "keil-assistant.contributes.conf.props.mdk.home.description": "Keil uVision (MDK/C51/C251) 安裝目錄,必填項; 預設值: C:\\Keil_v5", 7 | "keil-assistant.contributes.conf.props.C51.home.description": "Keil uVision C51 安裝目錄 可選項", 8 | "keil-assistant.contributes.conf.props.C251.home.description": "Keil uVision C251 安裝目錄 可選項", 9 | "keil-assistant.contributes.conf.props.project.excludeList.description": "uVision 項目檔名排除清單", 10 | "keil-assistant.contributes.conf.props.project.fileLocationList.description": "uVision 項目檔位置", 11 | "keil-assistant.contributes.conf.props.project.customIncludePaths.description": "uVision 項目自定義頭文件路徑; 使用相對路徑, 例如: \r\n\r\n../lib/inc,\r\n\r\n ../common/inc", 12 | "keil-assistant.contributes.commands.project-open.title": "打開Keil uVision專案", 13 | "keil-assistant.contributes.commands.project-switch.title": "切換目標", 14 | "keil-assistant.contributes.commands.project-active.title": "活動專案", 15 | "keil-assistant.contributes.commands.project-close.title": "關閉專案", 16 | "keil-assistant.contributes.commands.project-build.title": "編譯", 17 | "keil-assistant.contributes.commands.project-rebuild.title": "重新編譯", 18 | "keil-assistant.contributes.commands.project-download.title": "下載到設備", 19 | "keil-assistant.contributes.commands.project-copy.title": "複製專案值", 20 | "keil-assistant.contributes.conf.props.keil.home.markdownDescription": "Keil uVision (MDK/C51/C251) 安裝目錄,請根據實際安裝目錄修改預設值, 如果(MDK/C51/C251)安裝在同一目錄內,可以單獨設置MDK的安裝目錄即可. \r\n 預設值: {\"MDK\":\"C:\\Keil_v5\"}。\r\n\r\n例子:\r\n\r\n\"{\"MDK\":\"C:\\Keil_v5\"}\"\r\n\r\n\"{\"C51\":\"C:\\Keil_v5\"}\"\r\n\r\n\"{\"C251\":\"C:\\Keil_v5\"}\"", 21 | "keil-assistant.contributes.viewsWelcome.project.content": "uVision的專案不存在 [瞭解更多信息](https://github.com/jacksonjim/keil-assistant/blob/master/README.md).\n[開啟uVision專案](command:explorer.open)", 22 | "keil-assistant.contributes.conf.props.project.FindMaxDepth.description": "uVision 項目檔搜索深度" 23 | } -------------------------------------------------------------------------------- /res/icons/ActiveApplication_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/AssemblerSourceFile_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/BuildSelection_16x_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /res/icons/BuildSelection_16x_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /res/icons/BuildSolution_16x_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /res/icons/BuildSolution_16x_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /res/icons/CFile_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/CPPHeaderFile_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/CPP_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/ClassAdded_16x_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /res/icons/ClassAdded_16x_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/icons/ClassProtected_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/Class_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/CompilableFile_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/CopyToClipboard_16x_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /res/icons/CopyToClipboard_16x_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /res/icons/DeactiveApplication_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/FileExclude_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/FileWarning_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/FolderExclude_32x.svg: -------------------------------------------------------------------------------- 1 | folder_type_private -------------------------------------------------------------------------------- /res/icons/Folder_32x.svg: -------------------------------------------------------------------------------- 1 | default_folder -------------------------------------------------------------------------------- /res/icons/Library_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/StatusOffline_16x_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/icons/StatusOffline_16x_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/icons/SwitchSourceOrTarget_16x_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/icons/SwitchSourceOrTarget_16x_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/icons/Text_16x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/TransferDownload_16x_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /res/icons/TransferDownload_16x_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /res/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/icons/icon.png -------------------------------------------------------------------------------- /res/icons/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/refresh-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /res/icons/refresh-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /res/preview/active_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/active_target.png -------------------------------------------------------------------------------- /res/preview/build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/build.png -------------------------------------------------------------------------------- /res/preview/cpp_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/cpp_config.png -------------------------------------------------------------------------------- /res/preview/keil_save_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/keil_save_all.png -------------------------------------------------------------------------------- /res/preview/load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/load.png -------------------------------------------------------------------------------- /res/preview/load1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/load1.png -------------------------------------------------------------------------------- /res/preview/load2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/load2.png -------------------------------------------------------------------------------- /res/preview/load3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/load3.png -------------------------------------------------------------------------------- /res/preview/new_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/new_setting.png -------------------------------------------------------------------------------- /res/preview/open_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/open_file.png -------------------------------------------------------------------------------- /res/preview/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/preview.png -------------------------------------------------------------------------------- /res/preview/ref_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/ref_show.png -------------------------------------------------------------------------------- /res/preview/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/setting.png -------------------------------------------------------------------------------- /res/preview/switch_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksonjim/keil-assistant/d4abbc07464685b5c7502a4e1e1a62b6a5c2f886/res/preview/switch_target.png -------------------------------------------------------------------------------- /src/CmdLineHandler.ts: -------------------------------------------------------------------------------- 1 | 2 | export class CmdLineHandler { 3 | 4 | // private constructor() { } 5 | 6 | /** 7 | * powershell: & '@param callerFile' 'arg1' 'arg2' 8 | * 9 | * cmd: ""@param callerFile" "arg1" "arg2"" 10 | */ 11 | static getCommandLine(callerFile: string, args: string[], isPowershell?: boolean, noQuote = false): string { 12 | 13 | const quote = isPowershell ? "'" : '"'; 14 | const callerHeader = isPowershell ? '& ' : ''; 15 | const cmdPrefixSuffix = isPowershell ? '' : '"'; 16 | 17 | const commandLine: string = `${cmdPrefixSuffix + callerHeader 18 | + this.quoteString(callerFile, quote) } ${ 19 | args.map((arg) => noQuote ? arg : this.quoteString(arg, quote)).join(' ') 20 | }${cmdPrefixSuffix}`; 21 | 22 | return commandLine; 23 | } 24 | 25 | /** 26 | * input: ""a b.exe" -a -b -c" 27 | * 28 | * output: "a b.exe" -a -b -c 29 | */ 30 | static deleteCmdPrefix(cmdLine: string): string { 31 | return cmdLine.replace(/^"|"$/g, ''); 32 | } 33 | 34 | static quoteString(str: string, quote: string): string { 35 | return (str.includes(' ') && !str.includes(quote)) ? (quote + str + quote) : str; 36 | } 37 | } -------------------------------------------------------------------------------- /src/ResourceManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { File } from './node_utility/File'; 3 | import * as ini from 'ini'; 4 | 5 | let _instance: ResourceManager | undefined; 6 | 7 | const dirList: string[] = [ 8 | `${File.sep }bin`, 9 | `${File.sep }res`, 10 | `${File.sep }res${ File.sep }icons` 11 | ]; 12 | 13 | export class ResourceManager { 14 | 15 | private extensionDir: File; 16 | private dirMap: Map; 17 | private iconMap: Map; 18 | 19 | private constructor(context: vscode.ExtensionContext) { 20 | this.extensionDir = new File(context.extensionPath); 21 | this.dirMap = new Map(); 22 | this.iconMap = new Map(); 23 | this.init(); 24 | } 25 | 26 | static getInstance(context?: vscode.ExtensionContext): ResourceManager { 27 | if (_instance === undefined) { 28 | if (context) { 29 | _instance = new ResourceManager(context); 30 | } else { 31 | throw Error('context can\'t be undefined'); 32 | } 33 | } 34 | 35 | return _instance; 36 | } 37 | 38 | private init() { 39 | // init dirs 40 | for (const path of dirList) { 41 | const f = new File(this.extensionDir.path + path); 42 | 43 | if (f.isDir()) { 44 | this.dirMap.set(f.noSuffixName, f); 45 | } 46 | } 47 | 48 | // init icons 49 | const iconDir = this.dirMap.get('icons'); 50 | 51 | if (iconDir) { 52 | for (const icon of iconDir.getList([/\.svg$/i], File.emptyFilter)) { 53 | this.iconMap.set(icon.noSuffixName, icon.path); 54 | } 55 | } 56 | } 57 | 58 | private getAppConfig(): vscode.WorkspaceConfiguration { 59 | return vscode.workspace.getConfiguration('KeilAssistant'); 60 | } 61 | 62 | getBuilderExe(): string { 63 | return this.getFilePath('bin', 'Uv4Caller.exe'); 64 | } 65 | 66 | getKeilUV4Path(target: string): string { 67 | return `${this.getKeilRootDir(target)}${File.sep}UV4${File.sep}UV4.exe`; 68 | } 69 | 70 | getCompilerPath(target: string, compiler: string | undefined): string | undefined { 71 | if (compiler === "ARMCLANG") { 72 | return `${this.getKeilRootDir(target)}${File.sep}ARM${File.sep}ARMCLANG${File.sep}bin${File.sep}armclang.exe`; 73 | } 74 | if (compiler === "ARMCC") { 75 | return `${this.getKeilRootDir(target)}${File.sep}ARM${File.sep}ARMCC${File.sep}bin${File.sep}armcc.exe`; 76 | } 77 | 78 | return undefined; 79 | } 80 | 81 | getKeilRootDir(target: string): string { 82 | const homePath = this.getHomePath(target); 83 | 84 | return homePath ?? "C:\\Keil_v5"; 85 | } 86 | 87 | getPackDir(target: string): string { 88 | var path: string | undefined = undefined; 89 | const homePath = this.getKeilRootDir(target); 90 | 91 | try { 92 | const iniFile = new File(`${homePath + File.sep }TOOLS.INI`); 93 | const iniContent = iniFile.read(); 94 | const parsed = ini.parse(iniContent); 95 | 96 | path = parsed['UV2']['RTEPATH'].replace(/^["']+|["']+$/g, ''); 97 | } catch (e) { 98 | console.error("Error reading TOOL.INI file:", e); 99 | } 100 | 101 | return path ?? `${homePath + File.sep }ARM${ File.sep }PACK`; 102 | } 103 | 104 | private getHomePath(target: string): string | undefined { 105 | const homeObj = this.getAppConfig().get("Keil.HOME"); 106 | 107 | if (homeObj) { 108 | const pathMap = new Map(Object.entries(homeObj)); 109 | 110 | return pathMap.get(target) ?? pathMap.get("MDK"); 111 | } 112 | 113 | return undefined; 114 | } 115 | 116 | getPropertyValue(obj: T, key: K): T[K] { 117 | return obj[key]; 118 | } 119 | 120 | getProjectExcludeList(): string[] { 121 | return this.getAppConfig().get('Project.ExcludeList') ?? []; 122 | } 123 | 124 | getProjectFileLocationList(): string[] { 125 | return this.getAppConfig().get('Project.FileLocationList') ?? []; 126 | } 127 | 128 | getProjectCustomIncludePaths(): string[] { 129 | return this.getAppConfig().get('Project.CustomIncludePaths') ?? []; 130 | } 131 | 132 | getIconByName(name: string): vscode.Uri { 133 | const icon = this.iconMap.get(name); 134 | 135 | return vscode.Uri.file(icon!); 136 | } 137 | 138 | private getFilePath(dirKey: string, fileName: string): string { 139 | return this.dirMap.get(dirKey)?.path + File.sep + fileName; 140 | } 141 | 142 | public getProjectFileFindMaxDepth(): number { 143 | const depth = this.getAppConfig().get("Project.FindMaxDepth"); 144 | 145 | return depth ?? 1; 146 | } 147 | } -------------------------------------------------------------------------------- /src/core/FileGroup.ts: -------------------------------------------------------------------------------- 1 | import type { IView } from "./IView"; 2 | import type { Source } from "./Source"; 3 | 4 | export class FileGroup implements IView { 5 | 6 | label: string; 7 | prjID: string; 8 | tooltip?: string | undefined; 9 | contextVal?: string | undefined = 'FileGroup'; 10 | icons?: { light: string; dark: string; }; 11 | private _disabled: boolean; 12 | 13 | //---- 14 | sources: Source[]; 15 | 16 | constructor(pID: string, gName: string, disabled: boolean) { 17 | this.label = gName; 18 | this.prjID = pID; 19 | this.sources = []; 20 | this.tooltip = gName; 21 | this._disabled = disabled; 22 | const iconName = disabled ? 'FolderExclude_32x' : 'Folder_32x'; 23 | 24 | this.icons = { light: iconName, dark: iconName }; 25 | } 26 | 27 | private _cachedChildren?: IView[]; 28 | getChildViews(): IView[] | undefined { 29 | // return this.sources; 30 | this._cachedChildren ??= [...this.sources]; 31 | 32 | return this._cachedChildren; 33 | } 34 | 35 | updateDisabledState(disabled: boolean) { 36 | if (this._disabled !== disabled) { 37 | this._disabled = disabled; 38 | const iconName = disabled ? 'FolderExclude_32x' : 'Folder_32x'; 39 | 40 | this.icons = { light: iconName, dark: iconName }; 41 | this._cachedChildren = undefined; // 清除缓存 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/core/IView.ts: -------------------------------------------------------------------------------- 1 | export type IView = { 2 | 3 | label: string; 4 | 5 | prjID: string; 6 | 7 | icons?: { light: string, dark: string }; 8 | 9 | tooltip?: string; 10 | 11 | contextVal?: string; 12 | 13 | getChildViews(): IView[] | undefined; 14 | } -------------------------------------------------------------------------------- /src/core/KeilProject.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter as EventsEmitter } from 'events'; 2 | import { FileWatcher } from '../node_utility/FileWatcher'; 3 | import { File } from '../node_utility/File'; 4 | import { createHash } from 'crypto'; 5 | import { createWriteStream } from 'fs'; 6 | import { Time } from '../node_utility/Time'; 7 | import type { X2jOptions } from 'fast-xml-parser'; 8 | import { XMLParser } from 'fast-xml-parser'; 9 | import { normalize } from 'path'; 10 | import type { KeilProjectInfo } from './KeilProjectInfo'; 11 | 12 | import type { OutputChannel } from 'vscode'; 13 | import { window } from 'vscode'; 14 | import type { IView } from './IView'; 15 | import type { PTarget, UVisonInfo } from '../target/PTarget'; 16 | import { ArmTarget } from '../target/ArmTarget'; 17 | import { C251Target } from '../target/C251Target'; 18 | import { C51Target } from '../target/C51Target'; 19 | 20 | type KeilProperties = { 21 | project: object | any | undefined; 22 | } 23 | 24 | export class KeilProject implements IView, KeilProjectInfo { 25 | 26 | prjID: string; 27 | label: string; 28 | tooltip?: string | undefined; 29 | contextVal?: string | undefined = 'Project'; 30 | icons?: { light: string; dark: string; } = { 31 | light: 'DeactiveApplication_16x', 32 | dark: 'DeactiveApplication_16x' 33 | }; 34 | 35 | //------------- 36 | workspaceDir: string | undefined; 37 | vscodeDir: File; 38 | uvprjFile: File; 39 | logger: Console; 40 | isMultiplyProject: boolean; 41 | 42 | // uVison info 43 | uVsionFileInfo: UVisonInfo; 44 | 45 | activeTargetName: string | undefined; 46 | private prevUpdateTime: number | undefined; 47 | 48 | protected _event: EventsEmitter; 49 | protected watcher: FileWatcher; 50 | protected targetList: PTarget[]; 51 | 52 | keilVscodeProps: KeilProperties = { 53 | project: undefined, 54 | }; 55 | 56 | constructor(private channel: OutputChannel, _uvprjFile: File, workspace: string | undefined, hasMultiplyProject: boolean) { 57 | this._event = new EventsEmitter(); 58 | this.uVsionFileInfo = {} as UVisonInfo; 59 | this.targetList = []; 60 | this.workspaceDir = workspace; 61 | this.vscodeDir = new File(`${workspace + File.sep}.vscode`); 62 | this.vscodeDir.createDir(); 63 | const logPath = `${this.vscodeDir.path + File.sep}keil-assistant.log`; 64 | 65 | this.logger = new console.Console(createWriteStream(logPath, { flags: 'a+' })); 66 | this.uvprjFile = _uvprjFile; 67 | this.watcher = new FileWatcher(this.uvprjFile); 68 | this.isMultiplyProject = hasMultiplyProject; 69 | this.prjID = this.getMD5(_uvprjFile.path); 70 | this.label = _uvprjFile.noSuffixName; 71 | this.tooltip = _uvprjFile.path; 72 | this.logger.log(`[info] Log at : ${Time.getInstance().getTimeStamp()}\r\n`); 73 | this.getKeilVscodeProperties(); 74 | this.watcher.onChanged = () => { 75 | if (this.prevUpdateTime === undefined || 76 | this.prevUpdateTime + 2000 < Date.now()) { 77 | this.prevUpdateTime = Date.now(); // reset update time 78 | setTimeout(() => { void this.onReload(); }, 300); 79 | } 80 | }; 81 | this.watcher.watch(); 82 | } 83 | 84 | on(event: 'dataChanged', listener: () => void): void; 85 | on(event: any, listener: () => void): void { 86 | this._event.on(event, listener); 87 | } 88 | 89 | private async onReload() { 90 | try { 91 | this.targetList.forEach((target) => target.close()); 92 | this.targetList = []; 93 | await this.load(); 94 | this.notifyUpdateView(); 95 | } catch (error) { 96 | const err = (error as any).error; 97 | const code = err["code"]; 98 | 99 | if (code === 'EBUSY') { 100 | this.logger.log(`[Warn] uVision project file '${this.uvprjFile.name}' is locked !, delay 500 ms and retry !`); 101 | setTimeout(() => { void this.onReload(); }, 500); 102 | } else { 103 | window.showErrorMessage(`reload project failed !, msg: ${err["message"]}`); 104 | } 105 | } 106 | } 107 | 108 | private getMD5(data: string): string { 109 | const md5 = createHash('md5'); 110 | 111 | md5.update(data); 112 | 113 | return md5.digest('hex'); 114 | } 115 | 116 | async load() { 117 | let doc: any = {}; 118 | const options: X2jOptions = { 119 | attributeNamePrefix: "@_", 120 | textNodeName: "#text", 121 | ignoreAttributes: false, 122 | ignoreDeclaration: true, 123 | ignorePiTags: true, 124 | allowBooleanAttributes: false, 125 | parseAttributeValue: true, 126 | parseTagValue: true, 127 | trimValues: true, 128 | cdataPropName: "__cdata", 129 | // attributeValueProcessor: (val: any, attrName: any) => decode(val, { isAttributeValue: true }),//default is a=>a 130 | // tagValueProcessor: (val: any, tagName: any) => decode(val), //default is a=>a 131 | // stopNodes: [], 132 | numberParseOptions: { 133 | leadingZeros: true, 134 | hex: true, 135 | skipLike: /^[A-Za-z]+[0-9]*$/, 136 | eNotation: true 137 | } 138 | }; 139 | 140 | try { 141 | const parser = new XMLParser(options); 142 | const xmldoc = this.uvprjFile.read(); 143 | 144 | doc = parser.parse(xmldoc); 145 | } catch (e) { 146 | const errorMsg = e instanceof Error ? e.message : String(e); 147 | 148 | this.channel.show(); 149 | this.channel.appendLine(`XML解析失败: ${errorMsg}`); 150 | throw new Error(`Project load failed: ${errorMsg}`); 151 | } 152 | 153 | const targets = doc.Project.Targets.Target; 154 | const rteDom = doc.Project.RTE; 155 | 156 | // init uVsion info 157 | this.uVsionFileInfo.schemaVersion = doc.Project.SchemaVersion; 158 | 159 | if (Array.isArray(targets)) { 160 | for (const target of targets) { 161 | this.targetList.push(this.getInstance(target, rteDom)); 162 | } 163 | } else { 164 | this.targetList.push(this.getInstance(targets, rteDom)); 165 | } 166 | 167 | for (const target of this.targetList) { 168 | await target.load(); 169 | target.on('dataChanged', () => this.notifyUpdateView()); 170 | } 171 | 172 | if (this.keilVscodeProps['project']['activeTargetName'] === undefined) { 173 | this.activeTargetName = this.targetList[0].targetName; 174 | this.updateKeilVscodeProperties(); 175 | } else { 176 | this.activeTargetName = this.keilVscodeProps['project']['activeTargetName']; 177 | } 178 | 179 | this.updateClangdFile(); 180 | } 181 | 182 | getInstance(targetDOM: any, rteDom: any): PTarget { 183 | if (this.uvprjFile.suffix.toLowerCase() === '.uvproj') { 184 | 185 | if (targetDOM['TargetOption']['Target51'] !== undefined) { 186 | return new C51Target(this, this.uVsionFileInfo, targetDOM, rteDom); 187 | } 188 | 189 | if (targetDOM['TargetOption']['Target251'] !== undefined) { 190 | return new C251Target(this, this.uVsionFileInfo, targetDOM, rteDom); 191 | } 192 | 193 | if (targetDOM['TargetOption']['TargetArmAds'] !== undefined) { 194 | return new ArmTarget(this, this.uVsionFileInfo, targetDOM, rteDom); 195 | } 196 | 197 | return new ArmTarget(this, this.uVsionFileInfo, targetDOM, rteDom); 198 | } else { 199 | return new ArmTarget(this, this.uVsionFileInfo, targetDOM, rteDom); 200 | } 201 | } 202 | 203 | notifyUpdateView() { 204 | this._event.emit('dataChanged'); 205 | } 206 | 207 | close() { 208 | this.watcher.close(); 209 | this.targetList.forEach((target) => target.close()); 210 | this.logger.log(`[info] project closed: ${this.label}`); 211 | } 212 | 213 | toAbsolutePath(rePath: string): string { 214 | const path = rePath.replace(/\//g, File.sep); 215 | 216 | if (/^[a-z]:/i.test(path)) { 217 | return normalize(path); 218 | } 219 | 220 | return normalize(this.uvprjFile.dir + File.sep + path); 221 | } 222 | 223 | active() { 224 | this.icons = { light: 'ActiveApplication_16x', dark: 'ActiveApplication_16x' }; 225 | } 226 | 227 | deactive() { 228 | this.icons = { light: 'DeactiveApplication_16x', dark: 'DeactiveApplication_16x' }; 229 | } 230 | 231 | getTargetByName(name: string): PTarget | undefined { 232 | const index = this.targetList.findIndex((t) => t.targetName === name); 233 | 234 | if (index !== -1) { 235 | return this.targetList[index]; 236 | } 237 | } 238 | 239 | setActiveTarget(tName: string) { 240 | if (tName !== this.activeTargetName) { 241 | this.activeTargetName = tName; 242 | this.updateKeilVscodeProperties(); 243 | this.updateClangdFile(); 244 | this.notifyUpdateView(); // notify data changed 245 | } 246 | } 247 | 248 | updateClangdFile() { 249 | this.getActiveTarget()?.updateClangdFile(); 250 | } 251 | 252 | getActiveTarget(): PTarget | undefined { 253 | if (this.activeTargetName) { 254 | return this.getTargetByName(this.activeTargetName); 255 | } 256 | 257 | else if (this.targetList.length > 0) { 258 | return this.targetList[0]; 259 | } 260 | } 261 | 262 | getChildViews(): IView[] | undefined { 263 | 264 | if (this.activeTargetName) { 265 | const target = this.getTargetByName(this.activeTargetName); 266 | 267 | if (target) { 268 | return [target]; 269 | } 270 | } 271 | 272 | if (this.targetList.length > 0) { 273 | return [this.targetList[0]]; 274 | } 275 | 276 | return undefined; 277 | } 278 | 279 | getTargets(): PTarget[] { 280 | return this.targetList; 281 | } 282 | 283 | 284 | private getDefKeilVscodeProperties(): KeilProperties { 285 | return { 286 | project: { 287 | name: undefined, 288 | activeTargetName: undefined 289 | }, 290 | }; 291 | } 292 | 293 | private getKeilVscodeProperties() { 294 | const proFile = new File(`${this.vscodeDir.path}${File.sep}keil_project_properties.json`); 295 | 296 | if (proFile.isFile()) { 297 | try { 298 | this.keilVscodeProps = JSON.parse(proFile.read()); 299 | } catch (error) { 300 | this.logger.log(`[Warn] keil_project_properties.json parse failed !, ${error}`); 301 | this.keilVscodeProps = this.getDefKeilVscodeProperties(); 302 | } 303 | } else { 304 | this.keilVscodeProps = this.getDefKeilVscodeProperties(); 305 | } 306 | 307 | return proFile; 308 | } 309 | 310 | private updateKeilVscodeProperties() { 311 | const proFile = this.getKeilVscodeProperties(); 312 | 313 | const project = this.keilVscodeProps['project']; 314 | 315 | if (project?.name === this.prjID) { 316 | project.activeTargetName = this.activeTargetName; 317 | } else { 318 | project.name = this.prjID; 319 | project.activeTargetName = this.activeTargetName; 320 | } 321 | 322 | proFile.write(JSON.stringify(this.keilVscodeProps, undefined, 4)); 323 | } 324 | 325 | } -------------------------------------------------------------------------------- /src/core/KeilProjectInfo.ts: -------------------------------------------------------------------------------- 1 | import type { File } from '../node_utility/File'; 2 | 3 | export type KeilProjectInfo = { 4 | keilVscodeProps: any; 5 | 6 | prjID: string; 7 | 8 | vscodeDir: File; 9 | 10 | workspaceDir: string | undefined; 11 | 12 | uvprjFile: File; 13 | 14 | logger: Console; 15 | 16 | isMultiplyProject: boolean; 17 | 18 | toAbsolutePath(rePath: string): string; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/core/MacroHandler.ts: -------------------------------------------------------------------------------- 1 | export class MacroHandler { 2 | private regMatchers = { 3 | 'normalMacro': /^#define (\w+) (.*)$/, 4 | 'funcMacro': /^#define (\w+\([^\\)]*\)) (.*)$/ 5 | }; 6 | 7 | toExpression(macro: string): string | undefined { 8 | 9 | let mList = this.regMatchers['normalMacro'].exec(macro); 10 | 11 | if (mList && mList.length > 2) { 12 | return `${mList[1]}=${mList[2]}`; 13 | } 14 | 15 | mList = this.regMatchers['funcMacro'].exec(macro); 16 | if (mList && mList.length > 2) { 17 | return `${mList[1]}=`; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/core/Source.ts: -------------------------------------------------------------------------------- 1 | import type { File } from '../node_utility/File'; 2 | import type { IView } from './IView'; 3 | 4 | export class Source implements IView { 5 | 6 | label: string; 7 | prjID: string; 8 | icons?: { light: string; dark: string; } | undefined; 9 | tooltip?: string | undefined; 10 | contextVal?: string | undefined = 'Source'; 11 | 12 | //--- 13 | readonly file: File; 14 | readonly enable: boolean; 15 | 16 | children: Source[] | undefined; 17 | 18 | constructor(pID: string, file: File, _enable = true) { 19 | this.prjID = pID; 20 | this.enable = _enable; 21 | this.file = file; 22 | this.label = this.file.name; 23 | this.tooltip = file.path; 24 | 25 | let iconName = ''; 26 | 27 | if (file.isFile() === false) { 28 | iconName = 'FileWarning_16x'; 29 | } else if (_enable === false) { 30 | iconName = 'FileExclude_16x'; 31 | } else { 32 | iconName = this.getIconBySuffix(file.suffix.toLowerCase()); 33 | } 34 | 35 | this.icons = { 36 | dark: iconName, 37 | light: iconName 38 | }; 39 | } 40 | 41 | private getIconBySuffix(suffix: string): string { 42 | switch (suffix) { 43 | case '.c': 44 | return 'CFile_16x'; 45 | case '.h': 46 | case '.hpp': 47 | case '.hxx': 48 | case '.inc': 49 | return 'CPPHeaderFile_16x'; 50 | case '.cpp': 51 | case '.c++': 52 | case '.cxx': 53 | case '.cc': 54 | return 'CPP_16x'; 55 | case '.s': 56 | case '.a51': 57 | case '.asm': 58 | return 'AssemblerSourceFile_16x'; 59 | case '.lib': 60 | case '.a': 61 | return 'Library_16x'; 62 | default: 63 | return 'Text_16x'; 64 | } 65 | } 66 | 67 | getChildViews(): IView[] | undefined { 68 | return this.children; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ExtensionContext, OutputChannel, StatusBarItem} from 'vscode'; 3 | import { 4 | StatusBarAlignment, 5 | commands, env, l10n, window 6 | } from 'vscode'; 7 | 8 | import { dirname } from 'path'; 9 | 10 | import { File } from './node_utility/File'; 11 | import { ResourceManager } from './ResourceManager'; 12 | 13 | import { stat } from 'fs'; 14 | import { ProjectExplorer } from './ui/ProjectExplorer'; 15 | import type { IView } from './core/IView'; 16 | 17 | let myStatusBarItem: StatusBarItem; 18 | let channel: OutputChannel; 19 | 20 | export function activate(context: ExtensionContext) { 21 | console.info('---- keil-assistant actived >>> ----'); 22 | channel ??= window.createOutputChannel('keil-vscode'); 23 | myStatusBarItem = window.createStatusBarItem(StatusBarAlignment.Left, 200); 24 | myStatusBarItem.command = 'statusbar.project'; 25 | 26 | const testKeilRoot = ResourceManager.getInstance(context).getKeilRootDir("MDK"); 27 | 28 | stat(testKeilRoot, (err, stat) => { 29 | if (err || !stat.isDirectory()) { 30 | channel.show(); 31 | channel.appendLine(`Error: Please set keil root Path, ${err}`); 32 | window.showErrorMessage(`Error: Please set keil root Path, ${err}`); 33 | } 34 | }); 35 | 36 | const prjExplorer = new ProjectExplorer(context, channel, myStatusBarItem); 37 | const subscriber = context.subscriptions; 38 | 39 | const projectSwitchCommandId = 'project.switch'; 40 | 41 | subscriber.push(commands.registerCommand('explorer.open', async () => { 42 | const uri = await window.showOpenDialog({ 43 | openLabel: l10n.t('Open keil uVision project'), 44 | canSelectFolders: false, 45 | canSelectMany: false, 46 | filters: { 47 | 'keilProjectXml': ['uvproj', 'uvprojx', 'uvmpw'] 48 | } 49 | }); 50 | 51 | try { 52 | if (uri && uri.length > 0) { 53 | 54 | // load project 55 | const uvPrjPath = uri[0].fsPath; 56 | 57 | await prjExplorer.openProject(uvPrjPath, false); 58 | // switch workspace 59 | const msg = l10n.t('keil project load done ! switch workspace ?'); 60 | const result = await window.showInformationMessage(msg, l10n.t('Ok'), l10n.t('Later')); 61 | 62 | if (result === l10n.t('Ok')) { 63 | prjExplorer.openWorkspace(new File(dirname(uvPrjPath))); 64 | } 65 | } 66 | } catch (error) { 67 | const errMsg = l10n.t('Open project failed! msg'); 68 | 69 | window.showErrorMessage(`${errMsg}: ${(error as Error).message}`); 70 | } 71 | })); 72 | 73 | subscriber.push(commands.registerCommand('project.close', (item: IView) => prjExplorer.closeProject(item.prjID))); 74 | 75 | subscriber.push(commands.registerCommand('project.build', (item: IView) => prjExplorer.getTarget(item)?.build())); 76 | 77 | subscriber.push(commands.registerCommand('project.rebuild', (item: IView) => prjExplorer.getTarget(item)?.rebuild())); 78 | 79 | subscriber.push(commands.registerCommand('project.download', (item: IView) => prjExplorer.getTarget(item)?.download())); 80 | 81 | subscriber.push(commands.registerCommand('item.copyValue', (item: IView) => env.clipboard.writeText(item.tooltip ?? ''))); 82 | 83 | subscriber.push(commands.registerCommand(projectSwitchCommandId, (item: IView) => prjExplorer.switchTargetByProject(item))); 84 | 85 | subscriber.push(commands.registerCommand('project.active', (item: IView) => prjExplorer.activeProject(item))); 86 | 87 | subscriber.push(commands.registerCommand('statusbar.project', async () => { 88 | void prjExplorer.statusBarSwitchTargetByProject(); 89 | })); 90 | 91 | 92 | subscriber.push(myStatusBarItem); 93 | 94 | void prjExplorer.loadWorkspace(); 95 | console.info('---- keil-assistant actived <<<< ----'); 96 | } 97 | 98 | export function deactivate() { 99 | // console.log('---- keil-assistant closed ----'); 100 | channel.dispose(); 101 | } 102 | -------------------------------------------------------------------------------- /src/node_utility/File.ts: -------------------------------------------------------------------------------- 1 | import { basename, dirname, extname, sep, delimiter, isAbsolute, relative } from 'path'; 2 | import * as fs from 'fs'; 3 | import * as crypto from 'crypto'; 4 | 5 | export class File { 6 | 7 | static sep = sep; 8 | static delimiter = delimiter; 9 | static emptyFilter: RegExp[] = []; 10 | 11 | readonly name: string; // example 'demo.cpp' 12 | readonly noSuffixName: string; // example 'demo' 13 | readonly suffix: string; // example '.cpp' 14 | readonly dir: string; // example 'd:\\dir' 15 | readonly path: string; // example 'd:\\dir\\demo.cpp' 16 | 17 | constructor(fPath: string) { 18 | this.path = fPath; 19 | this.name = basename(fPath); 20 | this.noSuffixName = this.getNoSuffixName(this.name); 21 | this.suffix = extname(fPath); 22 | this.dir = dirname(fPath); 23 | } 24 | 25 | static fromArray(pathArray: string[]): File { 26 | return new File(pathArray.join(File.sep)); 27 | } 28 | 29 | static toUnixPath(path: string): string { 30 | return path.normalize(path).replace(/\\{1,}/g, '/'); 31 | } 32 | 33 | static toUri(path: string): string { 34 | return `file://${this.toNoProtocolUri(path)}`; 35 | } 36 | 37 | static toNoProtocolUri(path: string): string { 38 | return `/${encodeURIComponent(path.replace(/\\/g, '/'))}`; 39 | } 40 | 41 | // c:/abcd/../a -> c:\abcd\..\a 42 | static toLocalPath(path: string): string { 43 | 44 | const res = File.toUnixPath(path); 45 | 46 | if (File.sep === '\\') { 47 | return res.replace(/\//g, File.sep); 48 | } 49 | 50 | return res; 51 | } 52 | 53 | private static _match(str: string, isInverter: boolean, regList: RegExp[]): boolean { 54 | 55 | let isMatch = false; 56 | 57 | for (const reg of regList) { 58 | if (reg.test(str)) { 59 | isMatch = true; 60 | break; 61 | } 62 | } 63 | 64 | if (isInverter) { 65 | isMatch = !isMatch; 66 | } 67 | 68 | return isMatch; 69 | } 70 | 71 | private static _filter(fList: File[], isInverter: boolean, fileFilter?: RegExp[], dirFilter?: RegExp[]): File[] { 72 | 73 | const res: File[] = []; 74 | 75 | if (fileFilter) { 76 | fList.forEach(f => { 77 | if (f.isFile() && this._match(f.name, isInverter, fileFilter)) { 78 | res.push(f); 79 | } 80 | }); 81 | } else { 82 | fList.forEach(f => { 83 | if (f.isFile()) { 84 | res.push(f); 85 | } 86 | }); 87 | } 88 | 89 | if (dirFilter) { 90 | fList.forEach(f => { 91 | if (f.isDir() && this._match(f.name, isInverter, dirFilter)) { 92 | res.push(f); 93 | } 94 | }); 95 | } else { 96 | fList.forEach(f => { 97 | if (f.isDir()) { 98 | res.push(f); 99 | } 100 | }); 101 | } 102 | 103 | return res; 104 | } 105 | 106 | static filter(fList: File[], fileFilter?: RegExp[], dirFilter?: RegExp[]): File[] { 107 | return this._filter(fList, false, fileFilter, dirFilter); 108 | } 109 | 110 | static notMatchFilter(fList: File[], fileFilter?: RegExp[], dirFilter?: RegExp[]): File[] { 111 | return this._filter(fList, true, fileFilter, dirFilter); 112 | } 113 | 114 | private getNoSuffixName(name: string): string { 115 | const nList = this.name.split('.'); 116 | 117 | if (nList.length > 1) { 118 | nList.pop(); 119 | 120 | return nList.join('.'); 121 | } else { 122 | return name; 123 | } 124 | } 125 | 126 | private _copyRetainDir(baseDir: File, file: File) { 127 | 128 | const relativePath = baseDir.toRelativePath(file.dir); 129 | 130 | if (relativePath) { 131 | 132 | const dir = File.fromArray([this.path, relativePath.replace(/\//g, File.sep)]); 133 | 134 | if (!dir.isDir()) { 135 | this.createDir(true); 136 | } 137 | fs.copyFileSync(file.path, dir.path + File.sep + file.name); 138 | } 139 | } 140 | 141 | /** 142 | * example: this.path: 'd:\app\abc\.', absPath: 'd:\app\abc\.\def\a.c', result: '.\def\a.c' 143 | */ 144 | toRelativePath(abspath: string, hasPrefix = true): string | undefined { 145 | 146 | if (!isAbsolute(abspath)) { 147 | return undefined; 148 | } 149 | 150 | const rePath = relative(this.path, abspath); 151 | 152 | if (isAbsolute(rePath)) { 153 | return undefined; 154 | } 155 | 156 | return hasPrefix ? (`.${File.sep}${rePath}`) : rePath; 157 | } 158 | 159 | //---------------------------------------------------- 160 | 161 | createDir(recursive = false): void { 162 | if (!this.isDir()) { 163 | if (recursive) { 164 | const list = this.path.split(sep); 165 | let f: File; 166 | 167 | if (list.length > 0) { 168 | let dir: string = list[0]; 169 | 170 | for (let i = 0; i < list.length;) { 171 | f = new File(dir); 172 | if (!f.isDir()) { 173 | fs.mkdirSync(f.path); 174 | } 175 | dir += ++i < list.length ? (sep + list[i]) : ''; 176 | } 177 | 178 | return; 179 | } 180 | 181 | return; 182 | } 183 | fs.mkdirSync(this.path); 184 | } 185 | } 186 | 187 | path2File(path: string, fileFilter?: RegExp[], dirFilter?: RegExp[]): File[] { 188 | const list: File[] = []; 189 | 190 | if (path !== '.' && path !== '..') { 191 | const f = new File(path); 192 | 193 | if (f.isDir()) { 194 | if (dirFilter) { 195 | for (const reg of dirFilter) { 196 | if (reg.test(f.name)) { 197 | list.push(f); 198 | break; 199 | } 200 | } 201 | } else { 202 | list.push(f); 203 | } 204 | } else { 205 | if (fileFilter) { 206 | for (const reg of fileFilter) { 207 | if (reg.test(f.name)) { 208 | list.push(f); 209 | break; 210 | } 211 | } 212 | } else { 213 | list.push(f); 214 | } 215 | } 216 | } 217 | 218 | return list; 219 | } 220 | 221 | getList(fileFilter?: RegExp[], dirFilter?: RegExp[]): File[] { 222 | const list: File[] = []; 223 | 224 | fs.readdirSync(this.path).forEach((str: string) => { 225 | if (str !== '.' && str !== '..') { 226 | const f = new File(this.path + sep + str); 227 | 228 | if (f.isDir()) { 229 | if (dirFilter) { 230 | for (const reg of dirFilter) { 231 | if (reg.test(f.name)) { 232 | list.push(f); 233 | break; 234 | } 235 | } 236 | } else { 237 | list.push(f); 238 | } 239 | } else { 240 | if (fileFilter) { 241 | for (const reg of fileFilter) { 242 | if (reg.test(f.name)) { 243 | list.push(f); 244 | break; 245 | } 246 | } 247 | } else { 248 | list.push(f); 249 | } 250 | } 251 | } 252 | }); 253 | 254 | return list; 255 | } 256 | 257 | getAll(fileFilter?: RegExp[], dirFilter?: RegExp[]): File[] { 258 | const res: File[] = []; 259 | 260 | let fStack: File[] = this.getList(fileFilter); 261 | let f: File; 262 | 263 | while (fStack.length > 0) { 264 | f = fStack.pop() as File; 265 | if (f.isDir()) { 266 | fStack = fStack.concat(f.getList(fileFilter)); 267 | } 268 | res.push(f); 269 | } 270 | 271 | return File.filter(res, undefined, dirFilter); 272 | } 273 | 274 | copyRetainDir(baseDir: File, file: File) { 275 | this._copyRetainDir(baseDir, file); 276 | } 277 | 278 | copyFile(file: File) { 279 | fs.copyFileSync(file.path, this.path + File.sep + file.name); 280 | } 281 | 282 | copyList(dir: File, fileFilter?: RegExp[], dirFilter?: RegExp[]) { 283 | const fList = dir.getList(fileFilter, dirFilter); 284 | 285 | fList.forEach(f => { 286 | if (f.isFile()) { 287 | this.copyRetainDir(dir, f); 288 | } 289 | }); 290 | } 291 | 292 | copyAll(dir: File, fileFilter?: RegExp[], dirFilter?: RegExp[]) { 293 | const fList = dir.getAll(fileFilter, dirFilter); 294 | 295 | fList.forEach(f => { 296 | if (f.isFile()) { 297 | this.copyRetainDir(dir, f); 298 | } 299 | }); 300 | } 301 | 302 | //------------------------------------------------- 303 | 304 | read(encoding?: any): string { 305 | return fs.readFileSync(this.path, { encoding: encoding ?? 'utf8' }) as unknown as string; 306 | } 307 | 308 | write(str: string, options?: fs.WriteFileOptions) { 309 | fs.writeFileSync(this.path, str, options); 310 | } 311 | 312 | isExist(): boolean { 313 | return fs.existsSync(this.path); 314 | } 315 | 316 | isFile(): boolean { 317 | if (fs.existsSync(this.path)) { 318 | return fs.lstatSync(this.path).isFile(); 319 | } 320 | 321 | return false; 322 | } 323 | 324 | isDir(): boolean { 325 | if (fs.existsSync(this.path)) { 326 | return fs.lstatSync(this.path).isDirectory(); 327 | } 328 | 329 | return false; 330 | } 331 | 332 | getHash(hashName?: string): string { 333 | const hash = crypto.createHash(hashName ?? 'md5'); 334 | 335 | hash.update(fs.readFileSync(this.path)); 336 | 337 | return hash.digest('hex'); 338 | } 339 | 340 | getSize(): number { 341 | return fs.statSync(this.path).size; 342 | } 343 | 344 | toUri(): string { 345 | return `file://${this.toNoProtocolUri()}`; 346 | } 347 | 348 | toNoProtocolUri(): string { 349 | return `/${encodeURIComponent(this.path.replace(/\\/g, '/'))}`; 350 | } 351 | } -------------------------------------------------------------------------------- /src/node_utility/FileWatcher.ts: -------------------------------------------------------------------------------- 1 | import { File } from "./File"; 2 | import * as events from "events"; 3 | import type { FSWatcher} from "chokidar"; 4 | import { watch } from "chokidar"; 5 | 6 | export class FileWatcher { 7 | 8 | readonly file: File; 9 | private watcher?: FSWatcher; 10 | private selfWatcher?: FSWatcher; 11 | private isDir: boolean; 12 | private recursive: boolean; 13 | private _event: events.EventEmitter; 14 | 15 | onRename?: (file: File) => void; 16 | onChanged?: (file: File) => void; 17 | 18 | constructor(_file: File, _recursive = false) { 19 | this.file = _file; 20 | this.recursive = _recursive; 21 | this.isDir = this.file.isDir(); 22 | this._event = new events.EventEmitter(); 23 | } 24 | 25 | on(event: 'error', listener: (err: Error) => void): this; 26 | on(event: any, listener: (arg?: any) => void): this { 27 | this._event.on(event, listener); 28 | 29 | return this; 30 | } 31 | 32 | watch(): this { 33 | // 使用 chokidar 监控目录变化 34 | if (this.isDir && this.selfWatcher === undefined) { 35 | this.selfWatcher = watch(this.file.dir, { ignoreInitial: true, depth: 0 }); 36 | this.selfWatcher.on('unlink', (removedPath) => { 37 | if (removedPath && removedPath.endsWith(this.file.name) && this.onRename) { 38 | this.onRename(this.file); 39 | } 40 | }); 41 | this.selfWatcher.on('error', (err) => { 42 | this._event.emit('error', err); 43 | }); 44 | } 45 | 46 | // 使用 chokidar 监控文件变化 47 | if (this.watcher === undefined) { 48 | this.watcher = watch(this.file.path, { ignoreInitial: true, persistent: true, depth: this.recursive ? undefined : 0 }); 49 | this.watcher.on('change', (changedPath) => { 50 | if (this.onChanged) { 51 | this.onChanged(this.isDir ? File.fromArray([this.file.path, changedPath]) : this.file); 52 | } 53 | }); 54 | this.watcher.on('unlink', (removedPath) => { 55 | if (this.onRename) { 56 | this.onRename(this.isDir ? File.fromArray([this.file.path, removedPath]) : this.file); 57 | } 58 | }); 59 | this.watcher.on('error', (err) => { 60 | this._event.emit('error', err); 61 | }); 62 | } 63 | 64 | return this; 65 | } 66 | 67 | close() { 68 | if (this.selfWatcher) { 69 | void this.selfWatcher.close(); 70 | this.selfWatcher = undefined; 71 | } 72 | if (this.watcher) { 73 | void this.watcher.close(); 74 | this.watcher = undefined; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/node_utility/Time.ts: -------------------------------------------------------------------------------- 1 | let instance: Time; 2 | 3 | //Format Example: 2019/9/22|10:12:23|GMT... 4 | 5 | export type TimeInfo = { 6 | year: number; 7 | month: number; 8 | date: number; 9 | 10 | hour: number; 11 | minute: number; 12 | second: number; 13 | 14 | region: string; 15 | } 16 | 17 | export class Time { 18 | 19 | private date: Date; 20 | private separater: string; 21 | 22 | private constructor() { 23 | this.date = new Date(); 24 | this.separater = '|'; 25 | } 26 | 27 | static getInstance(): Time { 28 | if (instance) { 29 | return instance; 30 | } 31 | instance = new Time(); 32 | 33 | return instance; 34 | } 35 | 36 | getTimeStamp(): string { 37 | this.date.setTime(Date.now()); 38 | let dateStr = this.getDateString(); 39 | const tList = this.date.toTimeString().split(' '); 40 | 41 | dateStr += this.separater + tList[0] + this.separater + tList[1]; 42 | 43 | return dateStr; 44 | } 45 | 46 | private getDateString(): string { 47 | return `${this.date.getFullYear().toString() }/${ (this.date.getMonth() + 1).toString() }/${ this.date.getDate().toString()}`; 48 | } 49 | 50 | getTimeInfo(): TimeInfo { 51 | 52 | this.date.setTime(Date.now()); 53 | 54 | return { 55 | year: this.date.getFullYear(), 56 | month: this.date.getMonth(), 57 | date: this.date.getDate(), 58 | 59 | hour: this.date.getHours(), 60 | minute: this.date.getMinutes(), 61 | second: this.date.getSeconds(), 62 | 63 | region: this.date.toTimeString().split(' ')[1] 64 | }; 65 | } 66 | 67 | parse(timeStamp: string): TimeInfo { 68 | 69 | const fieldList = timeStamp.split('|'); 70 | const yearField = fieldList[0].split('/'); 71 | const timeField = fieldList[1].split(':'); 72 | 73 | return { 74 | year: Number.parseInt(yearField[0]), 75 | month: Number.parseInt(yearField[1]), 76 | date: Number.parseInt(yearField[2]), 77 | 78 | hour: Number.parseInt(timeField[0]), 79 | minute: Number.parseInt(timeField[1]), 80 | second: Number.parseInt(timeField[2]), 81 | 82 | region: fieldList[2] 83 | }; 84 | } 85 | 86 | stringify(timeData: TimeInfo): string { 87 | return `${timeData.year.toString() }/${ timeData.month.toString() }/${ timeData.date.toString() }|${ 88 | timeData.hour.toString() }:${ timeData.minute.toString() }:${ timeData.second.toString() }|${ 89 | timeData.region}`; 90 | } 91 | 92 | setTimeSeparater(sep: string) { 93 | this.separater = sep; 94 | } 95 | 96 | getTimeSeparater(): string { 97 | return this.separater; 98 | } 99 | } -------------------------------------------------------------------------------- /src/node_utility/Utility.ts: -------------------------------------------------------------------------------- 1 | export function arrayDelRepetition(arr: T[]): T[] { 2 | return Array.from(new Set(arr)); 3 | } -------------------------------------------------------------------------------- /src/target/C251Target.ts: -------------------------------------------------------------------------------- 1 | 2 | import { PTarget } from './PTarget'; 3 | import { File } from '../node_utility/File'; 4 | import { ResourceManager } from '../ResourceManager'; 5 | 6 | export class C251Target extends PTarget { 7 | 8 | protected checkProject(target: any): Error | undefined { 9 | const targetOption = target['TargetOption']; 10 | 11 | if (targetOption === undefined) { 12 | return new Error(`This uVision project is not a C251 project, but have a 'uvproj' suffix!`); 13 | } 14 | const target251 = targetOption['Target251']; 15 | 16 | if (target251 === undefined) { 17 | return new Error(`This uVision project is not a C251 project, but have a 'uvproj' suffix!`); 18 | } 19 | const c251 = target251['C251']; 20 | 21 | if (c251 === undefined) { 22 | return new Error(`This uVision project is not a C251 project, but have a 'uvproj' suffix!`); 23 | } 24 | } 25 | 26 | protected getToolName(_target: any): string { 27 | return 'C251'; 28 | } 29 | 30 | protected getKeilPlatform(): string { 31 | return "C251"; 32 | } 33 | 34 | protected parseRefLines(_target: any, _lines: string[]): string[] { 35 | return []; 36 | } 37 | 38 | protected getOutputFolder(_target: any): string | undefined { 39 | return undefined; 40 | } 41 | 42 | protected getSysDefines(_target: any): string[] { 43 | return [ 44 | '__C251__', 45 | '__VSCODE_C251__', 46 | 'reentrant=', 47 | 'compact=', 48 | 'small=', 49 | 'large=', 50 | 'data=', 51 | 'idata=', 52 | 'pdata=', 53 | 'bdata=', 54 | 'edata=', 55 | 'xdata=', 56 | 'code=', 57 | 'bit=char', 58 | 'sbit=char', 59 | 'sfr=char', 60 | 'sfr16=int', 61 | 'sfr32=int', 62 | 'interrupt=', 63 | 'using=', 64 | 'far=', 65 | '_at_=', 66 | '_priority_=', 67 | '_task_=' 68 | ]; 69 | } 70 | 71 | protected getRteDefines(target: any): string[] { 72 | if (target) { 73 | const components = target['components']['component']; 74 | const apis = target['apis']['api']; 75 | 76 | if (Array.isArray(components) || Array.isArray(apis)) { 77 | return ["_RTE_"]; 78 | } 79 | } 80 | 81 | return []; 82 | } 83 | 84 | protected getSystemIncludes(target: any): string[] | undefined { 85 | const keilRootDir = new File(ResourceManager.getInstance().getKeilRootDir(this.getKeilPlatform())); 86 | const vendor = target['TargetOption']['TargetCommonOption']['Vendor']; 87 | const list = []; 88 | 89 | if (keilRootDir.isDir()) { 90 | const c251Inc = `${keilRootDir.path}${File.sep}C251${File.sep}INC`; 91 | const vendorInc = `${c251Inc}${File.sep}${vendor}`; 92 | const vendorDirFile = new File(vendorInc); 93 | 94 | list.push(c251Inc); 95 | if (vendorDirFile.isExist() && vendorDirFile.isDir()) { 96 | list.push(vendorInc); 97 | } 98 | 99 | return list; 100 | } 101 | 102 | return undefined; 103 | } 104 | 105 | protected getRTEIncludes(_target: any, _rteDom: any): string[] | undefined { 106 | return undefined; 107 | } 108 | 109 | protected getIncString(target: any): string { 110 | const c251 = target['TargetOption']['Target251']['C251']; 111 | 112 | return c251['VariousControls']['IncludePath']; 113 | } 114 | 115 | protected getDefineString(target: any): string { 116 | const c251 = target['TargetOption']['Target251']['C251']; 117 | 118 | return c251['VariousControls']['Define']; 119 | } 120 | 121 | protected getGroups(target: any): any[] { 122 | return target['Groups']['Group'] ?? []; 123 | } 124 | 125 | protected getProblemMatcher(): string[] { 126 | return ['$c251']; 127 | } 128 | protected getCStandard(_target: any): string { 129 | return 'c89'; 130 | } 131 | protected getCppStandard(_target: any): string { 132 | return 'c++17'; 133 | } 134 | protected getIntelliSenseMode(_target: any): string { 135 | return '${default}';//'gcc-x86'; 136 | } 137 | } -------------------------------------------------------------------------------- /src/target/C51Target.ts: -------------------------------------------------------------------------------- 1 | 2 | import { PTarget } from './PTarget'; 3 | import { File } from '../node_utility/File'; 4 | import { ResourceManager } from '../ResourceManager'; 5 | 6 | 7 | export class C51Target extends PTarget { 8 | 9 | protected checkProject(target: any): Error | undefined { 10 | const targetOption = target['TargetOption']; 11 | 12 | if (targetOption === undefined) { 13 | return new Error(`This uVision project is not a C51 project, but have a 'uvproj' suffix !`); 14 | } 15 | const target51 = targetOption['Target51']; 16 | 17 | if (target51 === undefined) { 18 | return new Error(`This uVision project is not a C51 project, but have a 'uvproj' suffix !`); 19 | } 20 | const c51 = target51['C51']; 21 | 22 | if (c51 === undefined) { 23 | return new Error(`This uVision project is not a C51 project, but have a 'uvproj' suffix !`); 24 | } 25 | } 26 | protected getToolName(_target: any): string { 27 | return "C51"; 28 | } 29 | protected getKeilPlatform(): string { 30 | return "C51"; 31 | } 32 | 33 | protected parseRefLines(_target: any, _lines: string[]): string[] { 34 | return []; 35 | } 36 | 37 | protected getOutputFolder(_target: any): string | undefined { 38 | return undefined; 39 | } 40 | 41 | protected getSysDefines(_target: any): string[] { 42 | return [ 43 | '__C51__', 44 | '__VSCODE_C51__', 45 | 'reentrant=', 46 | 'compact=', 47 | 'small=', 48 | 'large=', 49 | 'data=', 50 | 'idata=', 51 | 'pdata=', 52 | 'bdata=', 53 | 'xdata=', 54 | 'code=', 55 | 'bit=char', 56 | 'sbit=char', 57 | 'sfr=char', 58 | 'sfr16=int', 59 | 'sfr32=int', 60 | 'interrupt=', 61 | 'using=', 62 | '_at_=', 63 | '_priority_=', 64 | '_task_=' 65 | ]; 66 | } 67 | 68 | protected getRteDefines(target: any): string[] { 69 | if (target) { 70 | const components = target['components']['component']; 71 | const apis = target['apis']['api']; 72 | 73 | if (Array.isArray(components) || Array.isArray(apis)) { 74 | return ["_RTE_"]; 75 | } 76 | } 77 | 78 | return []; 79 | } 80 | 81 | protected getSystemIncludes(target: any): string[] | undefined { 82 | const keilRootDir = new File(ResourceManager.getInstance().getKeilRootDir(this.getKeilPlatform())); 83 | const vendor = target['TargetOption']['TargetCommonOption']['Vendor']; 84 | const list = []; 85 | 86 | if (keilRootDir.isDir()) { 87 | const c51Inc = `${keilRootDir.path}${File.sep}C51${File.sep}INC`; 88 | const vendorInc = `${c51Inc}${File.sep}${vendor}`; 89 | const vendorDirFile = new File(vendorInc); 90 | 91 | list.push(c51Inc); 92 | if (vendorDirFile.isExist() && vendorDirFile.isDir()) { 93 | list.push(vendorInc); 94 | } 95 | 96 | return list; 97 | } 98 | 99 | return undefined; 100 | } 101 | 102 | protected getRTEIncludes(_target: any, _rteDom: any): string[] | undefined { 103 | return undefined; 104 | } 105 | 106 | protected getIncString(target: any): string { 107 | const target51 = target['TargetOption']['Target51']['C51']; 108 | 109 | return target51['VariousControls']['IncludePath']; 110 | } 111 | 112 | protected getDefineString(target: any): string { 113 | const target51 = target['TargetOption']['Target51']['C51']; 114 | 115 | return target51['VariousControls']['Define']; 116 | } 117 | 118 | protected getGroups(target: any): any[] { 119 | return target['Groups']['Group'] ?? []; 120 | } 121 | 122 | protected getProblemMatcher(): string[] { 123 | return ['$c51']; 124 | } 125 | 126 | protected getCStandard(_target: any): string { 127 | return 'c89'; 128 | } 129 | protected getCppStandard(_target: any): string { 130 | return 'c++17'; 131 | } 132 | protected getIntelliSenseMode(_target: any): string { 133 | return '${default}';//'gcc-x86'; 134 | } 135 | } -------------------------------------------------------------------------------- /src/target/PTarget.ts: -------------------------------------------------------------------------------- 1 | import type { IView } from "../core/IView"; 2 | import { EventEmitter as EventsEmitter } from 'events'; 3 | import { File } from "../node_utility/File"; 4 | import type { KeilProjectInfo } from "../core/KeilProjectInfo"; 5 | import type { OutputChannel } from "vscode"; 6 | import { commands, l10n, window } from "vscode"; 7 | import { FileGroup } from "../core/FileGroup"; 8 | import { normalize, resolve } from 'path'; 9 | import { ResourceManager } from "../ResourceManager"; 10 | import { Source } from "../core/Source"; 11 | import { closeSync, openSync, readSync, statSync, watchFile, writeFileSync } from "fs"; 12 | import { spawn } from "child_process"; 13 | import { decode } from "iconv-lite"; 14 | import * as yaml from 'js-yaml'; 15 | import { CompileCommand, CppProperty } from "./comm"; 16 | import path = require("path"); 17 | 18 | export type UVisonInfo = { 19 | schemaVersion: string | undefined; 20 | } 21 | 22 | export abstract class PTarget implements IView { 23 | 24 | prjID: string; 25 | label: string; 26 | tooltip?: string | undefined; 27 | contextVal?: string | undefined = 'Target'; 28 | icons?: { light: string; dark: string; } = { 29 | light: 'Class_16x', 30 | dark: 'Class_16x' 31 | }; 32 | 33 | 34 | //------------- 35 | 36 | readonly targetName: string; 37 | 38 | protected _event: EventsEmitter; 39 | protected project: KeilProjectInfo; 40 | protected cppConfigName: string; 41 | protected targetDOM: any; 42 | protected rteDom: any; 43 | protected uvInfo: UVisonInfo; 44 | protected fGroups: FileGroup[]; 45 | protected includes: Set; 46 | protected defines: Set; 47 | 48 | private uv4LogFile: File; 49 | // private uv4LogLockFileWatcher: FileWatcher; 50 | private isTaskRunning = false; 51 | private taskChannel: OutputChannel | undefined; 52 | private clangdContext: string | undefined; 53 | 54 | constructor(prjInfo: KeilProjectInfo, uvInfo: UVisonInfo, targetDOM: any, rteDom: any) { 55 | this._event = new EventsEmitter(); 56 | this.project = prjInfo; 57 | this.targetDOM = targetDOM; 58 | this.rteDom = rteDom; 59 | this.uvInfo = uvInfo; 60 | this.prjID = prjInfo.prjID; 61 | this.targetName = `${targetDOM['TargetName']}`; 62 | this.label = this.targetName; 63 | this.tooltip = this.targetName; 64 | this.cppConfigName = this.getCppConfigName(prjInfo, this.targetName); 65 | this.includes = new Set(); 66 | this.defines = new Set(); 67 | this.fGroups = []; 68 | 69 | this.uv4LogFile = new File(path.posix.join(this.project.vscodeDir.path, `${this.targetName}_uv4.log`)); 70 | } 71 | 72 | private getCppConfigName(project: KeilProjectInfo, target: string): string { 73 | if (project.isMultiplyProject) { 74 | return `${target} for ${project.uvprjFile.noSuffixName}`; 75 | } 76 | 77 | return target; 78 | } 79 | 80 | on(event: 'dataChanged', listener: () => void): void; 81 | on(event: any, listener: () => void): void { 82 | this._event.on(event, listener); 83 | } 84 | 85 | private lastCppConfig = ''; 86 | 87 | private updateCppProperties(cStandard: string, cppStandard: string, intelliSenseMode: string, compilerPath?: string, compilerArgs?: string[]) { 88 | 89 | const proFile = new File(path.posix.join(this.project.vscodeDir.path, 'c_cpp_properties.json')); 90 | const ccFile = new File(path.posix.join(this.project.workspaceDir!, 'compile_commands.json')); 91 | 92 | let cppProperties: any = { configurations: [], version: 4 }; 93 | 94 | if (proFile.isFile()) { 95 | try { 96 | cppProperties = JSON.parse(proFile.read()); 97 | } catch (error) { 98 | this.project.logger.log(`[Error] c_cpp_properties.json parse error: ${error}`); 99 | } 100 | } 101 | 102 | let compileCmds: CompileCommand[] = []; 103 | if (ccFile.isFile() && ccFile.isExist()) { 104 | try { 105 | compileCmds = JSON.parse(ccFile.read()); 106 | } catch (error) { 107 | this.project.logger.log(`[Error] c_cpp_properties.json parse error: ${error}`); 108 | } 109 | } 110 | 111 | const configurations: CppProperty[] = cppProperties['configurations']; 112 | const index = configurations.findIndex((conf) => conf.name === this.cppConfigName); 113 | 114 | // 提前将 Set 转换为数组,避免多次调用 Array.from 115 | const includeArray = Array.from(this.includes); 116 | const defineArray = Array.from(this.defines); 117 | compilerPath = compilerPath?.replace(/\\/g, '/'); // 替换反斜杠为正斜杠 118 | intelliSenseMode = intelliSenseMode.replace(/\\/g, '/'); // 替换反斜杠为正斜杠 119 | 120 | if (index === -1) { 121 | configurations.push({ 122 | name: `${this.cppConfigName}`, 123 | cStandard, 124 | cppStandard, 125 | compilerPath, 126 | compilerArgs, 127 | intelliSenseMode, 128 | includePath: includeArray, 129 | defines: defineArray 130 | }); 131 | } else { 132 | configurations[index]['compilerPath'] = compilerPath; 133 | configurations[index]['compilerArgs'] = compilerArgs; 134 | configurations[index]['cStandard'] = cStandard; 135 | configurations[index]['cppStandard'] = cppStandard; 136 | configurations[index]['intelliSenseMode'] = intelliSenseMode; 137 | configurations[index]['includePath'] = includeArray; 138 | configurations[index]['defines'] = defineArray; 139 | } 140 | 141 | const newConfig = JSON.stringify(cppProperties, undefined, 4); 142 | 143 | if (this.lastCppConfig !== newConfig) { 144 | proFile.write(newConfig); 145 | this.lastCppConfig = newConfig; 146 | } 147 | // 提前获取工作区目录,避免多次访问属性 148 | const workspaceDir = this.project.workspaceDir?.replace(/\\/g, '/') ?? "."; 149 | const incList = includeArray.map((inc) => `-I${inc}`); 150 | const defList = defineArray.map((def) => `-D${def}`); 151 | 152 | // 生成 .clangd 文件内容 153 | const clangdConfig = { 154 | CompileFlags: { 155 | Add: [ 156 | ...incList, 157 | ...defList, 158 | ...(compilerArgs ?? []) 159 | ], 160 | Compiler: compilerPath, 161 | } 162 | }; 163 | 164 | this.clangdContext = yaml.dump(clangdConfig, { 165 | noRefs: true, 166 | lineWidth: -1 167 | }); 168 | 169 | let files = this.fGroups.map(fg => fg.label).join(','); 170 | 171 | const ci = compileCmds.findIndex(item => item.configuration === this.cppConfigName); 172 | const workspacePath = workspaceDir.replace(workspaceDir, '.'); 173 | 174 | 175 | if (ci === -1) { 176 | compileCmds.push({ 177 | configuration: `${this.cppConfigName}`, 178 | directory: `${workspacePath}`, 179 | file: `**/{${files}}/**/*.c`, 180 | arguments: [ 181 | `${compilerPath}`, 182 | ...incList, 183 | ...defList, 184 | ...(compilerArgs ?? []), 185 | `-std=${cStandard}`, 186 | "-c", 187 | "{file}", 188 | "-o", 189 | "build/{file_dir}/{file_base}.o" 190 | ] 191 | }); 192 | } else { 193 | compileCmds[ci] = { 194 | configuration: `${this.cppConfigName}`, 195 | directory: workspacePath, 196 | file: `**/{${files}}/**/*.c`, 197 | arguments: [ 198 | `${compilerPath}`, 199 | ...incList, 200 | ...defList, 201 | ...(compilerArgs ?? []), 202 | `-std=${cStandard}`, 203 | "-c", 204 | "{file}", 205 | "-o", 206 | "build/{file_dir}/{file_base}.o" 207 | ] 208 | }; 209 | } 210 | 211 | ccFile.write(JSON.stringify(compileCmds, undefined, 4)); 212 | 213 | } 214 | 215 | updateClangdFile() { 216 | const clangdFile = new File(`${this.project.workspaceDir}${File.sep}.clangd`); 217 | 218 | if (this.clangdContext) { 219 | clangdFile.write(this.clangdContext); 220 | } else { 221 | this.project.logger.log(`[Error] .clangd file is empty`); 222 | } 223 | } 224 | 225 | async load(): Promise { 226 | 227 | // check target is valid 228 | const err = this.checkProject(this.targetDOM); 229 | 230 | if (err) { 231 | console.error(`check project failed, ${err}`); 232 | throw err; 233 | } 234 | 235 | const incListStr: string = this.getIncString(this.targetDOM); 236 | const defineListStr: string = this.getDefineString(this.targetDOM); 237 | const _groups: any = this.getGroups(this.targetDOM); 238 | const sysIncludes = this.getSystemIncludes(this.targetDOM); 239 | const rteIncludes = this.getRTEIncludes(this.targetDOM, this.rteDom); 240 | 241 | const cStandard = this.getCStandard(this.targetDOM); 242 | const cppStandard = this.getCppStandard(this.targetDOM); 243 | const intelliSenseMode = this.getIntelliSenseMode(this.targetDOM); 244 | 245 | // set includes 246 | this.includes.clear(); 247 | //this.includes.add('${workspaceFolder}/**'); 248 | 249 | if (rteIncludes !== undefined) 250 | this.includes.add("${workspaceFolder}/RTE/" + `_${this.targetName.replace(" ", "_")}`); 251 | 252 | if (sysIncludes) { 253 | sysIncludes.forEach((incPath) => { 254 | incPath = incPath.trim(); 255 | if (incPath !== '') 256 | this.includes.add(incPath); 257 | }); 258 | } 259 | if (rteIncludes) { 260 | rteIncludes.forEach((incPath) => { 261 | incPath = incPath.trim(); 262 | if (incPath !== '') 263 | this.includes.add(incPath); 264 | }); 265 | } 266 | 267 | const prjIncList = incListStr.split(';'); 268 | const workspaceDir = `${this.project.workspaceDir}${File.sep}`; 269 | 270 | prjIncList.forEach((incPath) => { 271 | incPath = incPath.trim(); 272 | if (incPath !== '') { 273 | incPath = normalize(this.project.uvprjFile.dir + File.sep + incPath); 274 | incPath = incPath.replace(workspaceDir, ''); 275 | this.includes.add(incPath); 276 | } 277 | }) 278 | 279 | 280 | ResourceManager.getInstance().getProjectFileLocationList().forEach( 281 | filePath => { 282 | this.includes.add(this.project.toAbsolutePath(filePath)); 283 | } 284 | ); 285 | 286 | 287 | // set defines 288 | this.defines.clear(); 289 | 290 | // add user macros 291 | defineListStr.split(/,|\s+/).forEach((define) => { 292 | if (define.trim() !== '') { 293 | this.defines.add(define); 294 | } 295 | }); 296 | 297 | // RTE macros 298 | this.getRteDefines(this.rteDom).forEach((define) => { 299 | this.defines.add(define); 300 | }); 301 | 302 | // add system macros 303 | this.getSysDefines(this.targetDOM).forEach((define) => { 304 | this.defines.add(define); 305 | }); 306 | 307 | // set file groups 308 | this.fGroups = []; 309 | 310 | let groups: any[]; 311 | 312 | if (Array.isArray(_groups)) { 313 | groups = _groups; 314 | } else { 315 | groups = [_groups]; 316 | } 317 | 318 | for (const group of groups) { 319 | const groupName = String(group['GroupName']); 320 | 321 | if (!group['Files']) { 322 | this.project.logger.log(`[Warn] 发现无效的文件组,Group: ${groupName}`); 323 | continue; 324 | } 325 | 326 | let isGroupExcluded = false; 327 | const gOption = group['GroupOption']; 328 | 329 | if (gOption) { // check group is excluded 330 | const gComProps = gOption['CommonProperty']; 331 | 332 | if (gComProps) { 333 | isGroupExcluded = (gComProps['IncludeInBuild'] === 0); 334 | } 335 | } 336 | const nGrp = new FileGroup(this.prjID, groupName, isGroupExcluded); 337 | 338 | // 处理文件列表(使用解构+管道操作优化) 339 | const fileList = [group['Files']] 340 | .flat() 341 | .flatMap(list => 342 | [list?.File] 343 | .flat() 344 | .filter(Boolean) 345 | ) 346 | .map(fItem => ({ 347 | ...fItem, 348 | absPath: this.project.toAbsolutePath(fItem.FilePath) 349 | })) 350 | .filter(({ FilePath: filePath }) => { 351 | const isValid = filePath?.trim(); 352 | 353 | !isValid && this.project.logger.log(`[Warn] 发现无效文件路径,Group: ${groupName}`); 354 | 355 | return isValid; 356 | }); 357 | 358 | // 使用管道操作处理文件列表 359 | fileList.forEach(({ FileOption: fileOption, absPath }) => { 360 | const isExcluded = isGroupExcluded || 361 | fileOption?.CommonProperty?.IncludeInBuild === 0; 362 | 363 | nGrp.sources.push(new Source(this.prjID, new File(absPath), !isExcluded)); 364 | }); 365 | 366 | this.fGroups.push(nGrp); 367 | 368 | } 369 | 370 | const toolName = this.getToolName(this.targetDOM); 371 | const compilerPath = ResourceManager.getInstance().getCompilerPath(this.getKeilPlatform(), toolName); 372 | 373 | const compilerArgs = toolName === 'ARMCLANG' ? ['--target=arm-arm-none-eabi'] : undefined; 374 | 375 | this.updateCppProperties(cStandard, cppStandard, intelliSenseMode, compilerPath, compilerArgs); 376 | 377 | this.updateSourceRefs(); 378 | } 379 | 380 | private runAsyncTask(name: string, type: 'b' | 'r' | 'f' = 'b') { 381 | if (this.isTaskRunning) { 382 | const msg = l10n.t('Task isRuning Please wait it finished try !'); 383 | 384 | window.showWarningMessage(msg); 385 | 386 | return; 387 | } 388 | this.isTaskRunning = true; 389 | 390 | writeFileSync(this.uv4LogFile.path, ''); 391 | if (this.taskChannel !== undefined) { 392 | this.taskChannel.dispose(); 393 | } 394 | this.taskChannel = window.createOutputChannel("keil Build"); 395 | this.taskChannel.appendLine(`Start to ${name} target ${this.label}`); 396 | this.taskChannel.show(); 397 | 398 | const fd = openSync(this.uv4LogFile.path, 'a+'); 399 | let curPos = 0; 400 | const buf = Buffer.alloc(4096); 401 | 402 | const logWatcher = watchFile(this.uv4LogFile.path, { persistent: true, interval: 1000 }, (curr, prev) => { 403 | if (curr.mtime > prev.mtime) { 404 | const numRead = readSync(fd, buf, 0, 4096, prev.size); 405 | 406 | if (numRead > 0) { 407 | curPos += numRead; 408 | const txt = this.dealLog(buf.subarray(0, numRead)); 409 | 410 | this.taskChannel?.appendLine(txt); 411 | } 412 | } 413 | }); 414 | 415 | const execCommand = spawn(`${ResourceManager.getInstance().getKeilUV4Path(this.getKeilPlatform())}`, 416 | [ 417 | `-${type}`, `${this.project.uvprjFile.path}`, 418 | '-j0', 419 | '-t', `${this.targetName}`, 420 | '-o', `${this.uv4LogFile.path}` 421 | ], 422 | { 423 | cwd: resolve(__dirname, "./"), 424 | stdio: ['pipe', 'pipe', 'pipe'] 425 | } 426 | ); 427 | 428 | execCommand.on('close', (_code) => { 429 | this.isTaskRunning = false; 430 | // let execSync = require('child_process').execSync; 431 | // execSync('sleep ' + 5); 432 | // await this.sleep(20); 433 | const stats = statSync(this.uv4LogFile.path); 434 | 435 | while (curPos < stats.size) { 436 | const numRead = readSync(fd, buf, 0, 4096, curPos); 437 | 438 | if (numRead > 0) { 439 | curPos += numRead; 440 | const txt = this.dealLog(buf.subarray(0, numRead)); 441 | 442 | this.taskChannel?.appendLine(txt); 443 | } 444 | } 445 | this.taskChannel?.appendLine(`Build Finished!`); 446 | closeSync(fd); 447 | logWatcher.removeAllListeners(); 448 | // watcher.dispose(); 449 | commands.executeCommand('workbench.action.focusActiveEditorGroup'); 450 | }); 451 | 452 | } 453 | 454 | dealLog(logTxt: Buffer): string { 455 | let logStr = decode(logTxt, 'cp936'); 456 | const srcFileExp = /((\.\.\/)?.*\..\(\d+\)):/g; 457 | 458 | if (srcFileExp.test(logStr)) { 459 | const prjRoot = this.project.uvprjFile.dir; 460 | 461 | logStr = logStr.replace(srcFileExp, function (_match, str) { 462 | return normalize(prjRoot + File.sep + str); 463 | }); 464 | } 465 | 466 | return logStr; 467 | 468 | } 469 | 470 | build() { 471 | this.runAsyncTask('Build', 'b'); 472 | } 473 | 474 | rebuild() { 475 | this.runAsyncTask('Rebuild', 'r'); 476 | } 477 | 478 | download() { 479 | this.runAsyncTask('Download', 'f'); 480 | } 481 | 482 | private refCache = new Map(); 483 | 484 | updateSourceRefs() { 485 | const rePath = this.getOutputFolder(this.targetDOM); 486 | 487 | if (!rePath) return; 488 | 489 | const outPath = this.project.toAbsolutePath(rePath); 490 | 491 | this.fGroups.forEach(group => { 492 | group.sources.forEach(source => { 493 | if (!source.enable) return; 494 | 495 | const cacheKey = `${outPath}|${source.file.noSuffixName}`; 496 | const cached = this.refCache.get(cacheKey); 497 | 498 | if (cached) { 499 | source.children = cached; 500 | 501 | return; 502 | } 503 | 504 | const refFile = File.fromArray([outPath, `${source.file.noSuffixName}.d`]); 505 | 506 | if (refFile.isFile()) { 507 | const refContent = refFile.read(); 508 | const refFileList = this.parseRefLines(this.targetDOM, refContent.split(/\r\n|\n/)) 509 | .map(rePath => this.project.toAbsolutePath(rePath)); 510 | const sources = refFileList.map(refFilePath => 511 | new Source(source.prjID, new File(refFilePath)) 512 | ); 513 | 514 | this.refCache.set(cacheKey, sources); 515 | source.children = sources; 516 | } 517 | }); 518 | }); 519 | this._event.emit('dataChanged'); 520 | } 521 | 522 | close() { 523 | // this.uv4LogLockFileWatcher.close(); 524 | } 525 | 526 | getChildViews(): IView[] | undefined { 527 | return this.fGroups; 528 | } 529 | 530 | protected abstract checkProject(target: any): Error | undefined; 531 | 532 | protected abstract getIncString(target: any): string; 533 | protected abstract getDefineString(target: any): string; 534 | protected abstract getSysDefines(target: any): string[]; 535 | protected abstract getRteDefines(target: any): string[]; 536 | protected abstract getGroups(target: any): any[]; 537 | protected abstract getSystemIncludes(target: any): string[] | undefined; 538 | protected abstract getRTEIncludes(target: any, rteDom: any): string[] | undefined; 539 | 540 | protected abstract getOutputFolder(target: any): string | undefined; 541 | protected abstract parseRefLines(target: any, lines: string[]): string[]; 542 | 543 | protected abstract getProblemMatcher(): string[]; 544 | 545 | protected abstract getKeilPlatform(): string; 546 | protected abstract getToolName(target: any): string; 547 | 548 | protected abstract getCStandard(target: any): string; 549 | protected abstract getCppStandard(target: any): string; 550 | protected abstract getIntelliSenseMode(target: any): string; 551 | 552 | } -------------------------------------------------------------------------------- /src/target/comm.ts: -------------------------------------------------------------------------------- 1 | export type CompileCommand = { 2 | configuration: string, 3 | directory: string, 4 | file: string, 5 | arguments: string[] 6 | } 7 | 8 | export type CppProperty = { 9 | name: string, 10 | intelliSenseMode: string, 11 | compilerPath: string | undefined, 12 | cStandard: string, 13 | cppStandard: string, 14 | compilerArgs: string[] | undefined, 15 | includePath: string[] | undefined, 16 | defines: string[] | undefined 17 | } -------------------------------------------------------------------------------- /src/ui/ProjectExplorer.ts: -------------------------------------------------------------------------------- 1 | import { XMLParser } from "fast-xml-parser"; 2 | import { statSync, readFileSync, readdirSync } from "fs"; 3 | import { dirname, extname, join, normalize, resolve } from "path"; 4 | import type { 5 | Event, ExtensionContext, ProviderResult, TreeDataProvider, 6 | OutputChannel, 7 | StatusBarItem 8 | } from "vscode"; 9 | import { commands, EventEmitter, l10n, 10 | TreeItem, TreeItemCollapsibleState, Uri, workspace, window 11 | } from "vscode"; 12 | 13 | import type { IView } from "../core/IView"; 14 | import { KeilProject } from "../core/KeilProject"; 15 | import { Source } from "../core/Source"; 16 | import { ResourceManager } from "../ResourceManager"; 17 | import { File } from '../node_utility/File'; 18 | import type { PTarget } from "../target/PTarget"; 19 | 20 | 21 | export class ProjectExplorer implements TreeDataProvider { 22 | 23 | private itemClickCommand = 'Item.Click'; 24 | 25 | onDidChangeTreeData: Event; 26 | private viewEvent: EventEmitter; 27 | 28 | private prjList: Map; 29 | private curActiveProject: KeilProject | undefined; 30 | private workspacePath: string | undefined; 31 | 32 | constructor(context: ExtensionContext, private channel: OutputChannel, private myStatusBarItem: StatusBarItem) { 33 | this.prjList = new Map(); 34 | this.viewEvent = new EventEmitter(); 35 | this.onDidChangeTreeData = this.viewEvent.event; 36 | context.subscriptions.push(window.registerTreeDataProvider('project', this)); 37 | context.subscriptions.push(commands.registerCommand(this.itemClickCommand, (item: IView) => this.onItemClick(item))); 38 | } 39 | private parseUvmpwFile(uvwPath: string, xmlParser: XMLParser): string[] { 40 | try { 41 | const uvmpwXml = readFileSync(uvwPath); 42 | const uvmpw = xmlParser.parse(uvmpwXml); 43 | const projectList = uvmpw['ProjectWorkspace']['project']; 44 | 45 | if (Array.isArray(projectList)) { 46 | return projectList.map(p => { 47 | const path = p['PathAndName'] as string; 48 | 49 | return resolve(dirname(uvwPath), path); 50 | }); 51 | } 52 | } catch (error) { 53 | this.channel.appendLine(`Error parsing .uvmpw file ${uvwPath}: ${error}`); 54 | } 55 | 56 | return []; 57 | } 58 | async loadWorkspace() { 59 | this.channel.show(); 60 | if (!workspace.workspaceFolders || workspace.workspaceFolders.length === 0) { 61 | this.channel.appendLine('No workspace folders found.'); 62 | 63 | return; 64 | } 65 | 66 | this.workspacePath = workspace.workspaceFile && workspace.workspaceFile.toString().startsWith('file:') 67 | ? dirname(workspace.workspaceFile.fsPath) 68 | : workspace.workspaceFolders[0].uri.fsPath; 69 | 70 | const prjWorkspace = new File(this.workspacePath); 71 | const searchDepth = ResourceManager.getInstance().getProjectFileFindMaxDepth(); 72 | 73 | if (!prjWorkspace.isDir()) { 74 | this.channel.appendLine('Error: this assistant works only in a folder.'); 75 | 76 | return; 77 | } 78 | 79 | this.channel.appendLine('search uvprj[x] project file >>>>> '); 80 | const excludeList = ResourceManager.getInstance().getProjectExcludeList(); 81 | let uvList: string[] = []; 82 | 83 | // Multiply project workspace 84 | const uvmwList = await this.findProject(prjWorkspace.path, [/\.uvmpw$/i], searchDepth); 85 | 86 | if (uvmwList && uvmwList.length > 0) { 87 | const options = { 88 | attributeNamePrefix: "@_", 89 | attrNodeName: "attr", //default is 'false' 90 | textNodeName: "#text", 91 | ignoreAttributes: false, 92 | ignoreNameSpace: false, 93 | allowBooleanAttributes: false, 94 | parseNodeValue: true, 95 | parseAttributeValue: false, 96 | trimValues: true, 97 | cdataTagName: "__cdata", //default is 'false' 98 | cdataPositionChar: "\\c", 99 | parseTrueNumberOnly: false, 100 | arrayMode: false, //"strict" 101 | // attrValueProcessor: (val: any, attrName: any) => decode(val, { isAttributeValue: true }),//default is a=>a 102 | // tagValueProcessor: (val: any, tagName: any) => decode(val), //default is a=>a 103 | stopNodes: ["parse-me-as-string"] 104 | }; 105 | const xmlParser = new XMLParser(options); 106 | 107 | uvList = uvmwList.flatMap(uvwPath => this.parseUvmpwFile(uvwPath, xmlParser)); 108 | } 109 | 110 | // Search for .uvproj and .uvprojx files if no .uvmpw files are found 111 | if (uvList.length === 0) { 112 | uvList = await this.findProject(prjWorkspace.path, [/\.uvproj[x]?$/i], searchDepth); 113 | } 114 | 115 | // Add additional project file locations 116 | uvList.push(...ResourceManager.getInstance().getProjectFileLocationList()); 117 | 118 | // Filter out excluded files 119 | uvList = uvList.filter(path => !excludeList.includes(extname(path).toLowerCase())); 120 | 121 | const hasMultiplyProject = uvList.length > 1; //Multiply 122 | 123 | // Load each project file 124 | for (const uvPath of uvList) { 125 | try { 126 | await this.openProject(uvPath, hasMultiplyProject); 127 | } catch (error) { 128 | this.channel.appendLine(`Error: open project ${error}`); 129 | window.showErrorMessage(`Failed to open project: '${uvPath}', Error: ${error}`); 130 | } 131 | } 132 | } 133 | 134 | async findProject(dir: string, fileFilter?: RegExp[], deep = 0): Promise { 135 | const list: string[] = []; 136 | const items = readdirSync(dir); 137 | 138 | for (const item of items) { 139 | if (item === '.' || item === '..') { 140 | continue; 141 | } 142 | const fullPath = join(dir, item); 143 | const stat = statSync(fullPath); 144 | 145 | if (stat.isFile()) { 146 | if (fileFilter) { 147 | const extension = extname(item); // 优化:直接使用 extname 获取扩展名 148 | 149 | if (fileFilter.some(reg => reg.test(extension))) { 150 | list.push(fullPath); 151 | } 152 | } else { 153 | list.push(fullPath); 154 | } 155 | } else if (stat.isDirectory() && deep > 0) { 156 | const subFiles = await this.findProject(fullPath, fileFilter, deep - 1); 157 | 158 | list.push(...subFiles); 159 | } 160 | } 161 | 162 | return list; 163 | } 164 | 165 | async openProject(path: string, hasMultiplyProject: boolean): Promise { 166 | if (this.workspacePath === undefined) { 167 | const msg = l10n.t('The workspace directory is empty, Goto open the workspace directory?'); 168 | const result = await window.showInformationMessage(msg, l10n.t('Ok'), l10n.t('Cancel')); 169 | 170 | if (result === l10n.t('Ok')) { 171 | this.openWorkspace(new File(dirname(path))); 172 | 173 | return; 174 | } 175 | if (result === l10n.t('Cancel')) { 176 | const fmsg = l10n.t('Error: open project'); 177 | const msg = l10n.t('Failed, The workspace Path'); 178 | const emsg = l10n.t('is NULL , Please use vscode open the project folder.'); 179 | const errorMsg = `${fmsg} ${path} ${msg} ${this.workspacePath} ${emsg}`; 180 | 181 | this.channel.appendLine(errorMsg); 182 | window.showErrorMessage(errorMsg); 183 | throw Error(errorMsg); 184 | } 185 | } 186 | const nPrj = new KeilProject(this.channel, new File(path), this.workspacePath, hasMultiplyProject); 187 | 188 | if (nPrj) { 189 | if (!this.prjList.has(nPrj.prjID)) { 190 | 191 | await nPrj.load(); 192 | nPrj.on('dataChanged', () => this.updateView()); 193 | this.prjList.set(nPrj.prjID, nPrj); 194 | 195 | if (this.curActiveProject === undefined) { 196 | this.curActiveProject = nPrj; 197 | this.curActiveProject.active(); 198 | } 199 | 200 | this.updateView(); 201 | 202 | return nPrj; 203 | } 204 | } 205 | 206 | return undefined; 207 | } 208 | 209 | async closeProject(pID: string) { 210 | const prj = this.prjList.get(pID); 211 | 212 | if (prj) { 213 | prj.deactive(); 214 | prj.close(); 215 | this.prjList.delete(pID); 216 | this.updateView(); 217 | } 218 | } 219 | 220 | 221 | async activeProject(view: IView) { 222 | this.curActiveProject?.deactive(); 223 | const project = this.prjList.get(view.prjID); 224 | 225 | project?.active(); 226 | this.curActiveProject = project; 227 | this.updateView(); 228 | 229 | } 230 | 231 | async switchTargetByProject(view: IView) { 232 | const prj = this.prjList.get(view.prjID); 233 | 234 | if (prj) { 235 | const tList = prj.getTargets(); 236 | const targetName = await window.showQuickPick(tList.map((ele) => ele.targetName), { 237 | canPickMany: false, 238 | placeHolder: l10n.t('please select a target name for keil project') 239 | }); 240 | 241 | if (targetName) { 242 | prj.setActiveTarget(targetName); 243 | } 244 | } 245 | } 246 | 247 | async statusBarSwitchTargetByProject() { 248 | if (this.curActiveProject) { 249 | const tList = this.curActiveProject.getTargets(); 250 | const targetName = await window.showQuickPick(tList.map((ele) => ele.targetName), { 251 | canPickMany: false, 252 | placeHolder: l10n.t('please select a target name for keil project') 253 | }); 254 | 255 | if (targetName) { 256 | this.curActiveProject?.setActiveTarget(targetName); 257 | } 258 | } 259 | } 260 | 261 | getTarget(view?: IView): PTarget | undefined { 262 | if (view) { 263 | const prj = this.prjList.get(view.prjID); 264 | 265 | if (prj) { 266 | const targets = prj.getTargets(); 267 | const index = targets.findIndex((target) => target.targetName === view.label); 268 | 269 | if (index !== -1) { 270 | return targets[index]; 271 | } 272 | } 273 | } else { // get active target 274 | if (this.curActiveProject) { 275 | return this.curActiveProject.getActiveTarget(); 276 | } else { 277 | window.showWarningMessage(l10n.t('Not found any active project !')); 278 | } 279 | } 280 | } 281 | 282 | updateView(v?: IView) { 283 | this.updateStatusBarItem(this.curActiveProject?.activeTargetName); 284 | this.viewEvent.fire(v!); 285 | } 286 | 287 | //---------------------------------- 288 | 289 | itemClickInfo: any = undefined; 290 | 291 | private async onItemClick(item: IView) { 292 | if (item.contextVal === 'Source') { 293 | const source = item as Source; 294 | const file = new File(normalize(source.file.path)); 295 | 296 | if (file.isFile()) { // file exist, open it 297 | 298 | let isPreview = true; 299 | 300 | if (this.itemClickInfo && 301 | this.itemClickInfo.name === file.path && 302 | this.itemClickInfo.time + 260 > Date.now()) { 303 | isPreview = false; 304 | } 305 | 306 | // reset prev click info 307 | this.itemClickInfo = { 308 | name: file.path, 309 | time: Date.now() 310 | }; 311 | 312 | window.showTextDocument(Uri.parse(file.toUri()), { preview: isPreview }); 313 | 314 | } else { 315 | const msg = l10n.t('Not found file'); 316 | 317 | window.showWarningMessage(`${msg}: ${source.file.path}`); 318 | } 319 | } 320 | } 321 | 322 | getTreeItem(element: IView): TreeItem { 323 | const res = new TreeItem(element.label); 324 | 325 | res.contextValue = element.contextVal; 326 | res.tooltip = element.tooltip; 327 | res.collapsibleState = element.getChildViews() === undefined ? 328 | TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed; 329 | 330 | if (element instanceof Source) { 331 | res.command = { 332 | title: element.label, 333 | command: this.itemClickCommand, 334 | arguments: [element] 335 | }; 336 | } 337 | 338 | if (element.icons) { 339 | res.iconPath = { 340 | light: ResourceManager.getInstance().getIconByName(element.icons.light), 341 | dark: ResourceManager.getInstance().getIconByName(element.icons.dark) 342 | }; 343 | } 344 | 345 | return res; 346 | } 347 | 348 | getChildren(element?: IView | undefined): ProviderResult { 349 | if (element === undefined) { 350 | return Array.from(this.prjList.values()); 351 | } else { 352 | return element.getChildViews(); 353 | } 354 | } 355 | 356 | openWorkspace(wsFile: File) { 357 | commands.executeCommand('vscode.openFolder', Uri.parse(wsFile.toUri())); 358 | } 359 | 360 | private updateStatusBarItem(prjName: string | undefined): void { 361 | if (prjName !== undefined) { 362 | this.myStatusBarItem.text = prjName; 363 | this.myStatusBarItem.tooltip = l10n.t('switch project target'); 364 | this.myStatusBarItem.show(); 365 | } else { 366 | this.myStatusBarItem.hide(); 367 | } 368 | } 369 | 370 | } -------------------------------------------------------------------------------- /syntaxes/a251.language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": ";" 4 | }, 5 | "indentationRules": { 6 | "increaseIndentPattern": "^[a-zA-Z_]\\w*\\s*:\\s*", 7 | "decreaseIndentPattern": "^[a-zA-Z_]\\w*\\s*:\\s*" 8 | } 9 | } -------------------------------------------------------------------------------- /syntaxes/a251.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "information_for_contributors": [ 3 | "251 assembly support, author: cl" 4 | ], 5 | "version": "1.0.0", 6 | "name": "a251", 7 | "scopeName": "source.asm.a251", 8 | "patterns": [ 9 | { 10 | "include": "#instruction" 11 | }, 12 | { 13 | "include": "#pseudoinstruction" 14 | }, 15 | { 16 | "include": "#operators" 17 | }, 18 | { 19 | "include": "#type" 20 | }, 21 | { 22 | "include": "#register" 23 | }, 24 | { 25 | "include": "#number" 26 | }, 27 | { 28 | "include": "#functionName" 29 | }, 30 | { 31 | "include": "#comment" 32 | }, 33 | { 34 | "include": "#string" 35 | }, 36 | { 37 | "include": "#jumpFuncName" 38 | } 39 | ], 40 | "repository": { 41 | "instruction": { 42 | "match": "(?i)\\b(MOV|MOVC|MOVX|PUSH|POP|XCH|XCHD|ADD|ADDC|SUBB|INC|DEC|MUL|DIV|DA|ANL|ORL|XRL|CLR|CPL|RL|RLC|RR|RRC|SWAP|SETB|JC|JNC|JB|JNB|JBC|ACALL|LCALL|RET|RETI|AJMP|LJMP|SJMP|JMP|JZ|JNZ|CJNE|DJNZ|NOP)\\b", 43 | "captures": { 44 | "1": { 45 | "name": "support.function.mnemonic.arithmetic.a51" 46 | } 47 | } 48 | }, 49 | "pseudoinstruction": { 50 | "match": "(?i)\\b(ORG|END|ALTNAME|INCLUDE|\\$TITLE|\\$NOLIST|\\$NOCODE)\\b", 51 | "captures": { 52 | "1": { 53 | "name": "keyword.control.import" 54 | } 55 | } 56 | }, 57 | "type": { 58 | "match": "(?i)\\b(EQU|SET|DATA|BYTE|WORD|BIT|DB|DW|DS)\\b", 59 | "captures": { 60 | "1": { 61 | "name": "entity.name.type" 62 | } 63 | } 64 | }, 65 | "operators": { 66 | "match": "\\+", 67 | "captures": { 68 | "0": { 69 | "name": "keyword.operator" 70 | } 71 | } 72 | }, 73 | "register": { 74 | "match": "\\b(R[0-7]|P[0-3]|PSW|A(?:CC)?|B|SP|DPL|DPH|PCON|TCON|TMOD|TL0|TL1|TH0|TH1|IE|IP|SCON|SBUF|CY|AC|F0|RS1|RS0|OV|P|TF1|TR1|TF0|TR0|IE1|IT1|IE0|IT0|EA|ES|ET1|EX1|ET0|EX0|PS|PT1|PX1|PT0|PX0|RD|WR|T1|T0|INT1|INT0|TXD|RXD|SM0|SM1|SM2|REN|TB8|RB8|TI|RI)\\b", 75 | "captures": { 76 | "1": { 77 | "name": "storage.other.register.a51" 78 | } 79 | } 80 | }, 81 | "number": { 82 | "match": "(?i)\\b([0-9A-F]+H|0x[0-9a-f]+)\\b", 83 | "captures": { 84 | "1": { 85 | "name": "constant.numeric" 86 | } 87 | } 88 | }, 89 | "comment": { 90 | "match": "(?i)(;.*)", 91 | "captures": { 92 | "1": { 93 | "name": "comment.line" 94 | } 95 | } 96 | }, 97 | "functionName": { 98 | "match": "^\\s*\\b([a-zA-Z_]\\w*)\\b\\s*:", 99 | "captures": { 100 | "1": { 101 | "name": "variable" 102 | } 103 | } 104 | }, 105 | "string": { 106 | "match": "(\".*\"|'.*')", 107 | "captures": { 108 | "1": { 109 | "name": "string" 110 | } 111 | } 112 | }, 113 | "jumpFuncName": { 114 | "match": ".*?\\b(JC|JNC|ACALL|LCALL|AJMP|LJMP|SJMP|JMP|JZ|JNZ)\\b\\s+([a-zA-Z_]\\w*)", 115 | "captures": { 116 | "1": { 117 | "patterns": [ 118 | { 119 | "include": "#instruction" 120 | } 121 | ] 122 | }, 123 | "2": { 124 | "name": "variable" 125 | } 126 | } 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /syntaxes/a51.language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": ";" 4 | }, 5 | "indentationRules": { 6 | "increaseIndentPattern": "^[a-zA-Z_]\\w*\\s*:\\s*", 7 | "decreaseIndentPattern": "^[a-zA-Z_]\\w*\\s*:\\s*" 8 | } 9 | } -------------------------------------------------------------------------------- /syntaxes/a51.snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "inst_MOV": { 3 | "prefix": "MOV", 4 | "body": [ 5 | "MOV ${1:A}, ${2:Rn}" 6 | ], 7 | "scope": "a51", 8 | "description": "8051 assembly instruction" 9 | }, 10 | "inst_MOVC": { 11 | "prefix": "MOVC", 12 | "body": [ 13 | "MOVC ${1:A}, ${2:value}" 14 | ], 15 | "scope": "a51", 16 | "description": "8051 assembly instruction" 17 | }, 18 | "inst_MOVX": { 19 | "prefix": "MOVX", 20 | "body": [ 21 | "MOVX ${1:A}, ${2:value}" 22 | ], 23 | "scope": "a51", 24 | "description": "8051 assembly instruction" 25 | }, 26 | "inst_PUSH": { 27 | "prefix": "PUSH", 28 | "body": [ 29 | "PUSH ${1:direct}" 30 | ], 31 | "scope": "a51", 32 | "description": "8051 assembly instruction" 33 | }, 34 | "inst_POP": { 35 | "prefix": "POP", 36 | "body": [ 37 | "POP ${1:direct}" 38 | ], 39 | "scope": "a51", 40 | "description": "8051 assembly instruction" 41 | }, 42 | "inst_XCH": { 43 | "prefix": "XCH", 44 | "body": [ 45 | "XCH ${1:A}, ${2:Rn}" 46 | ], 47 | "scope": "a51", 48 | "description": "8051 assembly instruction" 49 | }, 50 | "inst_XCHD": { 51 | "prefix": "XCHD", 52 | "body": [ 53 | "XCHD ${1:A}, ${2:value}" 54 | ], 55 | "scope": "a51", 56 | "description": "8051 assembly instruction" 57 | }, 58 | "inst_ADD": { 59 | "prefix": "ADD", 60 | "body": [ 61 | "ADD ${1:A}, ${2:Rn}" 62 | ], 63 | "scope": "a51", 64 | "description": "8051 assembly instruction" 65 | }, 66 | "inst_ADDC": { 67 | "prefix": "ADDC", 68 | "body": [ 69 | "ADDC ${1:A}, ${2:Rn}" 70 | ], 71 | "scope": "a51", 72 | "description": "8051 assembly instruction" 73 | }, 74 | "inst_SUBB": { 75 | "prefix": "SUBB", 76 | "body": [ 77 | "SUBB ${1:A}, ${2:Rn}" 78 | ], 79 | "scope": "a51", 80 | "description": "8051 assembly instruction" 81 | }, 82 | "inst_INC": { 83 | "prefix": "INC", 84 | "body": [ 85 | "INC ${1:A}" 86 | ], 87 | "scope": "a51", 88 | "description": "8051 assembly instruction" 89 | }, 90 | "inst_DEC": { 91 | "prefix": "DEC", 92 | "body": [ 93 | "DEC ${1:A}" 94 | ], 95 | "scope": "a51", 96 | "description": "8051 assembly instruction" 97 | }, 98 | "inst_MUL": { 99 | "prefix": "MUL", 100 | "body": [ 101 | "MUL ${1:AB}" 102 | ], 103 | "scope": "a51", 104 | "description": "8051 assembly instruction" 105 | }, 106 | "inst_DIV": { 107 | "prefix": "DIV", 108 | "body": [ 109 | "DIV ${1:AB}" 110 | ], 111 | "scope": "a51", 112 | "description": "8051 assembly instruction" 113 | }, 114 | "inst_DA": { 115 | "prefix": "DA", 116 | "body": [ 117 | "DA ${1:A}" 118 | ], 119 | "scope": "a51", 120 | "description": "8051 assembly instruction" 121 | }, 122 | "inst_ANL": { 123 | "prefix": "ANL", 124 | "body": [ 125 | "ANL ${1:A}, ${2:Rn}" 126 | ], 127 | "scope": "a51", 128 | "description": "8051 assembly instruction" 129 | }, 130 | "inst_ORL": { 131 | "prefix": "ORL", 132 | "body": [ 133 | "ORL ${1:A}, ${2:Rn}" 134 | ], 135 | "scope": "a51", 136 | "description": "8051 assembly instruction" 137 | }, 138 | "inst_XRL": { 139 | "prefix": "XRL", 140 | "body": [ 141 | "XRL ${1:A}, ${2:Rn}" 142 | ], 143 | "scope": "a51", 144 | "description": "8051 assembly instruction" 145 | }, 146 | "inst_CLR": { 147 | "prefix": "CLR", 148 | "body": [ 149 | "CLR ${1:A}" 150 | ], 151 | "scope": "a51", 152 | "description": "8051 assembly instruction" 153 | }, 154 | "inst_CPL": { 155 | "prefix": "CPL", 156 | "body": [ 157 | "CPL ${1:A}" 158 | ], 159 | "scope": "a51", 160 | "description": "8051 assembly instruction" 161 | }, 162 | "inst_RL": { 163 | "prefix": "RL", 164 | "body": [ 165 | "RL ${1:A}" 166 | ], 167 | "scope": "a51", 168 | "description": "8051 assembly instruction" 169 | }, 170 | "inst_RLC": { 171 | "prefix": "RLC", 172 | "body": [ 173 | "RLC ${1:A}" 174 | ], 175 | "scope": "a51", 176 | "description": "8051 assembly instruction" 177 | }, 178 | "inst_RR": { 179 | "prefix": "RR", 180 | "body": [ 181 | "RR ${1:A}" 182 | ], 183 | "scope": "a51", 184 | "description": "8051 assembly instruction" 185 | }, 186 | "inst_RRC": { 187 | "prefix": "RRC", 188 | "body": [ 189 | "RRC ${1:A}" 190 | ], 191 | "scope": "a51", 192 | "description": "8051 assembly instruction" 193 | }, 194 | "inst_SWAP": { 195 | "prefix": "SWAP", 196 | "body": [ 197 | "SWAP ${1:A}" 198 | ], 199 | "scope": "a51", 200 | "description": "8051 assembly instruction" 201 | }, 202 | "inst_SETB": { 203 | "prefix": "SETB", 204 | "body": [ 205 | "SETB ${1:C}" 206 | ], 207 | "scope": "a51", 208 | "description": "8051 assembly instruction" 209 | }, 210 | "inst_JC": { 211 | "prefix": "JC", 212 | "body": [ 213 | "JC ${1:rel}" 214 | ], 215 | "scope": "a51", 216 | "description": "8051 assembly instruction" 217 | }, 218 | "inst_JNC": { 219 | "prefix": "JNC", 220 | "body": [ 221 | "JNC ${1:rel}" 222 | ], 223 | "scope": "a51", 224 | "description": "8051 assembly instruction" 225 | }, 226 | "inst_JB": { 227 | "prefix": "JB", 228 | "body": [ 229 | "JB ${1:bit}, ${2:rel}" 230 | ], 231 | "scope": "a51", 232 | "description": "8051 assembly instruction" 233 | }, 234 | "inst_JNB": { 235 | "prefix": "JNB", 236 | "body": [ 237 | "JNB ${1:bit}, ${2:rel}" 238 | ], 239 | "scope": "a51", 240 | "description": "8051 assembly instruction" 241 | }, 242 | "inst_JBC": { 243 | "prefix": "JBC", 244 | "body": [ 245 | "JBC ${1:bit}, ${2:rel}" 246 | ], 247 | "scope": "a51", 248 | "description": "8051 assembly instruction" 249 | }, 250 | "inst_ACALL": { 251 | "prefix": "ACALL", 252 | "body": [ 253 | "ACALL ${1:addr11}" 254 | ], 255 | "scope": "a51", 256 | "description": "8051 assembly instruction" 257 | }, 258 | "inst_LCALL": { 259 | "prefix": "LCALL", 260 | "body": [ 261 | "LCALL ${1:addr16}" 262 | ], 263 | "scope": "a51", 264 | "description": "8051 assembly instruction" 265 | }, 266 | "inst_RET": { 267 | "prefix": "RET", 268 | "body": [ 269 | "RET" 270 | ], 271 | "scope": "a51", 272 | "description": "8051 assembly instruction" 273 | }, 274 | "inst_RETI": { 275 | "prefix": "RETI", 276 | "body": [ 277 | "RETI" 278 | ], 279 | "scope": "a51", 280 | "description": "8051 assembly instruction" 281 | }, 282 | "inst_AJMP": { 283 | "prefix": "AJMP", 284 | "body": [ 285 | "AJMP ${1:addr11}" 286 | ], 287 | "scope": "a51", 288 | "description": "8051 assembly instruction" 289 | }, 290 | "inst_LJMP": { 291 | "prefix": "LJMP", 292 | "body": [ 293 | "LJMP ${1:addr16}" 294 | ], 295 | "scope": "a51", 296 | "description": "8051 assembly instruction" 297 | }, 298 | "inst_SJMP": { 299 | "prefix": "SJMP", 300 | "body": [ 301 | "SJMP ${1:rel}" 302 | ], 303 | "scope": "a51", 304 | "description": "8051 assembly instruction" 305 | }, 306 | "inst_JMP": { 307 | "prefix": "JMP", 308 | "body": [ 309 | "JMP ${1:value}" 310 | ], 311 | "scope": "a51", 312 | "description": "8051 assembly instruction" 313 | }, 314 | "inst_JZ": { 315 | "prefix": "JZ", 316 | "body": [ 317 | "JZ ${1:rel}" 318 | ], 319 | "scope": "a51", 320 | "description": "8051 assembly instruction" 321 | }, 322 | "inst_JNZ": { 323 | "prefix": "JNZ", 324 | "body": [ 325 | "JNZ ${1:rel}" 326 | ], 327 | "scope": "a51", 328 | "description": "8051 assembly instruction" 329 | }, 330 | "inst_CJNE": { 331 | "prefix": "CJNE", 332 | "body": [ 333 | "CJNE ${1:A}, ${2:direct}, ${3:rel}" 334 | ], 335 | "scope": "a51", 336 | "description": "8051 assembly instruction" 337 | }, 338 | "inst_DJNZ": { 339 | "prefix": "DJNZ", 340 | "body": [ 341 | "DJNZ ${1:Rn}, ${2:rel}" 342 | ], 343 | "scope": "a51", 344 | "description": "8051 assembly instruction" 345 | }, 346 | "inst_NOP": { 347 | "prefix": "NOP", 348 | "body": [ 349 | "NOP" 350 | ], 351 | "scope": "a51", 352 | "description": "8051 assembly instruction" 353 | }, 354 | "pseudoinst_ORG": { 355 | "prefix": "ORG", 356 | "body": [ 357 | "ORG" 358 | ], 359 | "scope": "a51", 360 | "description": "8051 assembly pseudoinstruction" 361 | }, 362 | "pseudoinst_END": { 363 | "prefix": "END", 364 | "body": [ 365 | "END" 366 | ], 367 | "scope": "a51", 368 | "description": "8051 assembly pseudoinstruction" 369 | }, 370 | "pseudoinst_ALTNAME": { 371 | "prefix": "ALTNAME", 372 | "body": [ 373 | "ALTNAME" 374 | ], 375 | "scope": "a51", 376 | "description": "8051 assembly pseudoinstruction" 377 | }, 378 | "pseudoinst_INCLUDE": { 379 | "prefix": "INCLUDE", 380 | "body": [ 381 | "INCLUDE" 382 | ], 383 | "scope": "a51", 384 | "description": "8051 assembly pseudoinstruction" 385 | }, 386 | "register_PSW": { 387 | "prefix": "PSW", 388 | "body": [ 389 | "PSW" 390 | ], 391 | "scope": "a51", 392 | "description": "8051 register" 393 | }, 394 | "register_ACC": { 395 | "prefix": "ACC", 396 | "body": [ 397 | "ACC" 398 | ], 399 | "scope": "a51", 400 | "description": "8051 register" 401 | }, 402 | "register_B": { 403 | "prefix": "B", 404 | "body": [ 405 | "B" 406 | ], 407 | "scope": "a51", 408 | "description": "8051 register" 409 | }, 410 | "register_SP": { 411 | "prefix": "SP", 412 | "body": [ 413 | "SP" 414 | ], 415 | "scope": "a51", 416 | "description": "8051 register" 417 | }, 418 | "register_DPL": { 419 | "prefix": "DPL", 420 | "body": [ 421 | "DPL" 422 | ], 423 | "scope": "a51", 424 | "description": "8051 register" 425 | }, 426 | "register_DPH": { 427 | "prefix": "DPH", 428 | "body": [ 429 | "DPH" 430 | ], 431 | "scope": "a51", 432 | "description": "8051 register" 433 | }, 434 | "register_PCON": { 435 | "prefix": "PCON", 436 | "body": [ 437 | "PCON" 438 | ], 439 | "scope": "a51", 440 | "description": "8051 register" 441 | }, 442 | "register_TCON": { 443 | "prefix": "TCON", 444 | "body": [ 445 | "TCON" 446 | ], 447 | "scope": "a51", 448 | "description": "8051 register" 449 | }, 450 | "register_TMOD": { 451 | "prefix": "TMOD", 452 | "body": [ 453 | "TMOD" 454 | ], 455 | "scope": "a51", 456 | "description": "8051 register" 457 | }, 458 | "register_TL0": { 459 | "prefix": "TL0", 460 | "body": [ 461 | "TL0" 462 | ], 463 | "scope": "a51", 464 | "description": "8051 register" 465 | }, 466 | "register_TL1": { 467 | "prefix": "TL1", 468 | "body": [ 469 | "TL1" 470 | ], 471 | "scope": "a51", 472 | "description": "8051 register" 473 | }, 474 | "register_TH0": { 475 | "prefix": "TH0", 476 | "body": [ 477 | "TH0" 478 | ], 479 | "scope": "a51", 480 | "description": "8051 register" 481 | }, 482 | "register_TH1": { 483 | "prefix": "TH1", 484 | "body": [ 485 | "TH1" 486 | ], 487 | "scope": "a51", 488 | "description": "8051 register" 489 | }, 490 | "register_IE": { 491 | "prefix": "IE", 492 | "body": [ 493 | "IE" 494 | ], 495 | "scope": "a51", 496 | "description": "8051 register" 497 | }, 498 | "register_IP": { 499 | "prefix": "IP", 500 | "body": [ 501 | "IP" 502 | ], 503 | "scope": "a51", 504 | "description": "8051 register" 505 | }, 506 | "register_SCON": { 507 | "prefix": "SCON", 508 | "body": [ 509 | "SCON" 510 | ], 511 | "scope": "a51", 512 | "description": "8051 register" 513 | }, 514 | "register_SBUF": { 515 | "prefix": "SBUF", 516 | "body": [ 517 | "SBUF" 518 | ], 519 | "scope": "a51", 520 | "description": "8051 register" 521 | }, 522 | "register_CY": { 523 | "prefix": "CY", 524 | "body": [ 525 | "CY" 526 | ], 527 | "scope": "a51", 528 | "description": "8051 register" 529 | }, 530 | "register_AC": { 531 | "prefix": "AC", 532 | "body": [ 533 | "AC" 534 | ], 535 | "scope": "a51", 536 | "description": "8051 register" 537 | }, 538 | "register_F0": { 539 | "prefix": "F0", 540 | "body": [ 541 | "F0" 542 | ], 543 | "scope": "a51", 544 | "description": "8051 register" 545 | }, 546 | "register_RS1": { 547 | "prefix": "RS1", 548 | "body": [ 549 | "RS1" 550 | ], 551 | "scope": "a51", 552 | "description": "8051 register" 553 | }, 554 | "register_RS0": { 555 | "prefix": "RS0", 556 | "body": [ 557 | "RS0" 558 | ], 559 | "scope": "a51", 560 | "description": "8051 register" 561 | }, 562 | "register_OV": { 563 | "prefix": "OV", 564 | "body": [ 565 | "OV" 566 | ], 567 | "scope": "a51", 568 | "description": "8051 register" 569 | }, 570 | "register_P": { 571 | "prefix": "P", 572 | "body": [ 573 | "P" 574 | ], 575 | "scope": "a51", 576 | "description": "8051 register" 577 | }, 578 | "register_TF1": { 579 | "prefix": "TF1", 580 | "body": [ 581 | "TF1" 582 | ], 583 | "scope": "a51", 584 | "description": "8051 register" 585 | }, 586 | "register_TR1": { 587 | "prefix": "TR1", 588 | "body": [ 589 | "TR1" 590 | ], 591 | "scope": "a51", 592 | "description": "8051 register" 593 | }, 594 | "register_TF0": { 595 | "prefix": "TF0", 596 | "body": [ 597 | "TF0" 598 | ], 599 | "scope": "a51", 600 | "description": "8051 register" 601 | }, 602 | "register_TR0": { 603 | "prefix": "TR0", 604 | "body": [ 605 | "TR0" 606 | ], 607 | "scope": "a51", 608 | "description": "8051 register" 609 | }, 610 | "register_IE1": { 611 | "prefix": "IE1", 612 | "body": [ 613 | "IE1" 614 | ], 615 | "scope": "a51", 616 | "description": "8051 register" 617 | }, 618 | "register_IT1": { 619 | "prefix": "IT1", 620 | "body": [ 621 | "IT1" 622 | ], 623 | "scope": "a51", 624 | "description": "8051 register" 625 | }, 626 | "register_IE0": { 627 | "prefix": "IE0", 628 | "body": [ 629 | "IE0" 630 | ], 631 | "scope": "a51", 632 | "description": "8051 register" 633 | }, 634 | "register_IT0": { 635 | "prefix": "IT0", 636 | "body": [ 637 | "IT0" 638 | ], 639 | "scope": "a51", 640 | "description": "8051 register" 641 | }, 642 | "register_EA": { 643 | "prefix": "EA", 644 | "body": [ 645 | "EA" 646 | ], 647 | "scope": "a51", 648 | "description": "8051 register" 649 | }, 650 | "register_ES": { 651 | "prefix": "ES", 652 | "body": [ 653 | "ES" 654 | ], 655 | "scope": "a51", 656 | "description": "8051 register" 657 | }, 658 | "register_ET1": { 659 | "prefix": "ET1", 660 | "body": [ 661 | "ET1" 662 | ], 663 | "scope": "a51", 664 | "description": "8051 register" 665 | }, 666 | "register_EX1": { 667 | "prefix": "EX1", 668 | "body": [ 669 | "EX1" 670 | ], 671 | "scope": "a51", 672 | "description": "8051 register" 673 | }, 674 | "register_ET0": { 675 | "prefix": "ET0", 676 | "body": [ 677 | "ET0" 678 | ], 679 | "scope": "a51", 680 | "description": "8051 register" 681 | }, 682 | "register_EX0": { 683 | "prefix": "EX0", 684 | "body": [ 685 | "EX0" 686 | ], 687 | "scope": "a51", 688 | "description": "8051 register" 689 | }, 690 | "register_PS": { 691 | "prefix": "PS", 692 | "body": [ 693 | "PS" 694 | ], 695 | "scope": "a51", 696 | "description": "8051 register" 697 | }, 698 | "register_PT1": { 699 | "prefix": "PT1", 700 | "body": [ 701 | "PT1" 702 | ], 703 | "scope": "a51", 704 | "description": "8051 register" 705 | }, 706 | "register_PX1": { 707 | "prefix": "PX1", 708 | "body": [ 709 | "PX1" 710 | ], 711 | "scope": "a51", 712 | "description": "8051 register" 713 | }, 714 | "register_PT0": { 715 | "prefix": "PT0", 716 | "body": [ 717 | "PT0" 718 | ], 719 | "scope": "a51", 720 | "description": "8051 register" 721 | }, 722 | "register_PX0": { 723 | "prefix": "PX0", 724 | "body": [ 725 | "PX0" 726 | ], 727 | "scope": "a51", 728 | "description": "8051 register" 729 | }, 730 | "register_RD": { 731 | "prefix": "RD", 732 | "body": [ 733 | "RD" 734 | ], 735 | "scope": "a51", 736 | "description": "8051 register" 737 | }, 738 | "register_WR": { 739 | "prefix": "WR", 740 | "body": [ 741 | "WR" 742 | ], 743 | "scope": "a51", 744 | "description": "8051 register" 745 | }, 746 | "register_T1": { 747 | "prefix": "T1", 748 | "body": [ 749 | "T1" 750 | ], 751 | "scope": "a51", 752 | "description": "8051 register" 753 | }, 754 | "register_T0": { 755 | "prefix": "T0", 756 | "body": [ 757 | "T0" 758 | ], 759 | "scope": "a51", 760 | "description": "8051 register" 761 | }, 762 | "register_INT1": { 763 | "prefix": "INT1", 764 | "body": [ 765 | "INT1" 766 | ], 767 | "scope": "a51", 768 | "description": "8051 register" 769 | }, 770 | "register_INT0": { 771 | "prefix": "INT0", 772 | "body": [ 773 | "INT0" 774 | ], 775 | "scope": "a51", 776 | "description": "8051 register" 777 | }, 778 | "register_TXD": { 779 | "prefix": "TXD", 780 | "body": [ 781 | "TXD" 782 | ], 783 | "scope": "a51", 784 | "description": "8051 register" 785 | }, 786 | "register_RXD": { 787 | "prefix": "RXD", 788 | "body": [ 789 | "RXD" 790 | ], 791 | "scope": "a51", 792 | "description": "8051 register" 793 | }, 794 | "register_SM0": { 795 | "prefix": "SM0", 796 | "body": [ 797 | "SM0" 798 | ], 799 | "scope": "a51", 800 | "description": "8051 register" 801 | }, 802 | "register_SM1": { 803 | "prefix": "SM1", 804 | "body": [ 805 | "SM1" 806 | ], 807 | "scope": "a51", 808 | "description": "8051 register" 809 | }, 810 | "register_SM2": { 811 | "prefix": "SM2", 812 | "body": [ 813 | "SM2" 814 | ], 815 | "scope": "a51", 816 | "description": "8051 register" 817 | }, 818 | "register_REN": { 819 | "prefix": "REN", 820 | "body": [ 821 | "REN" 822 | ], 823 | "scope": "a51", 824 | "description": "8051 register" 825 | }, 826 | "register_TB8": { 827 | "prefix": "TB8", 828 | "body": [ 829 | "TB8" 830 | ], 831 | "scope": "a51", 832 | "description": "8051 register" 833 | }, 834 | "register_RB8": { 835 | "prefix": "RB8", 836 | "body": [ 837 | "RB8" 838 | ], 839 | "scope": "a51", 840 | "description": "8051 register" 841 | }, 842 | "register_TI": { 843 | "prefix": "TI", 844 | "body": [ 845 | "TI" 846 | ], 847 | "scope": "a51", 848 | "description": "8051 register" 849 | }, 850 | "register_RI": { 851 | "prefix": "RI", 852 | "body": [ 853 | "RI" 854 | ], 855 | "scope": "a51", 856 | "description": "8051 register" 857 | }, 858 | "type_EQU": { 859 | "prefix": "EQU", 860 | "body": [ 861 | "EQU" 862 | ], 863 | "scope": "a51", 864 | "description": "8051 assembly Data Type" 865 | }, 866 | "type_SET": { 867 | "prefix": "SET", 868 | "body": [ 869 | "SET" 870 | ], 871 | "scope": "a51", 872 | "description": "8051 assembly Data Type" 873 | }, 874 | "type_DATA": { 875 | "prefix": "DATA", 876 | "body": [ 877 | "DATA" 878 | ], 879 | "scope": "a51", 880 | "description": "8051 assembly Data Type" 881 | }, 882 | "type_BYTE": { 883 | "prefix": "BYTE", 884 | "body": [ 885 | "BYTE" 886 | ], 887 | "scope": "a51", 888 | "description": "8051 assembly Data Type" 889 | }, 890 | "type_WORD": { 891 | "prefix": "WORD", 892 | "body": [ 893 | "WORD" 894 | ], 895 | "scope": "a51", 896 | "description": "8051 assembly Data Type" 897 | }, 898 | "type_BIT": { 899 | "prefix": "BIT", 900 | "body": [ 901 | "BIT" 902 | ], 903 | "scope": "a51", 904 | "description": "8051 assembly Data Type" 905 | }, 906 | "type_DB": { 907 | "prefix": "DB", 908 | "body": [ 909 | "DB" 910 | ], 911 | "scope": "a51", 912 | "description": "8051 assembly Data Type" 913 | }, 914 | "type_DW": { 915 | "prefix": "DW", 916 | "body": [ 917 | "DW" 918 | ], 919 | "scope": "a51", 920 | "description": "8051 assembly Data Type" 921 | }, 922 | "type_DS": { 923 | "prefix": "DS", 924 | "body": [ 925 | "DS" 926 | ], 927 | "scope": "a51", 928 | "description": "8051 assembly Data Type" 929 | } 930 | } -------------------------------------------------------------------------------- /syntaxes/a51.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "information_for_contributors": [ 3 | "51 assembly support, author: cl" 4 | ], 5 | "version": "1.0.0", 6 | "name": "a51", 7 | "scopeName": "source.asm.a51", 8 | "patterns": [ 9 | { 10 | "include": "#instruction" 11 | }, 12 | { 13 | "include": "#pseudoinstruction" 14 | }, 15 | { 16 | "include": "#operators" 17 | }, 18 | { 19 | "include": "#type" 20 | }, 21 | { 22 | "include": "#register" 23 | }, 24 | { 25 | "include": "#number" 26 | }, 27 | { 28 | "include": "#functionName" 29 | }, 30 | { 31 | "include": "#comment" 32 | }, 33 | { 34 | "include": "#string" 35 | }, 36 | { 37 | "include": "#jumpFuncName" 38 | } 39 | ], 40 | "repository": { 41 | "instruction": { 42 | "match": "(?i)\\b(MOV|MOVC|MOVX|PUSH|POP|XCH|XCHD|ADD|ADDC|SUBB|INC|DEC|MUL|DIV|DA|ANL|ORL|XRL|CLR|CPL|RL|RLC|RR|RRC|SWAP|SETB|JC|JNC|JB|JNB|JBC|ACALL|LCALL|RET|RETI|AJMP|LJMP|SJMP|JMP|JZ|JNZ|CJNE|DJNZ|NOP)\\b", 43 | "captures": { 44 | "1": { 45 | "name": "support.function.mnemonic.arithmetic.a51" 46 | } 47 | } 48 | }, 49 | "pseudoinstruction": { 50 | "match": "(?i)\\b(ORG|END|ALTNAME|INCLUDE|\\$TITLE|\\$NOLIST|\\$NOCODE)\\b", 51 | "captures": { 52 | "1": { 53 | "name": "keyword.control.import" 54 | } 55 | } 56 | }, 57 | "type": { 58 | "match": "(?i)\\b(EQU|SET|DATA|BYTE|WORD|BIT|DB|DW|DS)\\b", 59 | "captures": { 60 | "1": { 61 | "name": "entity.name.type" 62 | } 63 | } 64 | }, 65 | "operators": { 66 | "match": "\\+", 67 | "captures": { 68 | "0": { 69 | "name": "keyword.operator" 70 | } 71 | } 72 | }, 73 | "register": { 74 | "match": "\\b(R[0-7]|P[0-3]|PSW|A(?:CC)?|B|SP|DPL|DPH|PCON|TCON|TMOD|TL0|TL1|TH0|TH1|IE|IP|SCON|SBUF|CY|AC|F0|RS1|RS0|OV|P|TF1|TR1|TF0|TR0|IE1|IT1|IE0|IT0|EA|ES|ET1|EX1|ET0|EX0|PS|PT1|PX1|PT0|PX0|RD|WR|T1|T0|INT1|INT0|TXD|RXD|SM0|SM1|SM2|REN|TB8|RB8|TI|RI)\\b", 75 | "captures": { 76 | "1": { 77 | "name": "storage.other.register.a51" 78 | } 79 | } 80 | }, 81 | "number": { 82 | "match": "(?i)\\b([0-9A-F]+H|0x[0-9a-f]+)\\b", 83 | "captures": { 84 | "1": { 85 | "name": "constant.numeric" 86 | } 87 | } 88 | }, 89 | "comment": { 90 | "match": "(?i)(;.*)", 91 | "captures": { 92 | "1": { 93 | "name": "comment.line" 94 | } 95 | } 96 | }, 97 | "functionName": { 98 | "match": "^\\s*\\b([a-zA-Z_]\\w*)\\b\\s*:", 99 | "captures": { 100 | "1": { 101 | "name": "variable" 102 | } 103 | } 104 | }, 105 | "string": { 106 | "match": "(\".*\"|'.*')", 107 | "captures": { 108 | "1": { 109 | "name": "string" 110 | } 111 | } 112 | }, 113 | "jumpFuncName": { 114 | "match": ".*?\\b(JC|JNC|ACALL|LCALL|AJMP|LJMP|SJMP|JMP|JZ|JNZ)\\b\\s+([a-zA-Z_]\\w*)", 115 | "captures": { 116 | "1": { 117 | "patterns": [ 118 | { 119 | "include": "#instruction" 120 | } 121 | ] 122 | }, 123 | "2": { 124 | "name": "variable" 125 | } 126 | } 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /syntaxes/build-log.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | scopeName 6 | build-log 7 | fileTypes 8 | 9 | log 10 | 11 | name 12 | Log file 13 | patterns 14 | 15 | 16 | match 17 | \bwarning: 18 | name 19 | log.warning 20 | 21 | 22 | match 23 | \berror: 24 | name 25 | log.error 26 | 27 | 28 | match 29 | \b(?i:([a-z]|[0-9])+\:((\/\/)|((\/\/)?(\S)))+) 30 | name 31 | storage 32 | 33 | 34 | uuid 35 | ab259404-3072-4cd4-a943-7cbbd32e373f 36 | 37 | -------------------------------------------------------------------------------- /test.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2023", 5 | "outDir": "dist", 6 | "lib": [ 7 | "ES2023", 8 | "dom" 9 | ], 10 | "sourceMap": true, 11 | "rootDir": ".", 12 | "removeComments": true, 13 | "noUnusedLocals": true, 14 | "strictNullChecks": true, 15 | "strict": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "skipLibCheck": true 18 | }, 19 | "include": [ 20 | "test/**/*.ts", 21 | "ui/**/*.ts", 22 | "./vscode*.d.ts", 23 | "src/types/*.d.ts", 24 | "src/**/*.ts" 25 | ], 26 | "exclude": [ 27 | "node_modules/**" 28 | ] 29 | } -------------------------------------------------------------------------------- /test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from '@vscode/test-electron'; 4 | 5 | async function go() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ 17 | extensionDevelopmentPath, 18 | extensionTestsPath, 19 | launchArgs:['--disable-extensions'] 20 | }); 21 | } catch (err) { 22 | console.error('Failed to run tests', err); 23 | process.exit(1); 24 | } 25 | } 26 | 27 | void go(); -------------------------------------------------------------------------------- /test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import { glob} from 'glob' 3 | 4 | export function run(): Promise { 5 | // Create the mocha test 6 | 7 | const testsRoot = path.resolve(__dirname, '..'); 8 | 9 | return new Promise((_c, _e) => { 10 | glob('**/**.test.js', { cwd: testsRoot }); 11 | 12 | }); 13 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2023", 5 | "outDir": "dist", 6 | "lib": [ 7 | "ES2023", 8 | "DOM" 9 | ], 10 | "inlineSourceMap": true, 11 | "rootDir": ".", 12 | "removeComments": true, 13 | "noUnusedLocals": false, 14 | "strictNullChecks": true, 15 | "strict": true, /* enable all strict type-checking options */ 16 | "forceConsistentCasingInFileNames": true, 17 | "skipLibCheck": true , 18 | "types": ["node", "vscode"], 19 | }, 20 | "include": [ 21 | "test/**/*.ts", 22 | "./vscode*.d.ts", 23 | "src/types/*.d.ts", 24 | "src/**/*.ts" 25 | ], 26 | "exclude": [ 27 | "node_modules" 28 | ] 29 | } -------------------------------------------------------------------------------- /webpack.config.mjs: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 'use strict'; 3 | 4 | import { resolve , dirname } from 'path'; 5 | import { fileURLToPath } from 'url'; 6 | 7 | //@ts-check 8 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 9 | 10 | /** @type WebpackConfig */ 11 | const config = { 12 | target: 'node', 13 | // mode: 'none', 14 | entry: './src/extension.ts', 15 | output: { 16 | path: resolve(dirname(fileURLToPath(import.meta.url)), 'dist', 'src'), 17 | filename: 'extension.js', 18 | libraryTarget: "commonjs2", 19 | devtoolModuleFilenameTemplate: "../[resource-path]" 20 | }, 21 | node: { 22 | __dirname: false, // leave the __dirname behavior intact 23 | }, 24 | devtool: 'source-map', 25 | externals: { 26 | vscode: 'commonjs vscode', 27 | xml2js: 'xml2js' 28 | }, 29 | resolve: { 30 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 31 | extensions: ['.ts', '.js'], 32 | mainFields: ['main', 'module'] 33 | }, 34 | module: { 35 | rules: [{ 36 | test: /\.ts$/, 37 | exclude: /node_modules/, 38 | use: [{ 39 | loader: 'ts-loader', 40 | options: { 41 | compilerOptions: { 42 | "inlineSorceMap": true, 43 | } 44 | } 45 | }] 46 | }, { 47 | test: /.node$/, 48 | loader: 'node-loader' 49 | }] 50 | }, 51 | optimization: { 52 | minimize: false 53 | }, 54 | stats: { 55 | warnings: false 56 | }, 57 | infrastructureLogging: { 58 | level: "log", // enables logging required for problem matchers 59 | } 60 | }; 61 | 62 | // export default [config]; 63 | export default () => { 64 | return config; 65 | }; 66 | --------------------------------------------------------------------------------