diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dc431b..438ea21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the "Simbolik VSCode" extension will be documented in this file. +## [5.0.0] - 2025-01-13 + +- Simbolik only recompiles the contracts if the source code has changed + ## [4.1.0] - 2024-12-19 - The web version now offers experimental support for debugging transactions from BuildBear sandboxes. diff --git a/README.md b/README.md index 06a6f89..1d245f1 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Visit our Documentation: https://docs.runtimeverification.com/simbolik Join our Discord: https://discord.gg/jnvEeDxW Join our TG group: https://t.me/rv_simbolik + + ## Features - **Step-by-step debugging**: Debug your Solidity smart contracts line by line. diff --git a/images/simbolik-screenshot-1.png b/images/simbolik-screenshot-1.png new file mode 100644 index 0000000..11a019b Binary files /dev/null and b/images/simbolik-screenshot-1.png differ diff --git a/package-lock.json b/package-lock.json index 3ad2543..5670455 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "simbolik", - "version": "4.1.0", + "version": "5.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "simbolik", - "version": "4.1.0", + "version": "5.0.0", "dependencies": { "@solidity-parser/parser": "^0.18.0", "@types/ws": "^8.5.10", @@ -1405,9 +1405,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -1513,9 +1513,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", "dev": true, "funding": [ { @@ -1793,9 +1793,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.74", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz", - "integrity": "sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==", + "version": "1.5.80", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", + "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==", "dev": true, "peer": true }, @@ -1806,9 +1806,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -1828,9 +1828,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, "peer": true }, @@ -2273,16 +2273,16 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -2313,16 +2313,26 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", + "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "peer": true }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -2942,9 +2952,9 @@ } }, "node_modules/is-core-module": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", - "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "dependencies": { "hasown": "^2.0.2" @@ -3645,9 +3655,9 @@ } }, "node_modules/ora/node_modules/chalk": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.0.tgz", - "integrity": "sha512-ZkD35Mx92acjB2yNJgziGqT9oKHEOxjTBTDRpOsRWtdecL/0jM3z5kM/CTzHWvHIen1GvkM85p6TuFfDGfc8/Q==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -4196,9 +4206,9 @@ } }, "node_modules/resolve": { - "version": "1.22.9", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", - "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "dependencies": { "is-core-module": "^2.16.0", @@ -4208,6 +4218,9 @@ "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4787,9 +4800,9 @@ } }, "node_modules/ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -4852,9 +4865,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -4870,9 +4883,9 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -4891,7 +4904,7 @@ "peer": true, "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" diff --git a/package.json b/package.json index 9893a7a..f7a43b3 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "publisher": "runtimeverification", "description": "Advanced Solidity and EVM Debugger", - "version": "4.1.0", + "version": "5.0.0", "engines": { "vscode": "^1.79.0" }, @@ -63,29 +63,23 @@ "order": 2 }, "simbolik.autobuild": { - "type": "boolean", - "default": true, - "description": "If set to true, the debugger will automatically build the project before starting the debugger.", + "type": "string", + "enum": ["never", "on-change", "always"], + "default": "on-change", + "markdownEnumDescriptions": [ + "Never build the project automatically. This is useful when you're using `forge build --watch` to build the project.", + "Build the project when the source code changes.", + "Always build the project before every debugging session." + ], + "description": "Chose when Simbolik should build the project.", "order": 3 }, - "simbolik.incremental-build": { - "type": "boolean", - "default": false, - "description": "If autobuild is enabled and incremental-build is set to true, the debugger will use incremental builds. Notice, that the support for incremental builds is experimental and sometimes leads to unexpected behavior.", - "order": 4 - }, "simbolik.stop-at-first-opcode": { "type": "boolean", "description": "If set to true, the debugger will stop at the first opcode. Otherwise it will stop at the function entry. Disabling this option is experimental and may lead to unexpected behavior.", "default": true, "order": 5 }, - "simbolik.enable-parameters": { - "type": "boolean", - "default": false, - "description": "If set to true, the debugger will show a debug button above functions with parameters. Notice, that this requires a backend that supports parameter debugging. The default Foundry backend does not support parameter debugging.", - "order": 6 - }, "simbolik.show-sourcemaps": { "type": "boolean", "default": false, diff --git a/src/CodelensProvider.ts b/src/CodelensProvider.ts index ffb1981..af22e6c 100644 --- a/src/CodelensProvider.ts +++ b/src/CodelensProvider.ts @@ -58,7 +58,6 @@ export class CodelensProvider implements vscode.CodeLensProvider { } private getFunctions(ast: any): [ContractDefinition, FunctionDefinition][] { - const enableParameters = getConfigValue('enable-parameters', false); const results: [ContractDefinition, FunctionDefinition][] = []; parser.visit(ast, { ContractDefinition: contract => { @@ -73,12 +72,12 @@ export class CodelensProvider implements vscode.CodeLensProvider { } }, }); - if (enableParameters || !hasConstructorArgs) { + if (!hasConstructorArgs) { parser.visit(contract, { FunctionDefinition: fn => { if ( this.isExecutable(fn) && - (enableParameters || fn.parameters.length === 0) + fn.parameters.length === 0 ) { results.push([contract, fn]); } diff --git a/src/WorkspaceWatcher.ts b/src/WorkspaceWatcher.ts new file mode 100644 index 0000000..128d5cb --- /dev/null +++ b/src/WorkspaceWatcher.ts @@ -0,0 +1,34 @@ +import * as vscode from 'vscode'; + +/** + * Watches for changes to Solidity files in the workspace. + * This is used to determine when a project needs to be recompiled. + */ +export +class WorkspaceWatcher { + constructor( + private _hasChanges : boolean = false + ) { + vscode.workspace.onDidSaveTextDocument(document => { + if (document.languageId === 'solidity') { + this._hasChanges = true; + } + }); + vscode.workspace.onDidCreateFiles(event => { + if (event.files.some(uri => uri.fsPath.endsWith('.sol'))) { + this._hasChanges = true; + } + }); + vscode.workspace.onDidRenameFiles(event => { + if (event.files.some(uri => uri.newUri.fsPath.endsWith('.sol'))) { + this._hasChanges = true; + } + }); + } + hasChanges() : boolean { + return this._hasChanges; + } + reset() { + this._hasChanges = false; + } +} diff --git a/src/extension.ts b/src/extension.ts index a2e7e77..6ed2881 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,6 +6,7 @@ import {SolidityDebugAdapterDescriptorFactory} from './DebugAdapter'; import {startDebugging} from './startDebugging'; import {KastProvider, viewKast} from './KastProvider'; import {getConfigValue} from './utils'; +import { WorkspaceWatcher } from './WorkspaceWatcher'; const outputChannel = vscode.window.createOutputChannel( 'Simbolik Solidity Debugger', @@ -35,9 +36,11 @@ export function activate(context: vscode.ExtensionContext) { ); context.subscriptions.push(disposable); + const workspaceWatcher = new WorkspaceWatcher(); + disposable = vscode.commands.registerCommand( 'simbolik.startDebugging', - (contract, method) => startDebugging(contract, method), + (contract, method) => startDebugging(contract, method, workspaceWatcher), ); context.subscriptions.push(disposable); diff --git a/src/foundry.ts b/src/foundry.ts index fe73ed9..830ffc1 100644 --- a/src/foundry.ts +++ b/src/foundry.ts @@ -4,7 +4,6 @@ import {parse as parseToml} from 'smol-toml'; export function forgeBuildTask(file: vscode.Uri) { - const incrementalBuild = getConfigValue('incremental-build', false); const forgePath = getConfigValue('forge-path', 'forge'); const cwd = file.with({path: file.path.split('/').slice(0, -1).join('/')}).fsPath; const task = new vscode.Task( @@ -23,7 +22,7 @@ function forgeBuildTask(file: vscode.Uri) { 'FOUNDRY_EXTRA_OUTPUT': '["storageLayout", "evm.bytecode.generatedSources", "evm.legacyAssembly", "evm.deployedBytecode.immutableReferences"]', 'FOUNDRY_BYTECODE_HASH': 'ipfs', 'FOUNDRY_CBOR_METADATA': 'true', - 'FOUNDRY_FORCE': incrementalBuild ? 'false' : 'true', + 'FOUNDRY_FORCE': 'true', } }) ); diff --git a/src/startDebugging.ts b/src/startDebugging.ts index 1ffd093..a6cb12c 100644 --- a/src/startDebugging.ts +++ b/src/startDebugging.ts @@ -7,10 +7,12 @@ import { import * as vscode from 'vscode'; import { getConfigValue } from './utils'; import { forgeBuildTask, foundryRoot, loadBuildInfo } from './foundry'; +import { WorkspaceWatcher } from './WorkspaceWatcher'; export async function startDebugging( contract: ContractDefinition, - method: FunctionDefinition + method: FunctionDefinition, + workspaceWatcher: WorkspaceWatcher ) { return await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, @@ -59,20 +61,46 @@ export async function startDebugging( const debugConfigName = `${contractName}.${methodSignature}`; const jsonRpcUrl = getConfigValue('json-rpc-url', 'http://localhost:8545'); const sourcifyUrl = getConfigValue('sourcify-url', 'http://localhost:5555'); - const autobuild = getConfigValue('autobuild', true); - if (autobuild) { + const autobuild = getConfigValue<'always'|'on-change'|'never'>('autobuild', 'on-change'); + + // Auto build if needed + // Notice, that if autobuild is set to 'on-change' and the project is not built, the project will be built + // This case is handled after this block + if (autobuild == 'always' || (autobuild == 'on-change' && workspaceWatcher.hasChanges())) { + progress.report({ message: "Compiling" }); + const build = forgeBuildTask(activeTextEditor.document.uri); + const buildExecution = await vscode.tasks.executeTask(build); + try { + await completed(buildExecution); + workspaceWatcher.reset(); + } catch (e) { + vscode.window.showErrorMessage('Failed to build project.'); + return; + } + } + + let buildInfo; + try { + buildInfo = await loadBuildInfo(activeTextEditor.document.uri); + } catch (e) { + if (autobuild == 'never') { + vscode.window.showErrorMessage('Failed to load build info. Please build the project first.'); + return; + } progress.report({ message: "Compiling" }); const build = forgeBuildTask(activeTextEditor.document.uri); const buildExecution = await vscode.tasks.executeTask(build); try { await completed(buildExecution); + workspaceWatcher.reset(); + buildInfo = await loadBuildInfo(activeTextEditor.document.uri); } catch (e) { vscode.window.showErrorMessage('Failed to build project.'); return; } } + const myFoundryRoot = await foundryRoot(activeTextEditor.document.uri); - const buildInfo = await loadBuildInfo(activeTextEditor.document.uri); const myDebugConfig = debugConfig( debugConfigName, file,