All notable changes to Solidity Workbench are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Constructor hovers showed no NatSpec (or
[Function: Object]).constructor,receive, andfallbackare now indexed like other declarations, and built-in-global lookup no longer readsObject.prototype.constructorwhen the cursor is on theconstructorkeyword. @inheritdocon apublic constantdid not pull interface getter NatSpec. Implementations that mirror an interface function with an auto-generated getter now inherit the parent@noticeand@returndocumentation.- Fuzzy workspace-symbol queries with no trigram overlap returned nothing.
Queries such as
Cntr→Counternow fall back to ordered-subsequence scoring when trigram pruning finds no candidates.
@inheritdocdid not show inherited NatSpec in hover or signature help. Implementations that only reference a base interface now display the parent@notice,@dev,@param, and@returndocumentation instead of an empty “Inherits Documentation From” label.
- CI release publish is Open VSX only on
v*.*.*tags: usesOVSX_TOKEN(legacyOVSX_PATstill accepted), fails the job if the token is missing orovsx publisherrors. VS Code Marketplace publish removed from the workflow. - Extension marketplace keywords expanded for Open VSX discovery (LSP, language server, Solidity IDE, etc.).
- File-level Solidity declarations were invisible to much of the LSP. Struct/enum members, free functions, file-level constants, and UDVTs are now indexed, completed, and surfaced in outline, code lenses, signature help, and go-to-definition — including receiver-typed member access and import aliases.
- Hyphenated custom NatSpec tags were folded into the previous
section.
@custom:security-contactnow parses as its own custom tag instead of becoming continuation text for@noticeor@dev, and hover/signature documentation renders it as a standaloneSecurity Contact: ...line.
- Editor latency stopped scaling with workspace size. The
call-hierarchy provider's
invalidateFileran on every keystroke and walked every callee name in the workspace, so the per-keystroke cost grew linearly with the indexed dependency tree (forge-std, OpenZeppelin, project libraries). Add per-file inverse indexes so invalidation touches only the names referenced from the changed file — typically a few dozen even in large files. This is the primary fix for the "feels fine at first, gets bad after a while" reports. - Bounded the call-hierarchy
fileTextCache. Source text for every dependency file consulted viasolcBridge.resolveReferenceused to be cached forever; on long-running editor sessions the cache grew without bound. Capped at 256 entries with insertion- order LRU eviction. - Mutate-restore semantic-tokens parameter scoping. The
reference-token pass cloned the file's
nameKindsmap per function to add parameter overrides — a 50-function file did 50 full-map copies on every keystroke. Push and pop overrides on the shared map instead. - Memoized code-lens selectors by canonical signature.
keccak256was being computed afresh for every function/event/error every time the editor requested code lenses. Cache by signature (content- addressed, so cache entries can never go stale). - Precomputed newline offsets in the call-hierarchy index pass.
The per-call-match
bodyText.slice(0, idx).match(/\n/g)was O(call_sites · body_length) per function. Walk the body once up front and binary-search the offset table per match. - Skipped heavy directories during workspace discovery. Initial
walkDirectoryexcluded onlynode_modulesandout, so the recursive walk descended into.git/, Foundry'scache/andbroadcast/,coverage/,target/, editor-config dirs, and JS toolchain caches. Now skips a fuller list, cutting the cold-start I/O on every editor restart.
-
Remote Chain Interaction UI. There was previously no in-IDE way to invoke a deployed contract's read-only methods — users had to drop to a terminal and remember
cast call <addr> "<sig>" --rpc-url <url>syntax by hand. Newsolidity-workbench.remoteChain.opencommand (palette-only) opens a webview panel with a chain picker, a contract address input, an ABI loader (paste JSON or pick fromout/**/*.json), a function picker grouped by mutability, and a typed args form per Solidity parameter type. Arrays accept JSON-array literals, tuples accept JSON objects (flattened to cast's(a,b,c)form in declaration order), bigint values are forwarded as decimal strings end-to-end souint256values past 2^53 round-trip intact. Read calls are dispatched throughexecFileAsync(not the terminal sendText pattern incommands/cast.ts) so the panel can capture stdout; results render in cards showing the raw hex output AND the decoded value with copy buttons. The seven seeded public RPCs are: Ethereum Mainnet (LlamaRPC), Sepolia (Tenderly public gateway), Base / Base Sepolia (Coinbase official), Optimism (OP Labs official), Arbitrum One (Offchain Labs official), and Polygon PoS (Polygon official) — every entry carries an inline source comment per the onchain-conventions rule. A "Custom RPC URL…" option is the escape hatch for Alchemy / Infura / private endpoints. Last selection (chain, address, abi-source) persists incontext.workspaceState. New pure helpersformatFunctionSignature,formatFunctionDisplaySignature,formatFunctionSignatureWithReturns, andisReadOnlyship in@solidity-workbench/common/abi-signature.tswith 27 server-side unit tests covering scalars, dynamic / fixed arrays, tuples, nested tuples, tuple arrays, return-types appending, and error cases. Write functions render in a separate<optgroup>titled "Writes — disabled in 0.3.2" with each option carrying atitle="writes require a private key — coming in a later release"tooltip. Implementation note:cast decode-outputdoes not exist on Foundry 1.5 — the plan called for that subcommand but the available one iscast decode-abi(no--jsonflag). Instead the panel invokes cast twice in parallel — once with the bare signature for raw hex, once with thename(in)(out)form for cast's built-in inline decoding — and renders both. Etherscan ABI fetch,cast send, multi-chain calls, and gas estimation overlays are explicitly deferred. -
Chisel REPL is now a structured webview panel. The 0.1.0 integration shelled chisel into a
vscode.Terminal; the terminal works but loses every advantage a structured panel gives — output is interleaved with whatever else the user types, there's no per-evaluation card, no history, no programmatic re-run, and no way to scriptsendSelectionagainst a live session. Replaced withChiselPanel: avscode.WebviewPanelthat owns a long-livedchisel(orchisel --fork-url <url>) subprocess spawned viachild_process.spawn, with per-evaluation cards (expression, response body, ok/error status badge, timestamp) and click-to-rerun on every history entry. Thesolidity-workbench.chisel.start/.startFork/.sendSelectioncommand IDs are unchanged — only the implementation moved — so existing keybindings continue to work.sendSelectionopens / focuses the panel and forwards the selection to the live session, waiting up to 5 seconds for chisel to become ready when the panel is being spawned fresh. History persists across sessions incontext.globalStateundersolidity-workbench.chisel.history, capped at 200 entries; the panel exposes a Clear History button. The subprocess is terminated on panel disposal AND on extensiondeactivate()(SIGTERM with a one-second SIGKILL fallback) so a VSCode reload doesn't leak a chisel process. Pure helperssplitChiselOutputByPrompt,ChiselOutputBuffer,stripAnsi,stripChiselBanner, andclassifyBodylive in@solidity-workbench/common/chisel-output.tswith 28 new server-side unit tests covering ANSI stripping, banner consumption, multi-chunk buffering, error classification, and subprocess-exit drain. Implementation note: chisel disables its➜prompt underreedlinewhen stdout is not a TTY (whichchild_process.spawndoesn't provide), so the panel pairs inputs to outputs via a 250 ms quiet-window heuristic instead of a prompt sentinel; this is documented at length in the module's prelude. -
Parallel parsing via a
worker_threadspool. Bulk workspace indexing was single-threaded — every.solfile parsed one after another on the LSP main thread, with the chunked yields from 0.2.0 keeping requests alive but no actual concurrency. A newParserPoolspawnsmin(6, max(1, cpus()/2))workers (each loading@solidity-parser/parseronce at startup) andSymbolIndex.indexFileroutes throughparser.parseAsync, which fans parses out across the pool.Promise.allover the per-batch slice is what actually drives concurrency — without it the awaited chain would still serialize. Empirically a 2.3× warm-pool speedup on a forge-std-only fixture; bigger wins on largerlib/trees where worker boot amortizes better. The raw ANTLR AST is intentionally not shipped across the worker boundary (too large; only the linter and semantic-tokens provider need it, and those run on user-opened files which the main thread parses synchronously).parser.getRawAstlazily re-parses on the main thread for the rare bulk-indexed file whose raw AST a downstream consumer reaches for. If the worker bundle is missing orWorkerconstruction throws, parser silently falls back to single-threaded synchronous parsing — the pre-pool behaviour. -
Test Explorer streams rich forge output into the run pane. The provider was piping pass/fail signals through the
TestRunAPI but never callingappendOutput, so VSCode's per-run output channel stayed empty and surfaced its default "The test run did not record any output." message — even thoughforge test --jsonproduces a wealth of detail (per-test gas, durations, decodedconsole.logoutput, fuzz run counts and median gas, counterexample calldata, build errors). Now formats each test result into the run pane scoped to its own child item viaappendOutput(text, undefined, childItem), so selecting a test in the explorer narrows the output to just its slice. The forge command line andcwdare echoed at the start of each invocation; each suite gets a footer summary ([suite] N passed, M failed); fuzzkind: { runs, mean_gas, median_gas }, unitkind: { gas }, and invariantkind: { runs, calls, reverts }are pulled out into the per-test header. Build errors / forge stderr now also flow into the pane instead of disappearing into the error toast.
- Storage Layout types rendered as raw forge type ids and the
legend colors never appeared. The panel was reading
entry.typestraight from forge, which is a type id liket_mapping(t_userDefinedValueType(PoolId)16015,t_uint256)— unreadable, andtypeColor'sstartsWith("uint" | "address" | "mapping" | …)checks never matched thet_prefix, so every slot drew with the default grey. The two pieces of data needed to render are indata.types[entry.type]: the human-readablelabel(e.g.mapping(PoolId => uint256)) andnumberOfBytes. Resolved each entry through that map, which both restores the legend colors and gives packed slots proportional bar widths instead of the previous every-bar-is-32-bytes default. - Multi-line natspec rendered as one run-on paragraph in
hover. The natspec parser was filtering out blank
///separator lines and joining every continuation line with a single space — so authored structure like paragraph breaks,## headings, and numbered lists collapsed into a single unreadable block in the hover popover. Continuation lines now join with\n(Markdown renders a single newline as a soft wrap; visually identical for short prose) and blank separator lines are preserved as\n\nparagraph breaks. The edge-trimming pass that previously dids.replace(/\\s+/g, " ")no longer flattens those newlines back out — it tidies intra-line whitespace only and keeps the Markdown structure end-to-end through the hover renderer. Applies to@notice,@dev,@param,@return, and@custom:*content;@titleand@authorstay single-line as before. - Duplicate
solidity-workbench.inspectStoragePanelmanifest entry. The 0.3.0solforge.*cleanup renamedsolforge.inspectStoragePaneltosolidity-workbench.inspectStoragePanelwithout noticing that the canonical entry already lived earlier in the contributes block. Net effect was the command palette listing "Solidity Workbench: Storage Layout Visualization" twice; both invoked the same registered command, so it was a UI papercut rather than a functional bug. Down to 46 unique command IDs.
- Storage Layout Visualization failed across every contract.
The panel ran
forge inspectwithcwd: workspaceFolders[0], so any workspace whose root sat above the actual Foundry project (a monorepo subdirectory, this repo's owntest/fixtures/sample-projectlayout, or a workspace opened one level too high) would have forge pick up nofoundry.tomland fail uniformly with "No contract found" — surfaced to the user as the unhelpful "Make sure the project compiles." Now resolves the nearestfoundry.tomlancestor of the active file viafindForgeRoot(matching the test-explorer fix from 0.2.0). The error path also surfaces forge's stderr verbatim so the actual cause is visible — compile error, ambiguous name, forge not on PATH, etc. Same fix applied to the ABI Explorer panel. - ABI Explorer was reading the wrong configuration namespace.
getConfiguration("solforge")(a leftover from a previous brand name) silently returned the default for every key, so the user'ssolidity-workbench.foundryPathsetting was ignored — only theforgeon PATH was ever invoked. Switched togetConfiguration("solidity-workbench").
-
Normalized command IDs. Four commands shipped under a legacy
solforge.*namespace from an earlier branding pass and never got renamed when the extension became Solidity Workbench. Renamed:solforge.gasDiff→solidity-workbench.gasDiffsolforge.gasDiffRefresh→solidity-workbench.gasDiffRefreshsolforge.inheritanceGraph→solidity-workbench.inheritanceGraphsolforge.showAbi→solidity-workbench.showAbi
Any existing keybindings or
tasks.jsonreferences will need updating. The matching tree-view ID (solforge-gas-diff) and webview type IDs (solforge-inheritance-graph,solforge-abi-panel) are also normalized; menu group names follow. -
Removed dead manifest entries that contributed
solforge.*commands with no matchingregisterCommandimplementation. Clicking these palette entries did nothing. The functionality was always reachable through the existingsolidity-workbench.*commands they shadowed:solforge.inspectStoragePanel(impl issolidity-workbench.inspectStoragePanel)solforge.chisel.start/.startFork/.sendSelection(already declared assolidity-workbench.chisel.*earlier in the manifest)solforge.copySelector(an internal command — the manifest entry exposed it in the palette by mistake; the real implementation issolidity-workbench.copySelector, invoked programmatically by code-lens click handlers)
- Hover, inlay hints, and definition were unresponsive until the
full workspace index finished. 0.2.0's "flush pending on miss"
fallback was correctness-correct but blocked the LSP: a single
lookup miss on a
forge-stdsymbol drained every remaininglib/file synchronously before the request returned. While the drain ran, the event loop couldn't dispatch any other LSP requests or progress notifications, which read to users as "the editor is frozen until indexing finishes" — the same symptom chunked indexing was meant to fix. Replaced withSymbolIndex.ensureImportsIndexed, which walks the transitive import graph of an opened/changed document and indexes only the files that document actually pulls in (typically a few dozen, vs. the wholelib/tree). Wired intodocuments.onDidChangeContentas fire-and-forget; avisitedset prevents re-work on subsequent edits and infinite recursion on import cycles. By-name lookups (findSymbols,getContract,findReferences, etc.) are back to fast cache reads. - Hover and signature-help natspec rendering. Two
>blockquote markers in front of theDev:label rendered as quoted blocks in the hover popover instead of inline emphasis. Switched to plain**Dev:**formatting.
- LSP startup no longer blocks until every dependency file is
indexed. The previous
for..of await indexFile()walk only crossed microtask boundaries between files, which doesn't let the event loop dispatch hover / completion / diagnostics requests; users with a populatedlib/tree saw the editor stall on activation.WorkspaceManagernow partitions discovered.solfiles intoproject(src/),tests(test/+script/), anddeps(lib/) tiers, andSymbolIndex.indexWorkspacewalks them in priority order with asetImmediateyield every 24 files. Project symbols become queryable while deps continue indexing in the background. - Streamed indexing progress.
serverStatenotifications now fire at every batch boundary, so the status bar showsIndexing 24/N,48/N, … instead of jumping from 0 to done. - Symbol-lookup misses drain the pending queue.
findSymbols,getContract,findReferences,referenceCount, andhasReferencesfall back to a synchronous drain of remaining tier files when the in-memory cache returns empty during the indexing window. The first miss on aforge-stdsymbol pays the catch-up cost; every later query takes the fast path. Editor-drivenupdateFilecalls also clear the file from the pending set so the bulk loop doesn't re-index something the user just opened.
- Marketplace upload rejected with "suspicious content". Two
GitHub URLs in the README still pointed at the old organization
the repository moved from. The Marketplace's content scanner
flags publisher/repository mismatches as potential
typosquatting. Repointed both URLs (the CI artifacts link and
the
git clonecommand) at the currentccashwellrepo. - VSIX shipped 4.7 MB of unnecessary source maps. Both
extension.js.mapandserver.js.mapwere being packaged even though end users never consume them — they added bytes the content scanner has to walk (maps embed the fullsourcesContentof our TS source) and tripled the VSIX size for no end-user benefit. Drop them from the packaged artifact via.vscodeignore; localdist/still has them for debugging. VSIX size now 416 KB (was 1.17 MB). - Test Explorer ran
forge testbut never displayed any results. Four compounding bugs, all fixed together:- stdout was split by
\nand each line parsed as JSON. Real forge--jsonoutput is a single JSON blob on one line, so only stray valid-JSON lines ever parsed — typically nothing. data.test_resultswas read one level too shallow. The actual shape is{ "<file>:<Contract>": { test_results: {...} } }, so the lookup was alwaysundefined.findChildByNamecompared the forge signature key (test_Increment()) against the TestItem label (test_Increment), so child matching always fell through to the parent container.- forge ran with
cwd = workspaceFolders[0]regardless of where the test actually lived. In a monorepo where the workspace root sits above the Foundry project (the common case for this repo'stest/fixtures/sample-project/), forge picked up the wrongfoundry.toml(or none at all) and emitted an empty result set, producing VSCode's "test run did not record any output" message. Now the parser consumes the whole blob as one JSON object, walks both levels of the forge shape, matches children by stripping the signature suffix (viastripForgeTestSignature), parses forge's human-readable duration strings viaparseForgeDurationMs, and resolves the correctcwdby walking up from each test file to the nearestfoundry.tomlvia a newfindForgeRoothelper. Unknown / empty forge responses now resolve the parent item asskippedinstead of leaving it in the started state forever. AddedstripForgeTestSignature,parseForgeDurationMs, andfindForgeRoothelpers to@solidity-workbench/common/foundry-cli.ts, with 13 unit tests across the real forge output shapes and nested-project scenarios (including an outer stubfoundry.tomlthat must NOT shadow the real project config).
- stdout was split by
forge testverbosity flag was malformed. Every command that invokedforge testwas building the verbosity flag as"-".repeat(n) + "v"— producing--vfor the defaulttest.verbosity=2, which forge rejects witherror: unexpected argument '--v' found. Coverage, the test explorer, and the palettetest/testFile/testFunctioncommands all hit it. Centralized the flag building in a newforgeVerbosityFlaghelper in@solidity-workbench/common(short flag shape:-v,-vv,-vvv,-vvvv,-vvvvv; empty for level 0; clamped at 5) so it can't be re-introduced. 6 unit tests cover every boundary including a/^-v+$/assertion that would have caught the original bug.- Auto-import quick fixes no longer offer to import symbols that
are declared in the current file, and no longer leak into Quick
Fix menus invoked on unrelated diagnostics. Three fixes in
AutoImportProvider:collectLocalNamesnow includes every kind of local declaration — contracts, interfaces, libraries, structs, enums, events, errors, modifiers, contract functions, file-level errors, file-level free functions, UDVTs, import aliases, and import unit aliases. Previously only contracts / structs / enums were treated as local, so locally-declared events / errors / UDVTs / free functions got flagged as "unresolved" and the provider offered to import same-named declarations from other files.findImportCandidatesshort-circuits to[]whenever any symbol with the requested name is declared in the current file. Defense-in-depth against stale / spurious "Undeclared identifier" diagnostics that would otherwise still trigger a wrong import suggestion.- Proactive import suggestions are now scoped to the
code-action request's
range. Quick Fix invoked on an unrelated diagnostic — e.g. the pragma line's floating-pragma warning — no longer shows a menu of imports for every uppercase identifier in the file. 5 new regression tests lock the behaviour in.
forge build --match-pathwas invalid — subgraph auto-compile emittedforge build --match-path <file>, but--match-pathis aforge testflag;forge buildexpects positional path arguments. Switched toforge build <relative-path>, which forge resolves against the configuredsrc//contracts/trees.- Word-at-cursor highlighting no longer leaks into comments and
strings. Without a
DocumentHighlightProvider, VSCode falls back to a regex-based highlighter that lights up every textual match of the cursor's word — including inside///natspec comments,//line comments, block comments, and string literals. The visible symptom was an identifier-styled "overlay" applied to ordinary prose inside comment blocks. NewDocumentHighlightProviderroutes highlights through the existing comment- and string-awareReferenceIndex, scoped to the current file. 7 new regression tests cover the comment, line-comment, and string-literal cases plus whitespace and multi-file isolation.
-
Indexer scaffolds beyond Graph Protocol. The subgraph scaffold generator now has two peers in
@solidity-workbench/common:generatePonderScaffold— emitsponder.config.ts,ponder.schema.ts(DrizzleonchainTableentries), ansrc/index.tswithponder.onhandlers, anabis/<Contract>Abi.tsTypeScriptas constmodule, pluspackage.json,.env.example, and a brief README. Column types map Solidity → Drizzle (address→t.hex(),uintN→t.bigint(), arrays →.array(), tuples →t.json()with TODO). Chain ids auto-resolve from the network slug.generateEnvioScaffold— emitsconfig.yaml,schema.graphql,src/EventHandlers.tswith<Contract>.<Event>.handlerregistrations, pluspackage.json,.env.example, and README. Event signatures drop theindexedkeyword (Envio infers it from the ABI), id derivation useschainId_blockNumber_logIndexfor cross-chain uniqueness. Shared ABI / event-signature / type-mapping helpers now live inindexer-shared.tsso all three generators stay in lockstep. New commandsolidity-workbench.indexer.scaffoldprompts for a backend (Graph / Ponder / Envio), shares the contract picker, auto-compile, and prompt flow with the legacy subgraph command (which stays as a direct shortcut to the subgraph backend). Ponder and Envio generators each get 10+ unit tests (manifest shape, type mapping, tuple warnings, unnamed params, empty-ABI stub, package.json sanity) — server test count 263 → 282.
-
Code lens: error selectors. Custom errors now get a
selector: 0x…code lens identical in shape to the function selector lens — clicking copies the 4-byte selector to the clipboard. Contract-level and file-level (global) errors are both covered.SolcBridgenow extracts error selectors from the ABI on eachforge buildand caches them for accurate struct / UDVT parameter types; cold-cache and pre-build states fall back to local keccak over the parser-level canonical signature. 4 new regression tests lock in zero-arg, primitive, file-level, and multi-error cases.
- Trigram-indexed fuzzy workspace symbols. New
packages/server/src/analyzer/trigram-index.tsmaintains a 3-gram posting list for every indexed name.findWorkspaceSymbolsnow prunes candidates via posting-list intersection for 3+-char queries and ranks matches (exact → prefix → substring → ordered-subsequence fuzzy). Typingctrin the VSCode symbol picker now surfacesCounter; long queries run sub-linearly on large workspaces. 15 new unit tests. - Aderyn (Cyfrin) static-analysis integration. Pure JSON-report
parser in
@solidity-workbench/common/aderyn-reportplus a VSCode wrapper underpackages/extension/src/analysis/aderyn.tsthat mirrors the Slither shape. New commandsolidity-workbench.aderyn, settingsaderyn.enabled/aderyn.path, and opt-in on-save analysis. 11 unit tests cover the parser, severity mapping, and summary helpers. - Subgraph scaffold generator.
generateSubgraphScaffoldin@solidity-workbench/common/subgraph-scaffold.tsemitssubgraph.yaml,schema.graphql, andsrc/<contract>.tsfrom a contract ABI — events-only scaffold with canonicalindexed-aware event signatures, scalar / array-type mapping to GraphQL, and TODO markers for tuple params. New commandsolidity-workbench.subgraph.scaffoldpicks a contract fromout/*.sol/*.json, prompts for network / address / start block, and writes the scaffold plus an ABI copy tosubgraph/<ContractName>/. 16 unit tests cover the generator. The command now auto-compiles on demand: if no artifacts exist (fresh clone, clean working copy) or the active.solfile has no matching artifact, it runsforge build(narrowed via--match-pathwhen a specific file is targeted) inside a progress notification before falling through to the quick-pick, instead of erroring out. Build failures surface the stderr in a dedicated output channel with an "Open Output" button. The active file's contract is also hoisted to the top of the pick list. - Expanded E2E coverage. Fixed the stale
uniswap.solidity-workbenchextension ID (publisher isccashwell) in the activation smoke tests. Added LSP round-trip tests for hover, workspace symbols (exact + fuzzy subsequence), references, rename response, code actions response, and formatting response — all with retry loops tolerating the async initial indexing pass.
Server unit-test count: 204 → 252.
- Parameter-name inlay hints now respect the receiver of a dotted
call.
Currency.unwrap(currency)was annotated with_wstETHAmount:— a parameter name belonging to an unrelated interface'sunwrap(uint256).InlayHintsProvidernow extracts the receiver fromReceiver.funcName(...)and either walks the receiver's inheritance chain for the matching function or emits no hints (when the receiver is a UDVT builtin likewrap/unwrap, or a variable whose type can't be inferred). Plain unqualified calls keep the existing lookup. 4 new regression tests. - Hover on dotted access no longer picks an unrelated same-named
function. Hovering
Currency.unwrap(x)could surfaceIWstETH.unwrap(uint256)simply because both declared anunwrapmethod — the hover fell through to a raw name lookup with no receiver check.HoverProvidernow detects theReceiver.memberpattern at the cursor, resolves the receiver through the symbol index (contract / interface / library / struct / UDVT), and returns only a member that actually belongs to that receiver. UDVTs synthesise a hover for their implicitwrap/unwrapfunctions, including the underlying type. When the receiver is identified but the member can't be found on it we returnnullrather than surfacing the wrong symbol. - Hover now covers every elementary Solidity type. Previously only
address,bool,string,bytes,uint256,int256, andbytes32had a hover — hoveringuint8,uint128,int24,bytes16, etc. returned nothing. Replaced the static map with a generator that recognises every legal width (uint8/int8…uint256/int256in steps of 8,bytes1…bytes32, plus theuint/int/bytealiases and the reservedfixed/ufixed(MxN)syntax). 8 new regression tests lock the coverage in. - "N references" code lens no longer throws "unexpected type" when
clicked. The lens was wired directly to VSCode's
editor.action.findReferences, which requires avscode.Uriinstance, but LSP emits URIs as strings over the wire so the command argument coercion failed. Route the lens through a new client-side shimsolidity-workbench.findReferencesAt(uri, position)that parses the URI into avscode.Uribefore invoking the editor command. 3 new regression tests cover the argument shape, the title format, and omission when a symbol has no usages. - Semantic tokens now cover struct members, event / error / function
parameters, state variable types, base contracts, user-defined value
types, and cross-body references to user-defined types. The
previous implementation only tokenized declaration names; struct
bodies like
struct EscrowedPosition { PoolKey poolKey; MarketId marketId; }rendered with no type/field colouring at all. TheSemanticTokensProvider.collectDeclarationTokensimplementation now walks the raw@solidity-parser/parserAST so every sub-node's preciselocis used, including.identifier.locon variable declarations and.typeName.locon user-defined type references.buildNameKindsalso registers contracts, interfaces, structs, enums, UDVTs, and errors so function-body references receive the correcttype/struct/enum/interfacecolour. 8 new regression tests cover the cases the old provider missed.
- AST-based linter rules.
reentrancy,missing-event,storage-in-loop,unchecked-call,dangerous-delegatecall, andunprotected-selfdestructnow walk the raw@solidity-parser/parserAST instead of regex-scanning function body text. Eliminates false positives on commented-out code and multi-line expressions. 6 new regression tests cover the cases the regex version would have missed or mis-flagged. - Scope-aware rename for locals and parameters.
RenameProviderconsultsSolcBridge.findLocalReferencesto rewrite precisely the byte ranges solc attributes to a single function-scoped declaration. Top-level symbols still use the workspace-wide path.prepareRenamegives a clear error when neither path applies (e.g. before first successfulforge build). - Canonical selectors from
forge build --json.SolcBridgecachesevm.methodIdentifiersandCodeLensProviderconsults the cache first, falling back to local keccak256 only when cold. Selectors for functions that take structs / UDTs are now Etherscan-accurate. - Deploy picker reads compiled artifacts.
deploy.ts::pickContractenumeratesout/**/*.jsonfor the authoritative contract list. When the ABI is present the constructor-args prompt becomes per-parameter with declared Solidity type in the prompt label; strings are auto-quoted forforge create. - E2E test scaffold via
@vscode/test-electron. A real VSCode binary boots with the extension loaded and the sample project open. 6-test smoke suite covering activation, command registration, language registration, and a round-trip throughvscode.executeDocumentSymbolProvider. Newe2eCI job underxvfb-run. - LCOV parser extracted into
@solidity-workbench/commonso it's unit-testable from the server test runner. - Provider test coverage expanded:
CompletionProvider,AutoImportProvider,CodeActionsProvider,DiagnosticsProvider.extractSyntaxDiagnostics, LCOV parser. Total server tests: 135 → 181.
- Line-level coverage via
forge coverage --report lcov. Each executable line gets a covered / uncovered / partial gutter decoration; branch coverage (BRDArecords) surfaces as "partial" on branches with uncovered sides. Replaces the previous file-level banner. Per-file totals still show in the status bar. - Gas profiler regression deltas. The last loaded
.gas-snapshotis persisted in extension global state; on every refresh the tree view shows arrows (↑regression /↓improvement /—no change) and inline gas decorations include the delta. - Test Explorer backed by the LSP AST. A new custom LSP request
(
solidity-workbench/listTests) enumerates test contracts and functions from the already-parsed AST instead of re-regexing each file. Handles braces inside strings, multi-line function headers, and nested contracts correctly. - Multi-root workspace support. The LSP server now tracks per-root
state (
foundry.toml, remappings, symbol index) and responds toworkspace/didChangeWorkspaceFolderswith add/remove-root plumbing. - Cancellation token support across heavy providers (
references,rename,semantic-tokens,call-hierarchy,workspace-symbols). Long-running queries now early-exit when the client cancels. - Dynamic configuration reload for every
solidity-workbench.*setting (not justfoundryPath). Debounce / verbosity / inlay-hint toggles take effect without a restart. - Status bar wired to server state — a live indicator shows LSP
readiness, most recent
forge buildresult (ok / errors / warnings), coverage percentage when loaded, and whether Anvil is running. foundry.tomlIntelliSense expanded to cover[rpc_endpoints],[etherscan],[fuzz],[invariant], and the full[profile.*]key surface area. Hovers cite the Foundry Book documentation section.- SolcBridge wired into providers. Hover, definition, and member
completion now consult the type-resolved solc AST (when a
forge buildhas succeeded) to pick the right overload and enumerate members of the concrete variable type. - Publish workflow. A tag-gated GitHub Actions job publishes the VSIX to
the VS Code Marketplace (
vsce publish) and Open VSX (ovsx publish) onv*.*.*tags. Credentials required:VSCE_PAT,OVSX_PAT. - Provider-level tests for
DefinitionProvider,HoverProvider,SignatureHelpProvider, andInlayHintsProvider. 111 → 150+ tests. - Extension icon + Marketplace gallery banner.
LICENSEfile (MIT).CHANGELOG.md(this file).
- Diagnostics include
Diagnostic.datawith the raw solcerrorCodeso code actions can key off it without parsing message text. - Reference index skips
lib/directories when a workspace has afoundry.toml— matches rename behaviour and keeps large monorepo indexes responsive.
- Gas profiler now correctly populates
GasEntry.delta(previously declared but never assigned). - Status bar
Solidity Workbenchitem is clickable to Run Build (was static label).
Initial pre-release. Feature set covers:
- LSP server with 17 providers: completion, definition, type-definition, references, hover, diagnostics, semantic tokens, code actions, formatting, document symbols, inlay hints, signature help, rename, code lens, auto-import, call hierarchy, type hierarchy.
- Custom linter with 8 security / best-practice rules.
- Foundry integration: build, test, format, gas snapshot, coverage, flatten,
storage layout, Anvil, Chisel, cast palette (9 commands),
forge scriptsimulate/broadcast/resume,forge createdeploy flow, contract verification. - Slither static analysis bridge.
- Test Explorer with inline pass/fail, fuzz counterexample display.
- Gas profiler tree view + inline decorations.
- Storage layout webview.
foundry.tomlIntelliSense (basic schema).- TextMate grammar for Solidity.
- 111 unit tests; CI matrix on Node 18/20/22 with Foundry installed.
See ARCHITECTURE.md for the design rationale and
PRODUCTION_GAPS.md for the tracked roadmap.