diff --git a/.eslintrc.json b/.eslintrc.json index 4d54b63..04683f3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,9 +5,7 @@ "ecmaVersion": 6, "sourceType": "module" }, - "plugins": [ - "@typescript-eslint" - ], + "plugins": ["@typescript-eslint"], "rules": { "@typescript-eslint/naming-convention": "warn", "@typescript-eslint/semi": "warn", diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..3579886 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,12 @@ +# This file is used to ignore certain commits (e.g. reformatting) in git blame. +# +# To use it, invoke `git blame` like this: +# +# git blame --ignore-revs-file .git-blame-ignore-revs +# +# Alternatively, make this the default: +# +# git config [--global] blame.ignoreRevsFile .git-blame-ignore-revs + +# 2022-12-31: Reformat code with prettier +fcc2ef44edfc1cc2bc10ade2522e7baf942ae090 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e72a4fe..83b6d4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,14 +11,14 @@ jobs: os: [macos-latest, ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install Node.js - uses: actions/setup-node@v2 - with: - node-version: 14.x - - run: yarn install - - run: xvfb-run -a yarn pretest - if: runner.os == 'Linux' - - run: yarn pretest - if: runner.os != 'Linux' + - name: Checkout + uses: actions/checkout@v2 + - name: Install Node.js + uses: actions/setup-node@v2 + with: + node-version: 14.x + - run: yarn install + - run: xvfb-run -a yarn pretest + if: runner.os == 'Linux' + - run: yarn pretest + if: runner.os != 'Linux' diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..42e34f0 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +/node_modules/ +/dist/ +/out/ +/dap/ +/package-lock.json +**/*.md diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000..4ab40a8 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,15 @@ +# Global rules +arrowParens: always +bracketSpacing: false +endOfLine: lf +printWidth: 100 +quoteProps: consistent +singleQuote: true +tabWidth: 4 +trailingComma: all + +# Overrides +overrides: + - files: '*.yml' + options: + tabWidth: 2 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9843e12..4e8390e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,10 @@ { - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "dbaeumer.vscode-eslint", - "amodio.tsl-problem-matcher", - "ms-vscode.hexeditor" - ] -} \ No newline at end of file + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "dbaeumer.vscode-eslint", + "amodio.tsl-problem-matcher", + "ms-vscode.hexeditor", + "esbenp.prettier-vscode" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index a419d14..313ff63 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,17 +1,13 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Extension", - "type": "extensionHost", - "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - ], - "outFiles": [ - "${workspaceFolder}/dist/ext/**/*.js" - ], - "preLaunchTask": "npm: watch" - } - ] -} \ No newline at end of file + "version": "0.2.0", + "configurations": [ + { + "name": "Extension", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/dist/ext/**/*.js"], + "preLaunchTask": "npm: watch" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 30bf8c2..24499a6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,9 @@ "out": true // set this to false to include "out" folder in search results }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts - "typescript.tsc.autoDetect": "off" -} \ No newline at end of file + "typescript.tsc.autoDetect": "off", + // Enable prettier format on save + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.formatOnPaste": false +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f45c02c..e10cd78 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -16,28 +16,19 @@ "script": "watch", "group": "build", "isBackground": true, - "problemMatcher": [ - "$ts-webpack-watch", - "$tslint-webpack-watch" - ] + "problemMatcher": ["$ts-webpack-watch", "$tslint-webpack-watch"] }, { "type": "npm", "script": "compile-web", - "problemMatcher": [ - "$ts-webpack", - "$tslint-webpack" - ] + "problemMatcher": ["$ts-webpack", "$tslint-webpack"] }, { "type": "npm", "script": "watch-web", "group": "build", "isBackground": true, - "problemMatcher": [ - "$ts-webpack-watch", - "$tslint-webpack-watch" - ] + "problemMatcher": ["$ts-webpack-watch", "$tslint-webpack-watch"] } ] -} \ No newline at end of file +} diff --git a/README.md b/README.md index 8032c60..f394d80 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,93 @@ # VS Code probe-rs-debugger - ## Documentation -Full documentation on Installation, Configuration and supported functionality can be found at [the probe-rs webpage](https://probe.rs/docs/tools/vscode/) and under the [visual tour heading](https://probe.rs/docs/tools/vscode/#a-visual-guide-of-implemented-features) -![](images/probe-rs-debugger.gif) +Full documentation on Installation, Configuration and supported functionality +can be found at [the probe-rs webpage](https://probe.rs/docs/tools/vscode/) and +under the [visual tour +heading](https://probe.rs/docs/tools/vscode/#a-visual-guide-of-implemented-features) + +![](images/probe-rs-debugger.gif) ## Development Setup -To work on this extensions, you first need to install VS Code and nodejs. Afterwards, follow the following steps: - -- Install yarn: - ```bash - npm install -g yarn - ``` -- Checkout this repository -- Inside the repository, install the prerequisites: - ```bash - yarn - ``` -- Install the extensions VS Code recommends. If you prefer to do this manually, you can find the list of recommended extensions in the repository's `.vscode/settings.json' file. These can then be installed from the command line, for example: - ```bash - code --install-extension amodio.tsl-problem-matcher - ``` -- Open VS Code -- Press F5 to start a new VS Code instance where the extension can be debugged. You can also open the "Run and Debug" panel in the left sidebar, and then start the "Extension" debug configuration. +To work on this extensions, you first need to install VS Code and nodejs. +Afterwards, follow the following steps: + +* Install yarn: + + npm install -g yarn + +* Checkout this repository +* Inside the repository, install the prerequisites: + + yarn + +* Install the extensions VS Code recommends. If you prefer to do this manually, + you can find the list of recommended extensions in the repository's + `.vscode/settings.json' file. These can then be installed from the command + line, for example: + + code --install-extension amodio.tsl-problem-matcher +* Open VS Code +* Press F5 to start a new VS Code instance where the extension can be debugged. + You can also open the "Run and Debug" panel in the left sidebar, and then + start the "Extension" debug configuration. ### To run against a compiled executable of `probe-rs-debugger` -* Modify the `debug-example` entry in '.vscode/launch.json' file to point to your target project. -* Press `F5` to __build and launch executable__ `probe-rs-debugger`. VSCode will open another VS Code window. In that window, +* Modify the `debug-example` entry in '.vscode/launch.json' file to point to + your target project. +* Press `F5` to __build and launch executable__ `probe-rs-debugger`. VSCode will + open another VS Code window. In that window, * You will see the `debug-example` project you just configured. -* Select the debug environment `probe_rs Executable Test`.* Press `F5` to start debugging. - +* Select the debug environment `probe_rs Executable Test`.* Press `F5` to start + debugging. + ### To run against a debuggable instance of `probe-rs-debugger` -* Clone the [probe-rs](https://github.com/probe-rs/probe-rs.git) repository, and open it in VSCode. - * In this `probe-rs` repo, select the debug environment `DAP-Server probe-rs-debugger` +* Clone the [probe-rs](https://github.com/probe-rs/probe-rs.git) repository, and + open it in VSCode. + * In this `probe-rs` repo, select the debug environment `DAP-Server + probe-rs-debugger` * Press `F5` to start `probe-rs-debugger` as a debuggable server. -* Switch to the VSCode instance of the probe-rs `vscode` repository. - * Modify the `debug-example` entry in '.vscode/launch.json' file to point to your target project. - * Press `F5` to __build and attach to the debuggable server instance of__ `probe-rs-debugger`. VSCode will open another VS Code window. In that window: +* Switch to the VSCode instance of the probe-rs `vscode` repository. + * Modify the `debug-example` entry in '.vscode/launch.json' file to point to + your target project. + * Press `F5` to __build and attach to the debuggable server instance of__ + `probe-rs-debugger`. VSCode will open another VS Code window. In that + window: * You will see the `debug-example` project you just configured. * Select the debug environment `probe_rs Server Test`. * Press `F5` to start debugging. ## Generating and releasing the extension -Because the extension is still regarded as being in 'Alpha' state, it is NOT being released on the Microsoft Visual Studio Code Extension Marketplace. The only way to get access to updated versions of the extension is to build it yourself, or to download it from the [GitHub release page](#development-setup) for this extension. + +Because the extension is still regarded as being in 'Alpha' state, it is NOT +being released on the Microsoft Visual Studio Code Extension Marketplace. The +only way to get access to updated versions of the extension is to build it +yourself, or to download it from the [GitHub release page](#development-setup) +for this extension. ### Build the extension -Building the extension refers to the process that generates the installable `.vsix` package. -* Follow the instructions to [setup your development environment](#development-setup). + +Building the extension refers to the process that generates the installable +`.vsix` package. + +* Follow the instructions to [setup your development + environment](#development-setup). * In a terminal window, execute the following command: - ``` - yarn probe-rs:package - ``` + + yarn probe-rs:package + * This will generate a .vsix file in the root of the repository ### Updating the GitHub release page -* Whenever any PR has been merged, you need to create a new release and upload the `.vsix` in the repository's [release page](https://github.com/probe-rs/vscode/releases). -* This means that every PR needs to update the version number appropriately in the `package.json` file. Please [see the Semantic Versioning guidelines](https://semver.org/). - +* Whenever any PR has been merged, you need to create a new release and upload + the `.vsix` in the repository's [release + page](https://github.com/probe-rs/vscode/releases). +* This means that every PR needs to update the version number appropriately in + the `package.json` file. Please [see the Semantic Versioning + guidelines](https://semver.org/). diff --git a/build/extension.webpack.config.js b/build/extension.webpack.config.js index 20aed7a..128e835 100644 --- a/build/extension.webpack.config.js +++ b/build/extension.webpack.config.js @@ -12,43 +12,48 @@ const path = require('path'); module.exports = /** @type WebpackConfig */ { - context: path.dirname(__dirname), - mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') - target: 'node', // vscode extensions run in a Node.js-context - entry: { - extension: './src/extension.ts' - }, - resolve: { // support reading TypeScript and JavaScript files - extensions: ['.ts', '.js'] - }, - node: { - __dirname: false, // leave the __dirname-behaviour intact - }, - module: { - rules: [{ - test: /\.ts$/, - exclude: /node_modules/, - use: [{ - // configure TypeScript loader: - // * enable sources maps for end-to-end source maps - loader: 'ts-loader', - options: { - compilerOptions: { - 'sourceMap': true, - 'declaration': false - } - } - }] - }] - }, - externals: { - vscode: "commonjs vscode" // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed - }, - output: { - filename: 'extension.js', - path: path.resolve(__dirname, '../dist/ext'), - libraryTarget: 'commonjs2', - devtoolModuleFilenameTemplate: "../../[resource-path]" - }, - devtool: 'source-map' -} + context: path.dirname(__dirname), + mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + target: 'node', // vscode extensions run in a Node.js-context + entry: { + extension: './src/extension.ts', + }, + resolve: { + // support reading TypeScript and JavaScript files + extensions: ['.ts', '.js'], + }, + node: { + __dirname: false, // leave the __dirname-behaviour intact + }, + module: { + rules: [ + { + test: /\.ts$/, + exclude: /node_modules/, + use: [ + { + // configure TypeScript loader: + // * enable sources maps for end-to-end source maps + loader: 'ts-loader', + options: { + compilerOptions: { + sourceMap: true, + declaration: false, + }, + }, + }, + ], + }, + ], + }, + externals: { + vscode: 'commonjs vscode', // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed + }, + output: { + filename: 'extension.js', + path: path.resolve(__dirname, '../dist/ext'), + libraryTarget: 'commonjs2', + devtoolModuleFilenameTemplate: '../../[resource-path]', + }, + devtool: 'source-map', +}; diff --git a/build/web-extension.webpack.config.js b/build/web-extension.webpack.config.js index 998cce0..8e7b460 100644 --- a/build/web-extension.webpack.config.js +++ b/build/web-extension.webpack.config.js @@ -12,45 +12,48 @@ const path = require('path'); module.exports = /** @type WebpackConfig */ { - context: path.dirname(__dirname), - mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') - target: 'webworker', // extensions run in a webworker context - entry: { - extension: './src/web/extension.ts', - }, - resolve: { - mainFields: ['module', 'main'], - extensions: ['.ts', '.js'], // support ts-files and js-files - alias: { - } - }, - module: { - rules: [{ - test: /\.ts$/, - exclude: /node_modules/, - use: [{ - // configure TypeScript loader: - // * enable sources maps for end-to-end source maps - loader: 'ts-loader', - options: { - compilerOptions: { - 'sourceMap': true, - 'declaration': false - } - } - }] - }] - }, - externals: { - 'vscode': 'commonjs vscode', // ignored because it doesn't exist - }, - performance: { - hints: false - }, - output: { - filename: 'extension.js', - path: path.join(__dirname, '../dist/web'), - libraryTarget: 'commonjs' - }, - devtool: 'source-map' + context: path.dirname(__dirname), + mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + target: 'webworker', // extensions run in a webworker context + entry: { + extension: './src/web/extension.ts', + }, + resolve: { + mainFields: ['module', 'main'], + extensions: ['.ts', '.js'], // support ts-files and js-files + alias: {}, + }, + module: { + rules: [ + { + test: /\.ts$/, + exclude: /node_modules/, + use: [ + { + // configure TypeScript loader: + // * enable sources maps for end-to-end source maps + loader: 'ts-loader', + options: { + compilerOptions: { + sourceMap: true, + declaration: false, + }, + }, + }, + ], + }, + ], + }, + externals: { + vscode: 'commonjs vscode', // ignored because it doesn't exist + }, + performance: { + hints: false, + }, + output: { + filename: 'extension.js', + path: path.join(__dirname, '../dist/web'), + libraryTarget: 'commonjs', + }, + devtool: 'source-map', }; diff --git a/package.json b/package.json index 4f05f1e..ffc389a 100644 --- a/package.json +++ b/package.json @@ -1,508 +1,509 @@ { - "name": "probe-rs-debugger", - "displayName": "Debugger for probe-rs", - "version": "0.4.1", - "publisher": "probe-rs", - "description": "probe-rs Debug Adapter for VS Code.", - "author": { - "name": "Jack Noppé", - "email": "noppej@hotmail.com" - }, - "license": "MIT", - "keywords": [ - "probe-rs embedded debug" - ], - "engines": { - "vscode": "^1.74.1" - }, - "icon": "images/probe-rs-debugger.png", - "categories": [ - "Debuggers" - ], - "private": true, - "repository": { - "type": "git", - "url": "https://github.com/probe-rs/vscode.git" - }, - "bugs": { - "url": "https://github.com/probe-rs/vscode/issues" - }, - "scripts": { - "probe-rs: getDebugProtocolLatest": "curl -LJs https://microsoft.github.io/debug-adapter-protocol/debugAdapterProtocol.json -o dap/debugProtocol.json", - "probe-rs:Update Dev Dependencies": "yarn upgrade --latest", - "probe-rs:package": "vsce package", - "vscode:prepublish": "yarn run package-ext && yarn run package-web", - "compile": "tsc -p ./", - "lint": "eslint src --ext ts", - "watch": "webpack --watch --devtool nosources-source-map --config ./build/extension.webpack.config.js", - "watch2": "tsc -watch -p ./", - "pretest": "yarn run compile && yarn run lint", - "publish": "vsce publish", - "package-ext": "webpack --mode production --config ./build/extension.webpack.config.js", - "compile-web": "webpack --devtool nosources-source-map --config ./build/web-extension.webpack.config.js", - "watch-web": "webpack --watch --devtool nosources-source-map --config ./build/web-extension.webpack.config.js", - "package-web": "webpack --mode production --config ./build/web-extension.webpack.config.js" - }, - "enabledApiProposals": [], - "dependencies": { - "@vscode/debugadapter": "^1.58.0", - "await-notify": "1.0.1", - "portfinder": "^1.0.32" - }, - "devDependencies": { - "@types/glob": "^8.0.0", - "@types/mocha": "^10.0.1", - "@types/node": "^18.11.15", - "@types/vscode": "^1.74.0", - "@typescript-eslint/eslint-plugin": "^5.46.1", - "@typescript-eslint/parser": "^5.46.1", - "@vscode/debugprotocol": "^1.58.0", - "eslint": "^8.29.0", - "glob": "^8.0.3", - "mocha": "^10.2.0", - "portfinder": "^1.0.32", - "ts-loader": "^9.4.2", - "typescript": "^4.9.4", - "vsce": "^2.15.0", - "webpack": "^5.75.0", - "webpack-cli": "^5.0.1" - }, - "main": "./dist/ext/extension.js", - "browser": "./dist/web/extension.js", - "activationEvents": [ - "onDebug" - ], - "workspaceTrust": { - "request": "never" - }, - "contributes": { - "breakpoints": [ - { - "language": "rust" - } + "name": "probe-rs-debugger", + "displayName": "Debugger for probe-rs", + "version": "0.4.1", + "publisher": "probe-rs", + "description": "probe-rs Debug Adapter for VS Code.", + "author": { + "name": "Jack Noppé", + "email": "noppej@hotmail.com" + }, + "license": "MIT", + "keywords": [ + "probe-rs embedded debug" ], - "debuggers": [ - { - "type": "probe-rs-debug", - "label": "Debug adapter on top of probe-rs", - "languages": [ - "rust" + "engines": { + "vscode": "^1.74.1" + }, + "icon": "images/probe-rs-debugger.png", + "categories": [ + "Debuggers" + ], + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/probe-rs/vscode.git" + }, + "bugs": { + "url": "https://github.com/probe-rs/vscode/issues" + }, + "scripts": { + "probe-rs: getDebugProtocolLatest": "curl -LJs https://microsoft.github.io/debug-adapter-protocol/debugAdapterProtocol.json -o dap/debugProtocol.json", + "probe-rs:Update Dev Dependencies": "yarn upgrade --latest", + "probe-rs:package": "vsce package", + "vscode:prepublish": "yarn run package-ext && yarn run package-web", + "compile": "tsc -p ./", + "lint": "eslint src --ext ts", + "watch": "webpack --watch --devtool nosources-source-map --config ./build/extension.webpack.config.js", + "watch2": "tsc -watch -p ./", + "pretest": "yarn run compile && yarn run lint && prettier --check .", + "publish": "vsce publish", + "package-ext": "webpack --mode production --config ./build/extension.webpack.config.js", + "compile-web": "webpack --devtool nosources-source-map --config ./build/web-extension.webpack.config.js", + "watch-web": "webpack --watch --devtool nosources-source-map --config ./build/web-extension.webpack.config.js", + "package-web": "webpack --mode production --config ./build/web-extension.webpack.config.js" + }, + "enabledApiProposals": [], + "dependencies": { + "@vscode/debugadapter": "^1.58.0", + "await-notify": "1.0.1", + "portfinder": "^1.0.32" + }, + "devDependencies": { + "@types/glob": "^8.0.0", + "@types/mocha": "^10.0.1", + "@types/node": "^18.11.15", + "@types/vscode": "^1.74.0", + "@typescript-eslint/eslint-plugin": "^5.46.1", + "@typescript-eslint/parser": "^5.46.1", + "@vscode/debugprotocol": "^1.58.0", + "eslint": "^8.29.0", + "glob": "^8.0.3", + "mocha": "^10.2.0", + "portfinder": "^1.0.32", + "prettier": "^2.8.1", + "ts-loader": "^9.4.2", + "typescript": "^4.9.4", + "vsce": "^2.15.0", + "webpack": "^5.75.0", + "webpack-cli": "^5.0.1" + }, + "main": "./dist/ext/extension.js", + "browser": "./dist/web/extension.js", + "activationEvents": [ + "onDebug" + ], + "workspaceTrust": { + "request": "never" + }, + "contributes": { + "breakpoints": [ + { + "language": "rust" + } ], - "configurationAttributes": { - "launch": { - "required": [ - "chip", - "coreConfigs" - ], - "properties": { - "server": { - "type": "string", - "description": "Optionally connect to an existing `probe-rs-debugger` session on IP and Port, e.g. '127.0.0.1:50000'", - "default": "127.0.0.1:50000" - }, - "consoleLogLevel": { - "type": "string", - "description": "The level of log info printed to the console. This does NOT affect the RUST_LOG defined in the `env` property.", - "enum": [ - "Console", - "Info", - "Debug" - ], - "enumDescriptions": [ - "The console will only contain error messages and process status messages.", - "The console log will also contain high level information about interactions between the extension and the debug adapter.", - "The console log will also contain detailed information about interactions between the extension and the debug adapter." - ], - "default": "Console" - }, - "runtimeExecutable": { - "type": "string", - "description": "An OS resolvable path to the Probe-rs debugger executable.", - "default": "probe-rs-debugger" - }, - "runtimeArgs": { - "type": "array", - "items": { - "type": "string" - }, - "description": "String array of arguments to provide the startup arguments for the Probe-rs debugger executable.", - "default": [ - "debug", - "--dap" - ] - }, - "env": { - "additionalProperties": { - "type": "string" - }, - "default": {}, - "description": "Environment variables defined as a key value pair. The 'key' is the name of the environment variable, and the 'value' is value of the environment variable.", - "type": "object" - }, - "cwd": { - "type": "string", - "description": "The working directory of the debugger, typically the RUST crate root", - "default": "${workspaceFolder}" - }, - "probe": { - "type": "string", - "description": "Use this flag to select a specific probe in the list. Use '--probe VID:PID' or '--probe VID:PID:Serial' if you have more than one probe with the same VID:PID." - }, - "chip": { - "type": "string", - "description": "Please specify the appropriate chip from the list of supported chips reported by running `probe-rs-debugger list-chips`." - }, - "connectUnderReset": { - "type": "boolean", - "description": "This option will result in the target reset pin being held high during the attach operation.", - "default": false - }, - "speed": { - "type": "number", - "description": "Specify the protocol speed in kHz." - }, - "wireProtocol": { - "type": "string", - "description": "The correct wire Protocol to use.", - "enum": [ - "Swd", - "Jtag" + "debuggers": [ + { + "type": "probe-rs-debug", + "label": "Debug adapter on top of probe-rs", + "languages": [ + "rust" ], - "enumDescriptions": [ - "Use the Serial Wire Debug (SWD) protocol.", - "Use the Joint Test Action Group (JTAG) protocol." - ] - }, - "allowEraseAll": { - "type": "boolean", - "description": "Allow the session to erase all memory of the chip or reset it to factory default.", - "default": true - }, - "flashingConfig": { - "type": "object", - "description": "These flashing options are applied when flashing one or more core `program_binary` files to the target memory.", - "flashingEnabled": { - "type": "boolean", - "description": "Flash the target before debugging.", - "default": true - }, - "resetAfterFlashing": { - "type": "boolean", - "description": "Reset all cores on the target after flashing.", - "default": true - }, - "haltAfterReset": { - "type": "boolean", - "description": "Halt all cores on the target after reset.", - "default": true - }, - "fullChipErase": { - "type": "boolean", - "description": "Do a full chip erase, versus page-by-page erase.", - "default": false - }, - "restoreUnwrittenBytes": { - "type": "boolean", - "description": "Restore erased bytes that will not be rewritten from ELF.", - "default": false - } - }, - "coreConfigs": { - "type": "array", - "description": "Each MCU core will have a mandatory `coreIndex`, `programBinary`, and `chip` as well as several other optional properties.", - "items": { - "required": [ - "programBinary" - ], - "coreIndex": { - "type": "number", - "description": "The zero based index of the MCU core for this session", - "default": 0 - }, - "programBinary": { - "type": "string", - "description": "The path (relative to `cwd` or absolute) to the binary for your target firmware", - "default": "./target/thumbv7em-none-eabihf/debug/${workspaceFolderBasename}" - }, - "svdFile": { - "type": "string", - "description": "The path (relative to `cwd` or absolute) to the CMCIS-SVD file for your target core", - "default": "./CMSIS.SVD" - }, - "rttEnabled": { - "type": "boolean", - "description": "If true, the debugger will open an RTT Terminal tab for each of the active channels on the target.", - "default": false - }, - "rttChannelFormats": { - "type": "array", - "items": { - "channelNumber": { - "type": "number", - "description": "The channel number to which this data format applies. If any active channel numbers are omitted, we will assume the default will be `dataFormat=String', and 'showTimestamps=false'." - }, - "dataFormat": { - "type": "string", - "description": "One of the supported data formats for RTT channels.", - "enum": [ - "String", - "BinaryLE", - "Defmt" + "configurationAttributes": { + "launch": { + "required": [ + "chip", + "coreConfigs" ], - "enumDescriptions": [ - "String (text) format.", - "Binary Little Endian format.", - "Deferred formatting (see: https://defmt.ferrous-systems.com)." + "properties": { + "server": { + "type": "string", + "description": "Optionally connect to an existing `probe-rs-debugger` session on IP and Port, e.g. '127.0.0.1:50000'", + "default": "127.0.0.1:50000" + }, + "consoleLogLevel": { + "type": "string", + "description": "The level of log info printed to the console. This does NOT affect the RUST_LOG defined in the `env` property.", + "enum": [ + "Console", + "Info", + "Debug" + ], + "enumDescriptions": [ + "The console will only contain error messages and process status messages.", + "The console log will also contain high level information about interactions between the extension and the debug adapter.", + "The console log will also contain detailed information about interactions between the extension and the debug adapter." + ], + "default": "Console" + }, + "runtimeExecutable": { + "type": "string", + "description": "An OS resolvable path to the Probe-rs debugger executable.", + "default": "probe-rs-debugger" + }, + "runtimeArgs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "String array of arguments to provide the startup arguments for the Probe-rs debugger executable.", + "default": [ + "debug", + "--dap" + ] + }, + "env": { + "additionalProperties": { + "type": "string" + }, + "default": {}, + "description": "Environment variables defined as a key value pair. The 'key' is the name of the environment variable, and the 'value' is value of the environment variable.", + "type": "object" + }, + "cwd": { + "type": "string", + "description": "The working directory of the debugger, typically the RUST crate root", + "default": "${workspaceFolder}" + }, + "probe": { + "type": "string", + "description": "Use this flag to select a specific probe in the list. Use '--probe VID:PID' or '--probe VID:PID:Serial' if you have more than one probe with the same VID:PID." + }, + "chip": { + "type": "string", + "description": "Please specify the appropriate chip from the list of supported chips reported by running `probe-rs-debugger list-chips`." + }, + "connectUnderReset": { + "type": "boolean", + "description": "This option will result in the target reset pin being held high during the attach operation.", + "default": false + }, + "speed": { + "type": "number", + "description": "Specify the protocol speed in kHz." + }, + "wireProtocol": { + "type": "string", + "description": "The correct wire Protocol to use.", + "enum": [ + "Swd", + "Jtag" + ], + "enumDescriptions": [ + "Use the Serial Wire Debug (SWD) protocol.", + "Use the Joint Test Action Group (JTAG) protocol." + ] + }, + "allowEraseAll": { + "type": "boolean", + "description": "Allow the session to erase all memory of the chip or reset it to factory default.", + "default": true + }, + "flashingConfig": { + "type": "object", + "description": "These flashing options are applied when flashing one or more core `program_binary` files to the target memory.", + "flashingEnabled": { + "type": "boolean", + "description": "Flash the target before debugging.", + "default": true + }, + "resetAfterFlashing": { + "type": "boolean", + "description": "Reset all cores on the target after flashing.", + "default": true + }, + "haltAfterReset": { + "type": "boolean", + "description": "Halt all cores on the target after reset.", + "default": true + }, + "fullChipErase": { + "type": "boolean", + "description": "Do a full chip erase, versus page-by-page erase.", + "default": false + }, + "restoreUnwrittenBytes": { + "type": "boolean", + "description": "Restore erased bytes that will not be rewritten from ELF.", + "default": false + } + }, + "coreConfigs": { + "type": "array", + "description": "Each MCU core will have a mandatory `coreIndex`, `programBinary`, and `chip` as well as several other optional properties.", + "items": { + "required": [ + "programBinary" + ], + "coreIndex": { + "type": "number", + "description": "The zero based index of the MCU core for this session", + "default": 0 + }, + "programBinary": { + "type": "string", + "description": "The path (relative to `cwd` or absolute) to the binary for your target firmware", + "default": "./target/thumbv7em-none-eabihf/debug/${workspaceFolderBasename}" + }, + "svdFile": { + "type": "string", + "description": "The path (relative to `cwd` or absolute) to the CMCIS-SVD file for your target core", + "default": "./CMSIS.SVD" + }, + "rttEnabled": { + "type": "boolean", + "description": "If true, the debugger will open an RTT Terminal tab for each of the active channels on the target.", + "default": false + }, + "rttChannelFormats": { + "type": "array", + "items": { + "channelNumber": { + "type": "number", + "description": "The channel number to which this data format applies. If any active channel numbers are omitted, we will assume the default will be `dataFormat=String', and 'showTimestamps=false'." + }, + "dataFormat": { + "type": "string", + "description": "One of the supported data formats for RTT channels.", + "enum": [ + "String", + "BinaryLE", + "Defmt" + ], + "enumDescriptions": [ + "String (text) format.", + "Binary Little Endian format.", + "Deferred formatting (see: https://defmt.ferrous-systems.com)." + ], + "default": "String" + }, + "showTimestamps": { + "type": "boolean", + "default": false, + "description": "Enable the inclusion of timestamps in the RTT output for `dataFormat=String`." + }, + "showLocation": { + "type": "boolean", + "default": true, + "description": "Enable the inclusion of Defmt location information in the RTT output for `dataFormat=Defmt`." + } + } + } + } + } + } + }, + "attach": { + "required": [ + "chip", + "coreConfigs" ], - "default": "String" - }, - "showTimestamps": { - "type": "boolean", - "default": false, - "description": "Enable the inclusion of timestamps in the RTT output for `dataFormat=String`." - }, - "showLocation": { - "type": "boolean", - "default": true, - "description": "Enable the inclusion of Defmt location information in the RTT output for `dataFormat=Defmt`." - } + "properties": { + "server": { + "type": "string", + "description": "Optionally onnect to an existing `probe-rs-debugger` session on IP and Port, e.g. '127.0.0.1:50000'", + "default": "127.0.0.1:50000" + }, + "consoleLogLevel": { + "type": "string", + "description": "The level of log info printed to the console. This does NOT affect the RUST_LOG defined in the `env` property.", + "enum": [ + "Console", + "Info", + "Debug" + ], + "enumDescriptions": [ + "The console will only contain error messages and process status messages.", + "The console log will also contain high level information about interactions between the extension and the debug adapter.", + "The console log will also contain detailed information about interactions between the extension and the debug adapter." + ], + "default": "Console" + }, + "runtimeExecutable": { + "type": "string", + "description": "An OS resolvable path to the Probe-rs debugger executable.", + "default": "probe-rs-debugger" + }, + "runtimeArgs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "String array of arguments to provide the startup arguments for the Probe-rs debugger executable.", + "default": [ + "debug", + "--dap" + ] + }, + "env": { + "additionalProperties": { + "type": "string" + }, + "default": {}, + "description": "Environment variables defined as a key value pair. The 'key' is the name of the environment variable, and the 'value' is value of the environment variable.", + "type": "object" + }, + "cwd": { + "type": "string", + "description": "The working directory of the debugger, typically the RUST crate root", + "default": "${workspaceFolder}" + }, + "probe": { + "type": "string", + "description": "Use this flag to select a specific probe in the list. Use '--probe VID:PID' or '--probe VID:PID:Serial' if you have more than one probe with the same VID:PID." + }, + "chip": { + "type": "string", + "description": "Please specify the appropriate chip from the list of supported chips reported by running `probe-rs-debugger list-chips`." + }, + "connectUnderReset": { + "type": "boolean", + "description": "This option will result in the target reset pin being held high during the attach operation.", + "default": false + }, + "speed": { + "type": "number", + "description": "Specify the protocol speed in kHz." + }, + "wireProtocol": { + "type": "string", + "description": "The correct wire Protocol to use.", + "enum": [ + "Swd", + "Jtag" + ], + "enumDescriptions": [ + "Use the Serial Wire Debug (SWD) protocol.", + "Use the Joint Test Action Group (JTAG) protocol." + ] + }, + "allowEraseAll": { + "type": "boolean", + "description": "Allow the session to erase all memory of the chip or reset it to factory default.", + "default": true + }, + "flashingConfig": { + "type": "object", + "description": "These flashing options are applied when flashing one or more core `program_binary` files to the target memory.", + "flashingEnabled": { + "type": "boolean", + "description": "Flash the target before debugging.", + "default": true + }, + "resetAfterFlashing": { + "type": "boolean", + "description": "Reset all cores on the target after flashing.", + "default": true + }, + "haltAfterReset": { + "type": "boolean", + "description": "Halt all cores on the target after reset.", + "default": true + }, + "fullChipErase": { + "type": "boolean", + "description": "Do a full chip erase, versus page-by-page erase.", + "default": false + }, + "restoreUnwrittenBytes": { + "type": "boolean", + "description": "Restore erased bytes that will not be rewritten from ELF.", + "default": false + } + }, + "coreConfigs": { + "type": "array", + "description": "Each MCU core will have a mandatory `coreIndex`, `programBinary`, and `chip` as well as several other optional properties.", + "items": { + "required": [ + "programBinary" + ], + "coreIndex": { + "type": "number", + "description": "The zero based index of the MCU core for this session", + "default": 0 + }, + "programBinary": { + "type": "string", + "description": "The path (relative to `cwd` or absolute) to the binary for your target firmware", + "default": "./target/thumbv7em-none-eabihf/debug/${workspaceFolderBasename}" + }, + "svdFile": { + "type": "string", + "description": "The path (relative to `cwd` or absolute) to the CMCIS-SVD file for your target core", + "default": "./CMSIS.SVD" + }, + "rttEnabled": { + "type": "boolean", + "description": "If true, the debugger will open an RTT Terminal tab for each of the active channels on the target.", + "default": false + }, + "rttChannelFormats": { + "type": "array", + "items": { + "channelNumber": { + "type": "number", + "description": "The channel number to which this data format applies. If any active channel numbers are omitted, we will assume the default will be `dataFormat=String', and 'showTimestamps=false'." + }, + "dataFormat": { + "type": "string", + "description": "One of the supported data formats for RTT channels.", + "enum": [ + "String", + "BinaryLE", + "Defmt" + ], + "enumDescriptions": [ + "String (text) format.", + "Binary Little Endian format.", + "Deferred formatting (see: https://defmt.ferrous-systems.com)." + ], + "default": "String" + }, + "showTimestamps": { + "type": "boolean", + "default": false, + "description": "Enable the inclusion of timestamps in the RTT output for `dataFormat=String`." + }, + "showLocation": { + "type": "boolean", + "default": true, + "description": "Enable the inclusion of Defmt location information in the RTT output for `dataFormat=Defmt`." + } + } + } + } + } + } } - } - } - } - } - }, - "attach": { - "required": [ - "chip", - "coreConfigs" - ], - "properties": { - "server": { - "type": "string", - "description": "Optionally onnect to an existing `probe-rs-debugger` session on IP and Port, e.g. '127.0.0.1:50000'", - "default": "127.0.0.1:50000" - }, - "consoleLogLevel": { - "type": "string", - "description": "The level of log info printed to the console. This does NOT affect the RUST_LOG defined in the `env` property.", - "enum": [ - "Console", - "Info", - "Debug" - ], - "enumDescriptions": [ - "The console will only contain error messages and process status messages.", - "The console log will also contain high level information about interactions between the extension and the debug adapter.", - "The console log will also contain detailed information about interactions between the extension and the debug adapter." - ], - "default": "Console" - }, - "runtimeExecutable": { - "type": "string", - "description": "An OS resolvable path to the Probe-rs debugger executable.", - "default": "probe-rs-debugger" - }, - "runtimeArgs": { - "type": "array", - "items": { - "type": "string" - }, - "description": "String array of arguments to provide the startup arguments for the Probe-rs debugger executable.", - "default": [ - "debug", - "--dap" - ] - }, - "env": { - "additionalProperties": { - "type": "string" }, - "default": {}, - "description": "Environment variables defined as a key value pair. The 'key' is the name of the environment variable, and the 'value' is value of the environment variable.", - "type": "object" - }, - "cwd": { - "type": "string", - "description": "The working directory of the debugger, typically the RUST crate root", - "default": "${workspaceFolder}" - }, - "probe": { - "type": "string", - "description": "Use this flag to select a specific probe in the list. Use '--probe VID:PID' or '--probe VID:PID:Serial' if you have more than one probe with the same VID:PID." - }, - "chip": { - "type": "string", - "description": "Please specify the appropriate chip from the list of supported chips reported by running `probe-rs-debugger list-chips`." - }, - "connectUnderReset": { - "type": "boolean", - "description": "This option will result in the target reset pin being held high during the attach operation.", - "default": false - }, - "speed": { - "type": "number", - "description": "Specify the protocol speed in kHz." - }, - "wireProtocol": { - "type": "string", - "description": "The correct wire Protocol to use.", - "enum": [ - "Swd", - "Jtag" + "initialConfigurations": [ + { + "type": "probe-rs-debug", + "request": "launch", + "name": "probe-rs Test", + "cwd": "${workspaceFolder}", + "connectUnderReset": true, + "chip": "STM32H745ZITx", + "flashingConfig": { + "flashingEnabled": true, + "resetAfterFlashing": true, + "haltAfterReset": true + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "./target/thumbv7em-none-eabihf/debug/${workspaceFolderBasename}" + } + ] + } ], - "enumDescriptions": [ - "Use the Serial Wire Debug (SWD) protocol.", - "Use the Joint Test Action Group (JTAG) protocol." - ] - }, - "allowEraseAll": { - "type": "boolean", - "description": "Allow the session to erase all memory of the chip or reset it to factory default.", - "default": true - }, - "flashingConfig": { - "type": "object", - "description": "These flashing options are applied when flashing one or more core `program_binary` files to the target memory.", - "flashingEnabled": { - "type": "boolean", - "description": "Flash the target before debugging.", - "default": true - }, - "resetAfterFlashing": { - "type": "boolean", - "description": "Reset all cores on the target after flashing.", - "default": true - }, - "haltAfterReset": { - "type": "boolean", - "description": "Halt all cores on the target after reset.", - "default": true - }, - "fullChipErase": { - "type": "boolean", - "description": "Do a full chip erase, versus page-by-page erase.", - "default": false - }, - "restoreUnwrittenBytes": { - "type": "boolean", - "description": "Restore erased bytes that will not be rewritten from ELF.", - "default": false - } - }, - "coreConfigs": { - "type": "array", - "description": "Each MCU core will have a mandatory `coreIndex`, `programBinary`, and `chip` as well as several other optional properties.", - "items": { - "required": [ - "programBinary" - ], - "coreIndex": { - "type": "number", - "description": "The zero based index of the MCU core for this session", - "default": 0 - }, - "programBinary": { - "type": "string", - "description": "The path (relative to `cwd` or absolute) to the binary for your target firmware", - "default": "./target/thumbv7em-none-eabihf/debug/${workspaceFolderBasename}" - }, - "svdFile": { - "type": "string", - "description": "The path (relative to `cwd` or absolute) to the CMCIS-SVD file for your target core", - "default": "./CMSIS.SVD" - }, - "rttEnabled": { - "type": "boolean", - "description": "If true, the debugger will open an RTT Terminal tab for each of the active channels on the target.", - "default": false - }, - "rttChannelFormats": { - "type": "array", - "items": { - "channelNumber": { - "type": "number", - "description": "The channel number to which this data format applies. If any active channel numbers are omitted, we will assume the default will be `dataFormat=String', and 'showTimestamps=false'." - }, - "dataFormat": { - "type": "string", - "description": "One of the supported data formats for RTT channels.", - "enum": [ - "String", - "BinaryLE", - "Defmt" - ], - "enumDescriptions": [ - "String (text) format.", - "Binary Little Endian format.", - "Deferred formatting (see: https://defmt.ferrous-systems.com)." - ], - "default": "String" - }, - "showTimestamps": { - "type": "boolean", - "default": false, - "description": "Enable the inclusion of timestamps in the RTT output for `dataFormat=String`." - }, - "showLocation": { - "type": "boolean", - "default": true, - "description": "Enable the inclusion of Defmt location information in the RTT output for `dataFormat=Defmt`." - } + "configurationSnippets": [ + { + "label": "probe-rs Debug: Launch", + "description": "A new configuration for embedded debugging of a user selected rust file with probe-rs.", + "body": { + "type": "probe-rs-debug", + "request": "launch", + "name": "probe-rs Test", + "cwd": "${workspaceFolder}", + "connectUnderReset": false, + "chip": "STM32H745ZITx", + "flashingConfig": { + "flashingEnabled": true, + "resetAfterFlashing": true, + "haltAfterReset": true + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "./target/thumbv7em-none-eabihf/debug/${workspaceFolderBasename}" + } + ] + } } - } - } - } - } - } - }, - "initialConfigurations": [ - { - "type": "probe-rs-debug", - "request": "launch", - "name": "probe-rs Test", - "cwd": "${workspaceFolder}", - "connectUnderReset": true, - "chip": "STM32H745ZITx", - "flashingConfig": { - "flashingEnabled": true, - "resetAfterFlashing": true, - "haltAfterReset": true - }, - "coreConfigs": [ - { - "coreIndex": 0, - "programBinary": "./target/thumbv7em-none-eabihf/debug/${workspaceFolderBasename}" - } - ] - } - ], - "configurationSnippets": [ - { - "label": "probe-rs Debug: Launch", - "description": "A new configuration for embedded debugging of a user selected rust file with probe-rs.", - "body": { - "type": "probe-rs-debug", - "request": "launch", - "name": "probe-rs Test", - "cwd": "${workspaceFolder}", - "connectUnderReset": false, - "chip": "STM32H745ZITx", - "flashingConfig": { - "flashingEnabled": true, - "resetAfterFlashing": true, - "haltAfterReset": true - }, - "coreConfigs": [ - { - "coreIndex": 0, - "programBinary": "./target/thumbv7em-none-eabihf/debug/${workspaceFolderBasename}" - } - ] + ] } - } ] - } - ] - } + } } diff --git a/src/extension.ts b/src/extension.ts index 5a76b7f..c70221f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,378 +7,466 @@ import * as child_process from 'child_process'; import * as os from 'os'; import * as vscode from 'vscode'; -import { DebugAdapterTracker, DebugAdapterTrackerFactory, } from 'vscode'; +import {DebugAdapterTracker, DebugAdapterTrackerFactory} from 'vscode'; export async function activate(context: vscode.ExtensionContext) { - - const descriptorFactory = new ProbeRSDebugAdapterServerDescriptorFactory(); - - context.subscriptions.push( - vscode.debug.registerDebugAdapterDescriptorFactory('probe-rs-debug', descriptorFactory), - vscode.debug.onDidReceiveDebugSessionCustomEvent(descriptorFactory.receivedCustomEvent.bind(descriptorFactory)), - vscode.debug.onDidTerminateDebugSession(descriptorFactory.dispose.bind(descriptorFactory)), - ); - - // I cannot find a way to programmatically test for when VSCode is debugging the extension, versus when a user is using the extension to debug their own code, but the following code is usefull in the former situation, so I will leave it here to be commented out by extension developers when needed. - // const trackerFactory = new ProbeRsDebugAdapterTrackerFactory(); - // context.subscriptions.push( - // vscode.debug.registerDebugAdapterTrackerFactory('probe-rs-debug', trackerFactory), - // ); - + const descriptorFactory = new ProbeRSDebugAdapterServerDescriptorFactory(); + + context.subscriptions.push( + vscode.debug.registerDebugAdapterDescriptorFactory('probe-rs-debug', descriptorFactory), + vscode.debug.onDidReceiveDebugSessionCustomEvent( + descriptorFactory.receivedCustomEvent.bind(descriptorFactory), + ), + vscode.debug.onDidTerminateDebugSession(descriptorFactory.dispose.bind(descriptorFactory)), + ); + + // I cannot find a way to programmatically test for when VSCode is debugging the extension, versus when a user is using the extension to debug their own code, but the following code is usefull in the former situation, so I will leave it here to be commented out by extension developers when needed. + // const trackerFactory = new ProbeRsDebugAdapterTrackerFactory(); + // context.subscriptions.push( + // vscode.debug.registerDebugAdapterTrackerFactory('probe-rs-debug', trackerFactory), + // ); } export function deactivate(context: vscode.ExtensionContext) { - return undefined; + return undefined; } // Cleanup inconsitent line breaks in String data -const formatText = (text: string) => `\r${text.split(/(\r?\n)/g).join("\r")}\r`; +const formatText = (text: string) => `\r${text.split(/(\r?\n)/g).join('\r')}\r`; // Constant for handling/filtering console log messages. const enum ConsoleLogSources { - console = 'probe-rs-debug', // Identifies messages from the extension or debug adapter that must be sent to the Debug Console. - debug = 'DEBUG', // Identifies messages that contain detailed level debug information. - info = 'INFO', // Identifies messages that contain summary level of debug information. - error = 'ERROR', // Identifies messages that contain error information. -}; + console = 'probe-rs-debug', // Identifies messages from the extension or debug adapter that must be sent to the Debug Console. + debug = 'DEBUG', // Identifies messages that contain detailed level debug information. + info = 'INFO', // Identifies messages that contain summary level of debug information. + error = 'ERROR', // Identifies messages that contain error information. +} -// This is just the default. It will be updated after the configuration has been resolved. +// This is just the default. It will be updated after the configuration has been resolved. var consoleLogLevel = ConsoleLogSources.console; // Common handler for error/exit codes function handleExit(code: number | null, signal: string | null) { - var actionHint: string = '\tPlease report this issue at https://github.com/probe-rs/probe-rs/issues/new'; - if (code) { - vscode.window.showErrorMessage(`${ConsoleLogSources.error}: ${ConsoleLogSources.console.toLowerCase()} exited with an unexpected code: ${code} ${actionHint}`); - } else if (signal) { - vscode.window.showErrorMessage(`${ConsoleLogSources.error}: ${ConsoleLogSources.console.toLowerCase()} exited with signal: ${signal} ${actionHint}`); - } + var actionHint: string = + '\tPlease report this issue at https://github.com/probe-rs/probe-rs/issues/new'; + if (code) { + vscode.window.showErrorMessage( + `${ + ConsoleLogSources.error + }: ${ConsoleLogSources.console.toLowerCase()} exited with an unexpected code: ${code} ${actionHint}`, + ); + } else if (signal) { + vscode.window.showErrorMessage( + `${ + ConsoleLogSources.error + }: ${ConsoleLogSources.console.toLowerCase()} exited with signal: ${signal} ${actionHint}`, + ); + } } // Adapted from https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case function toCamelCase(str: string) { - return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match: string, index: number) { - if (+match === 0) {return "";} // or if (/\s+/.test(match)) for white spaces - return index === 0 ? match.toLowerCase() : match.toUpperCase(); - }); + return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function (match: string, index: number) { + if (+match === 0) { + return ''; + } // or if (/\s+/.test(match)) for white spaces + return index === 0 ? match.toLowerCase() : match.toUpperCase(); + }); } -// Messages to be sent to the debug session's console. -// Any local (generated directly by this extension) messages MUST start with ConsoleLogLevels.error, or ConsoleLogSources.console.toLowerCase(), or `DEBUG`. +// Messages to be sent to the debug session's console. +// Any local (generated directly by this extension) messages MUST start with ConsoleLogLevels.error, or ConsoleLogSources.console.toLowerCase(), or `DEBUG`. // Any messages that start with ConsoleLogLevels.error or ConsoleLogSources.console.toLowerCase() will always be logged. // Any messages that come from the ConsoleLogSources.console.toLowerCase() STDERR will always be logged. function logToConsole(consoleMesssage: string, fromDebugger: boolean = false) { - console.log(consoleMesssage); // During VSCode extension development, this will also log to the local debug console - if (fromDebugger) { - // STDERR messages of the `error` variant. These deserve to be shown as an error message in the UI also. - // This filter might capture more than expected, but since RUST_LOG messages can take many formats, it seems that this is the safest/most inclusive. - if (consoleMesssage.includes(ConsoleLogSources.error)) { - vscode.window.showErrorMessage(consoleMesssage); - } else { - // Any other messages that come directly from the debugger, are assumed to be relevant and should be logged to the console. - vscode.debug.activeDebugConsole.appendLine(consoleMesssage); - } - } else if (consoleMesssage.includes(ConsoleLogSources.console.toLowerCase())) { - vscode.debug.activeDebugConsole.appendLine(consoleMesssage); - } else { - switch (consoleLogLevel) { - case ConsoleLogSources.debug: // Log Info, Error AND Debug - if (consoleMesssage.includes(ConsoleLogSources.console.toLowerCase()) || consoleMesssage.includes(ConsoleLogSources.error) || consoleMesssage.includes(ConsoleLogSources.debug)) { - vscode.debug.activeDebugConsole.appendLine(consoleMesssage); - } - break; - default: // ONLY log console and error messages - if (consoleMesssage.includes(ConsoleLogSources.console.toLowerCase()) || consoleMesssage.includes(ConsoleLogSources.error)) { - vscode.debug.activeDebugConsole.appendLine(consoleMesssage); - } - break; - } - } + console.log(consoleMesssage); // During VSCode extension development, this will also log to the local debug console + if (fromDebugger) { + // STDERR messages of the `error` variant. These deserve to be shown as an error message in the UI also. + // This filter might capture more than expected, but since RUST_LOG messages can take many formats, it seems that this is the safest/most inclusive. + if (consoleMesssage.includes(ConsoleLogSources.error)) { + vscode.window.showErrorMessage(consoleMesssage); + } else { + // Any other messages that come directly from the debugger, are assumed to be relevant and should be logged to the console. + vscode.debug.activeDebugConsole.appendLine(consoleMesssage); + } + } else if (consoleMesssage.includes(ConsoleLogSources.console.toLowerCase())) { + vscode.debug.activeDebugConsole.appendLine(consoleMesssage); + } else { + switch (consoleLogLevel) { + case ConsoleLogSources.debug: // Log Info, Error AND Debug + if ( + consoleMesssage.includes(ConsoleLogSources.console.toLowerCase()) || + consoleMesssage.includes(ConsoleLogSources.error) || + consoleMesssage.includes(ConsoleLogSources.debug) + ) { + vscode.debug.activeDebugConsole.appendLine(consoleMesssage); + } + break; + default: // ONLY log console and error messages + if ( + consoleMesssage.includes(ConsoleLogSources.console.toLowerCase()) || + consoleMesssage.includes(ConsoleLogSources.error) + ) { + vscode.debug.activeDebugConsole.appendLine(consoleMesssage); + } + break; + } + } } class ProbeRSDebugAdapterServerDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { - - rttTerminals: [channelNumber: number, dataFormat: String, rttTerminal: vscode.Terminal, channelWriteEmitter: vscode.EventEmitter][] = []; - - createRttTerminal(channelNumber: number, dataFormat: string, channelName: string) { - // Make sure we have a terminal window per channel, for RTT Logging - if (vscode.debug.activeDebugSession) { - let session = vscode.debug.activeDebugSession; - let channelWriteEmitter = new vscode.EventEmitter(); - let channelPty: vscode.Pseudoterminal = { - onDidWrite: channelWriteEmitter.event, - open: () => { - let windowIsOpen = true; - session.customRequest("rttWindowOpened", { channelNumber, windowIsOpen }).then((response) => { - logToConsole(`${ConsoleLogSources.console.toLowerCase()}: RTT Window opened, and ready to receive RTT data on channel ${JSON.stringify(channelNumber, null, 2)}`); - }); - }, - close: () => { - let windowIsOpen = false; - session.customRequest("rttWindowOpened", { channelNumber, windowIsOpen }).then((response) => { - logToConsole(`${ConsoleLogSources.console.toLowerCase()}: RTT Window closed, and can no longer receive RTT data on channel ${JSON.stringify(channelNumber, null, 2)}`); - }); - }, - }; - let channelTerminalConfig: vscode.ExtensionTerminalOptions | undefined; - let channelTerminal: vscode.Terminal | undefined; - for (let reuseTerminal of vscode.window.terminals) { - if (reuseTerminal.name === channelName) { - channelTerminal = reuseTerminal; - channelTerminalConfig = channelTerminal.creationOptions as vscode.ExtensionTerminalOptions; - let windowIsOpen = true; - session.customRequest("rttWindowOpened", { channelNumber, windowIsOpen }).then((response) => { - logToConsole(`${ConsoleLogSources.console.toLowerCase()}: RTT Window reused, and ready to receive RTT data on channel ${JSON.stringify(channelNumber, null, 2)}`); - }); - break; - } - } - if (channelTerminal === undefined) { - channelTerminalConfig = { - name: channelName, - pty: channelPty - }; - for (let index in this.rttTerminals) { - var [formerChannelNumber, , ,] = this.rttTerminals[index]; - if (formerChannelNumber === channelNumber) { - this.rttTerminals.splice(+index, 1); - break; - } - } - channelTerminal = vscode.window.createTerminal(channelTerminalConfig); - vscode.debug.activeDebugConsole.appendLine(`${ConsoleLogSources.console.toLowerCase()}: Opened a new RTT Terminal window named: ${channelName}`); - this.rttTerminals.push([+channelNumber, dataFormat, channelTerminal, channelWriteEmitter]); - } - if (channelNumber === 0) { - channelTerminal.show(false); - } - } - } - - receivedCustomEvent(customEvent: vscode.DebugSessionCustomEvent) { - switch (customEvent.event) { - case 'probe-rs-rtt-channel-config': - this.createRttTerminal(+customEvent.body?.channelNumber, customEvent.body?.dataFormat, customEvent.body?.channelName); - break; - case 'probe-rs-rtt-data': - let incomingChannelNumber: number = +customEvent.body?.channelNumber; - for (var [channelNumber, dataFormat, , channelWriteEmitter] of this.rttTerminals) { - if (channelNumber === incomingChannelNumber) { - switch (dataFormat) { - case 'BinaryLE': //Don't mess with or filter this data - channelWriteEmitter.fire(customEvent.body?.data); - break; - default: //Replace newline characters with platform appropriate newline/carriage-return combinations - channelWriteEmitter.fire(formatText(customEvent.body?.data)); - console.log(customEvent.body?.data); - } - break; - } - } - break; - case 'probe-rs-show-message': - switch (customEvent.body?.severity) { - case 'information': - vscode.window.showInformationMessage(customEvent.body?.message); - break; - case 'warning': - vscode.debug.activeDebugConsole.appendLine(customEvent.body?.message); - vscode.window.showWarningMessage(customEvent.body?.message); - break; - case 'error': - vscode.debug.activeDebugConsole.appendLine(customEvent.body?.message); - vscode.window.showErrorMessage(customEvent.body?.message); - break; - default: - logToConsole(`${ConsoleLogSources.error}: ${ConsoleLogSources.console.toLowerCase()}: Received custom event with unknown message severity: + rttTerminals: [ + channelNumber: number, + dataFormat: String, + rttTerminal: vscode.Terminal, + channelWriteEmitter: vscode.EventEmitter, + ][] = []; + + createRttTerminal(channelNumber: number, dataFormat: string, channelName: string) { + // Make sure we have a terminal window per channel, for RTT Logging + if (vscode.debug.activeDebugSession) { + let session = vscode.debug.activeDebugSession; + let channelWriteEmitter = new vscode.EventEmitter(); + let channelPty: vscode.Pseudoterminal = { + onDidWrite: channelWriteEmitter.event, + open: () => { + let windowIsOpen = true; + session + .customRequest('rttWindowOpened', {channelNumber, windowIsOpen}) + .then((response) => { + logToConsole( + `${ConsoleLogSources.console.toLowerCase()}: RTT Window opened, and ready to receive RTT data on channel ${JSON.stringify( + channelNumber, + null, + 2, + )}`, + ); + }); + }, + close: () => { + let windowIsOpen = false; + session + .customRequest('rttWindowOpened', {channelNumber, windowIsOpen}) + .then((response) => { + logToConsole( + `${ConsoleLogSources.console.toLowerCase()}: RTT Window closed, and can no longer receive RTT data on channel ${JSON.stringify( + channelNumber, + null, + 2, + )}`, + ); + }); + }, + }; + let channelTerminalConfig: vscode.ExtensionTerminalOptions | undefined; + let channelTerminal: vscode.Terminal | undefined; + for (let reuseTerminal of vscode.window.terminals) { + if (reuseTerminal.name === channelName) { + channelTerminal = reuseTerminal; + channelTerminalConfig = + channelTerminal.creationOptions as vscode.ExtensionTerminalOptions; + let windowIsOpen = true; + session + .customRequest('rttWindowOpened', {channelNumber, windowIsOpen}) + .then((response) => { + logToConsole( + `${ConsoleLogSources.console.toLowerCase()}: RTT Window reused, and ready to receive RTT data on channel ${JSON.stringify( + channelNumber, + null, + 2, + )}`, + ); + }); + break; + } + } + if (channelTerminal === undefined) { + channelTerminalConfig = { + name: channelName, + pty: channelPty, + }; + for (let index in this.rttTerminals) { + var [formerChannelNumber, , ,] = this.rttTerminals[index]; + if (formerChannelNumber === channelNumber) { + this.rttTerminals.splice(+index, 1); + break; + } + } + channelTerminal = vscode.window.createTerminal(channelTerminalConfig); + vscode.debug.activeDebugConsole.appendLine( + `${ConsoleLogSources.console.toLowerCase()}: Opened a new RTT Terminal window named: ${channelName}`, + ); + this.rttTerminals.push([ + +channelNumber, + dataFormat, + channelTerminal, + channelWriteEmitter, + ]); + } + if (channelNumber === 0) { + channelTerminal.show(false); + } + } + } + + receivedCustomEvent(customEvent: vscode.DebugSessionCustomEvent) { + switch (customEvent.event) { + case 'probe-rs-rtt-channel-config': + this.createRttTerminal( + +customEvent.body?.channelNumber, + customEvent.body?.dataFormat, + customEvent.body?.channelName, + ); + break; + case 'probe-rs-rtt-data': + let incomingChannelNumber: number = +customEvent.body?.channelNumber; + for (var [channelNumber, dataFormat, , channelWriteEmitter] of this.rttTerminals) { + if (channelNumber === incomingChannelNumber) { + switch (dataFormat) { + case 'BinaryLE': //Don't mess with or filter this data + channelWriteEmitter.fire(customEvent.body?.data); + break; + default: //Replace newline characters with platform appropriate newline/carriage-return combinations + channelWriteEmitter.fire(formatText(customEvent.body?.data)); + console.log(customEvent.body?.data); + } + break; + } + } + break; + case 'probe-rs-show-message': + switch (customEvent.body?.severity) { + case 'information': + vscode.window.showInformationMessage(customEvent.body?.message); + break; + case 'warning': + vscode.debug.activeDebugConsole.appendLine(customEvent.body?.message); + vscode.window.showWarningMessage(customEvent.body?.message); + break; + case 'error': + vscode.debug.activeDebugConsole.appendLine(customEvent.body?.message); + vscode.window.showErrorMessage(customEvent.body?.message); + break; + default: + logToConsole(`${ + ConsoleLogSources.error + }: ${ConsoleLogSources.console.toLowerCase()}: Received custom event with unknown message severity: ${JSON.stringify(customEvent.body?.severity, null, 2)}`); - } - break; - case `exited`: - this.dispose(); - break; - default: - logToConsole(`${ConsoleLogSources.error}: ${ConsoleLogSources.console.toLowerCase()}: Received unknown custom event: + } + break; + case `exited`: + this.dispose(); + break; + default: + logToConsole(`${ + ConsoleLogSources.error + }: ${ConsoleLogSources.console.toLowerCase()}: Received unknown custom event: ${JSON.stringify(customEvent, null, 2)}`); - break; - } - } - - // Note. We do NOT use `DebugAdapterExecutable`, but instead use `DebugAdapterServer` in all cases. - // - The decision was made during investigation of an [issue](https://github.com/probe-rs/probe-rs/issues/703) ... basically, after the probe-rs API was fixed, the code would work well for TCP connections (`DebugAdapterServer`), but would not work for STDIO connections (`DebugAdapterServer`). After some searches I found other extension developers that also found the TCP based connections to be more stable. - // - Since then, we have taken advantage of the access to stderr that `DebugAdapterServer` offers to route `RUST_LOG` output from the debugger to the user's VSCode Debug Console. This is a very useful capability, and cannot easily be implemented in `DebugAdapterExecutable`, because it does not allow access to `stderr` [See ongoing issue in VScode repo](https://github.com/microsoft/vscode/issues/108145). - async createDebugAdapterDescriptor(session: vscode.DebugSession, executable: vscode.DebugAdapterExecutable | undefined): Promise { - if (session.configuration.hasOwnProperty('consoleLogLevel')) { - consoleLogLevel = session.configuration.consoleLogLevel.toLowerCase(); - }; - - - // Initiate either the 'attach' or 'launch' request. - logToConsole(`${ConsoleLogSources.console.toLowerCase()}: Session: ${JSON.stringify(session, null, 2)}`); - - // When starting the debugger process, we have to wait for debuggerStatus to be set to `DebuggerStatus.running` before we continue - enum DebuggerStatus { - starting, - running, - failed, - } - var debuggerStatus: DebuggerStatus = DebuggerStatus.starting; - - var debugServer = new String("127.0.0.1:50000").split(":", 2); // ... provide default server host and port for "launch" configurations, where this is NOT a mandatory config - if (session.configuration.hasOwnProperty('server')) { - debugServer = new String(session.configuration.server).split(":", 2); - logToConsole(`${ConsoleLogSources.console.toLowerCase()}: Debug using existing server" ${JSON.stringify(debugServer[0])} on port ${JSON.stringify(debugServer[1])}`); - logToConsole(`${ConsoleLogSources.console.toLowerCase()}: Please note that debug server error messages will only be reported by the existing server console.`); - debuggerStatus = DebuggerStatus.running; // If this is not true as expected, then the user will be notified later. - } else { // Find and use the first available port and spawn a new probe-rs-debugger process - var portfinder = require('portfinder'); - try { - var port: number = await portfinder.getPortPromise(); - debugServer = `127.0.0.1:${port}`.split(":", 2); - } - catch (err: any) { - logToConsole(`${ConsoleLogSources.error}: ${JSON.stringify(err.message, null, 2)}`); - vscode.window.showErrorMessage(`Searching for available port failed with: ${JSON.stringify(err.message, null, 2)}`); - return undefined; - } - var args: string[]; - if (session.configuration.hasOwnProperty('runtimeArgs')) { - args = session.configuration.runtimeArgs; - } else { - args = [ - 'debug', - ]; - } - args.push("--port"); - args.push(debugServer[1]); - - var options = { - cwd: session.configuration.cwd, - env: { ...process.env, ...session.configuration.env }, - windowsHide: true, - }; - - var command = ""; - if (!executable) { - if (session.configuration.hasOwnProperty('runtimeExecutable')) { - command = session.configuration.runtimeExecutable; - } else { - switch (os.platform()) { - case 'win32': command = "probe-rs-debugger.exe"; break; - default: command = "probe-rs-debugger"; - } - } - } - else { - command = executable.command; - } - - // The debug adapter process was launched by VSCode, and should terminate itself at the end of every debug session (when receiving `Disconnect` or `Terminate` Request from VSCode). The "false"(default) state of this option implies that the process was launched (and will be managed) by the user. - args.push("--vscode"); - - // Launch the debugger ... launch errors will be reported in `onClose event` - logToConsole(`${ConsoleLogSources.console.toLowerCase()}: Launching new server ${JSON.stringify(command)} ${JSON.stringify(args)} ${JSON.stringify(options)}`); - var launchedDebugAdapter = child_process.spawn( - command, - args, - options, - ); - - // Capture stdout and stderr to ensure OS and RUST_LOG error messages can be brought to the user's attention. - var debuggerReadySignature = `${ConsoleLogSources.console.toLowerCase()}: Listening for requests on port ${debugServer[1]}`; - launchedDebugAdapter.stderr?.on('data', (data: string) => { - if (data.includes(debuggerReadySignature)) { - debuggerStatus = DebuggerStatus.running; - } else if (debuggerStatus === (DebuggerStatus.running as DebuggerStatus)) { - logToConsole(data, true); - } else { - vscode.window.showErrorMessage(data); - } - }); - launchedDebugAdapter.on('close', (code: number | null, signal: string | null) => { - if (debuggerStatus !== (DebuggerStatus.failed as DebuggerStatus)) { - handleExit(code, signal); - } - }); - launchedDebugAdapter.on('error', (err: Error) => { - if (debuggerStatus !== (DebuggerStatus.failed as DebuggerStatus)) { - debuggerStatus = DebuggerStatus.failed; - vscode.window.showErrorMessage(`probe-rs-debugger process encountered an error: ${JSON.stringify(err)}`); - launchedDebugAdapter.kill(); - } - }); - // Wait to make sure probe-rs-debugger startup completed, and is ready to accept connections. - var msRetrySleep = 250; - var numRetries = 5000 / msRetrySleep; - while (debuggerStatus === DebuggerStatus.starting) { - await new Promise((resolve) => setTimeout(resolve, msRetrySleep)); - if (numRetries > 0) { - numRetries--; - } else { - debuggerStatus = DebuggerStatus.failed; - logToConsole(`${ConsoleLogSources.error}: Timeout waiting for probe-rs-debugger to launch`); - vscode.window.showErrorMessage("Timeout waiting for probe-rs-debugger to launch"); - break; - } - } - - if (debuggerStatus === (DebuggerStatus.running as DebuggerStatus)) { - await new Promise((resolve) => setTimeout(resolve, 500)); // Wait for a fraction of a second more, to allow TCP/IP port to initialize in probe-rs-debugger - } - } - - // make VS Code connect to debug server - if (debuggerStatus === (DebuggerStatus.running as DebuggerStatus)) { - return new vscode.DebugAdapterServer(+debugServer[1], debugServer[0]); - } else { - return undefined; - } - - } - - dispose() { - logToConsole(`${ConsoleLogSources.console.toLowerCase()}: Closing probe-rs debug extension`); - } + break; + } + } + + // Note. We do NOT use `DebugAdapterExecutable`, but instead use `DebugAdapterServer` in all cases. + // - The decision was made during investigation of an [issue](https://github.com/probe-rs/probe-rs/issues/703) ... basically, after the probe-rs API was fixed, the code would work well for TCP connections (`DebugAdapterServer`), but would not work for STDIO connections (`DebugAdapterServer`). After some searches I found other extension developers that also found the TCP based connections to be more stable. + // - Since then, we have taken advantage of the access to stderr that `DebugAdapterServer` offers to route `RUST_LOG` output from the debugger to the user's VSCode Debug Console. This is a very useful capability, and cannot easily be implemented in `DebugAdapterExecutable`, because it does not allow access to `stderr` [See ongoing issue in VScode repo](https://github.com/microsoft/vscode/issues/108145). + async createDebugAdapterDescriptor( + session: vscode.DebugSession, + executable: vscode.DebugAdapterExecutable | undefined, + ): Promise { + if (session.configuration.hasOwnProperty('consoleLogLevel')) { + consoleLogLevel = session.configuration.consoleLogLevel.toLowerCase(); + } + + // Initiate either the 'attach' or 'launch' request. + logToConsole( + `${ConsoleLogSources.console.toLowerCase()}: Session: ${JSON.stringify( + session, + null, + 2, + )}`, + ); + + // When starting the debugger process, we have to wait for debuggerStatus to be set to `DebuggerStatus.running` before we continue + enum DebuggerStatus { + starting, + running, + failed, + } + var debuggerStatus: DebuggerStatus = DebuggerStatus.starting; + + var debugServer = new String('127.0.0.1:50000').split(':', 2); // ... provide default server host and port for "launch" configurations, where this is NOT a mandatory config + if (session.configuration.hasOwnProperty('server')) { + debugServer = new String(session.configuration.server).split(':', 2); + logToConsole( + `${ConsoleLogSources.console.toLowerCase()}: Debug using existing server" ${JSON.stringify( + debugServer[0], + )} on port ${JSON.stringify(debugServer[1])}`, + ); + logToConsole( + `${ConsoleLogSources.console.toLowerCase()}: Please note that debug server error messages will only be reported by the existing server console.`, + ); + debuggerStatus = DebuggerStatus.running; // If this is not true as expected, then the user will be notified later. + } else { + // Find and use the first available port and spawn a new probe-rs-debugger process + var portfinder = require('portfinder'); + try { + var port: number = await portfinder.getPortPromise(); + debugServer = `127.0.0.1:${port}`.split(':', 2); + } catch (err: any) { + logToConsole(`${ConsoleLogSources.error}: ${JSON.stringify(err.message, null, 2)}`); + vscode.window.showErrorMessage( + `Searching for available port failed with: ${JSON.stringify( + err.message, + null, + 2, + )}`, + ); + return undefined; + } + var args: string[]; + if (session.configuration.hasOwnProperty('runtimeArgs')) { + args = session.configuration.runtimeArgs; + } else { + args = ['debug']; + } + args.push('--port'); + args.push(debugServer[1]); + + var options = { + cwd: session.configuration.cwd, + env: {...process.env, ...session.configuration.env}, + windowsHide: true, + }; + + var command = ''; + if (!executable) { + if (session.configuration.hasOwnProperty('runtimeExecutable')) { + command = session.configuration.runtimeExecutable; + } else { + switch (os.platform()) { + case 'win32': + command = 'probe-rs-debugger.exe'; + break; + default: + command = 'probe-rs-debugger'; + } + } + } else { + command = executable.command; + } + + // The debug adapter process was launched by VSCode, and should terminate itself at the end of every debug session (when receiving `Disconnect` or `Terminate` Request from VSCode). The "false"(default) state of this option implies that the process was launched (and will be managed) by the user. + args.push('--vscode'); + + // Launch the debugger ... launch errors will be reported in `onClose event` + logToConsole( + `${ConsoleLogSources.console.toLowerCase()}: Launching new server ${JSON.stringify( + command, + )} ${JSON.stringify(args)} ${JSON.stringify(options)}`, + ); + var launchedDebugAdapter = child_process.spawn(command, args, options); + + // Capture stdout and stderr to ensure OS and RUST_LOG error messages can be brought to the user's attention. + var debuggerReadySignature = `${ConsoleLogSources.console.toLowerCase()}: Listening for requests on port ${ + debugServer[1] + }`; + launchedDebugAdapter.stderr?.on('data', (data: string) => { + if (data.includes(debuggerReadySignature)) { + debuggerStatus = DebuggerStatus.running; + } else if (debuggerStatus === (DebuggerStatus.running as DebuggerStatus)) { + logToConsole(data, true); + } else { + vscode.window.showErrorMessage(data); + } + }); + launchedDebugAdapter.on('close', (code: number | null, signal: string | null) => { + if (debuggerStatus !== (DebuggerStatus.failed as DebuggerStatus)) { + handleExit(code, signal); + } + }); + launchedDebugAdapter.on('error', (err: Error) => { + if (debuggerStatus !== (DebuggerStatus.failed as DebuggerStatus)) { + debuggerStatus = DebuggerStatus.failed; + vscode.window.showErrorMessage( + `probe-rs-debugger process encountered an error: ${JSON.stringify(err)}`, + ); + launchedDebugAdapter.kill(); + } + }); + // Wait to make sure probe-rs-debugger startup completed, and is ready to accept connections. + var msRetrySleep = 250; + var numRetries = 5000 / msRetrySleep; + while (debuggerStatus === DebuggerStatus.starting) { + await new Promise((resolve) => setTimeout(resolve, msRetrySleep)); + if (numRetries > 0) { + numRetries--; + } else { + debuggerStatus = DebuggerStatus.failed; + logToConsole( + `${ConsoleLogSources.error}: Timeout waiting for probe-rs-debugger to launch`, + ); + vscode.window.showErrorMessage( + 'Timeout waiting for probe-rs-debugger to launch', + ); + break; + } + } + + if (debuggerStatus === (DebuggerStatus.running as DebuggerStatus)) { + await new Promise((resolve) => setTimeout(resolve, 500)); // Wait for a fraction of a second more, to allow TCP/IP port to initialize in probe-rs-debugger + } + } + + // make VS Code connect to debug server + if (debuggerStatus === (DebuggerStatus.running as DebuggerStatus)) { + return new vscode.DebugAdapterServer(+debugServer[1], debugServer[0]); + } else { + return undefined; + } + } + + dispose() { + logToConsole( + `${ConsoleLogSources.console.toLowerCase()}: Closing probe-rs debug extension`, + ); + } } // @ts-ignore class ProbeRsDebugAdapterTrackerFactory implements DebugAdapterTrackerFactory { - createDebugAdapterTracker(session: vscode.DebugSession): vscode.ProviderResult { - logToConsole( - `${ConsoleLogSources.debug}: Creating new debug adapter tracker`); - const tracker = new ProbeRsDebugAdapterTracker(); - - return tracker; - } + createDebugAdapterTracker( + session: vscode.DebugSession, + ): vscode.ProviderResult { + logToConsole(`${ConsoleLogSources.debug}: Creating new debug adapter tracker`); + const tracker = new ProbeRsDebugAdapterTracker(); + + return tracker; + } } class ProbeRsDebugAdapterTracker implements DebugAdapterTracker { - - onWillReceiveMessage(message: any) { - if (consoleLogLevel === toCamelCase(ConsoleLogSources.debug)) { - logToConsole(`${ConsoleLogSources.debug}: Received message from debug adapter: + onWillReceiveMessage(message: any) { + if (consoleLogLevel === toCamelCase(ConsoleLogSources.debug)) { + logToConsole(`${ConsoleLogSources.debug}: Received message from debug adapter: ${JSON.stringify(message, null, 2)}`); - } - } + } + } - onDidSendMessage(message: any) { - if (consoleLogLevel === toCamelCase(ConsoleLogSources.debug)) { - logToConsole(`${ConsoleLogSources.debug}: Sending message to debug adapter: + onDidSendMessage(message: any) { + if (consoleLogLevel === toCamelCase(ConsoleLogSources.debug)) { + logToConsole(`${ConsoleLogSources.debug}: Sending message to debug adapter: ${JSON.stringify(message, null, 2)}`); - } - } + } + } - onError(error: Error) { - if (consoleLogLevel === toCamelCase(ConsoleLogSources.debug)) { - logToConsole(`${ConsoleLogSources.error}: Error in communication with debug adapter: + onError(error: Error) { + if (consoleLogLevel === toCamelCase(ConsoleLogSources.debug)) { + logToConsole(`${ConsoleLogSources.error}: Error in communication with debug adapter: ${JSON.stringify(error, null, 2)}`); - } - } - - onExit(code: number, signal: string) { - handleExit(code, signal); - } + } + } + onExit(code: number, signal: string) { + handleExit(code, signal); + } } - - diff --git a/src/web/extension.ts b/src/web/extension.ts index a0dfe16..6200661 100644 --- a/src/web/extension.ts +++ b/src/web/extension.ts @@ -10,5 +10,5 @@ // } export function deactivate() { - // nothing to do + // nothing to do } diff --git a/tsconfig.json b/tsconfig.json index 86b8334..5877dea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,29 +1,25 @@ { - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "outDir": "out", - "lib": [ - "es6" - ], - "sourceMap": true, - "rootDir": "src", - "strict": true, /* enable all strict type-checking options */ - /* Additional Checks */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "out", + "lib": ["es6"], + "sourceMap": true, + "rootDir": "src", + "strict": true /* enable all strict type-checking options */, + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitAny": false, - "removeComments": false, - "noUnusedLocals": true, - "noImplicitThis": true, - "inlineSourceMap": false, - "preserveConstEnums": true, - "strictNullChecks": true, - "noUnusedParameters": false - }, - "exclude": [ - "node_modules" - ] + "noImplicitAny": false, + "removeComments": false, + "noUnusedLocals": true, + "noImplicitThis": true, + "inlineSourceMap": false, + "preserveConstEnums": true, + "strictNullChecks": true, + "noUnusedParameters": false + }, + "exclude": ["node_modules"] }