Arete for VS Code
IDL-aware authoring for Arete stacks — completion, hover, and diagnostics for the Solana streaming DSL.
Why
Arete stacks map entity fields onto IDL paths:
#[map(pump_sdk::accounts::BondingCurve::virtual_token_reserves, strategy = LastWrite)]
pub virtual_token_reserves: Option<u64>,
pump_sdk::accounts::BondingCurve is not real Rust — the #[arete] macro synthesizes it from the IDL at compile time. rust-analyzer cannot see it, so there is no completion, no hover, and no go-to-definition, and a typo surfaces only after a cargo build (minutes, on Solana dependencies) or after a deploy.
Every valid path is derivable from the IDL JSON already in the repo. This extension reads it, so the feedback arrives as you type instead of minutes later.
Features
- Completion inside
#[map], #[from_instruction], #[event], #[snapshot], #[aggregate], #[derive_from] — accounts, instructions, events, types, their fields, and synthetic fields like __account_address.
- Hover — the IDL type behind a mapped path, its byte offset in the encoded account (discriminator included), the discriminator itself in decimal and hex, and any docs the IDL carries.
- Diagnostics — unknown paths, illegal strategies, unknown transforms, and
accounts::* under a CPI event, each with a "did you mean". The rules mirror arete-macros' own validation, so a squiggle here means a cargo build failure there.
- Quick fixes — Ctrl+. applies the closest suggestion.
- Inlay hints — the resolved IDL type beside each mapped path, and
strategy = SetOnce where it is implied.
- Context-aware strategies —
#[map] and #[from_instruction] accept only SetOnce and LastWrite; other attributes get all nine.
- Anchor and Codama IDL dialects, including modern Anchor IDLs where account layouts live in the
types table.
Requires no CLI, no API key, and no network — an IDL file on disk is enough.
Diagnostics are conservative on purpose
A wrong completion is a suggestion you ignore. A wrong diagnostic asserts that correct code is broken — and since coding agents read the Problems panel, a false positive can induce one to "fix" working code.
So where arete-macros is permissive, this extension is too: item lookups are case-insensitive, an account whose IDL carries no field metadata accepts any field name, and events with no recoverable layout are not field-checked. When a rule cannot be decided from the IDL alone, nothing is reported.
Install
Marketplace listings pending first release. To run from source:
npm install
npm run build
Then press F5 in VS Code to launch an Extension Development Host.
Development
npm run watch # rebuild on change
npm run typecheck
npm run package # produce a .vsix
The vocabulary file
vocabulary.json is the single source of truth for DSL attributes, strategies, and transforms. No other file may hardcode those names.
Because this repo sits outside the Arete OSS monorepo, nothing automatically tells it when the DSL changes. scripts/extract-vocabulary.mjs closes that gap: it downloads the published arete-macros crate, extracts the vocabulary from source, and diffs it.
node scripts/extract-vocabulary.mjs # print what upstream declares
node scripts/extract-vocabulary.mjs --check # exit 1 on drift
CI runs --check nightly, on release dispatch from the OSS repo, and as a release gate. Drift opens an issue rather than silently shipping wrong diagnostics.
To wire the dispatch, add this to the OSS repo's release workflow after arete-macros publishes:
- name: Notify extension repo
run: |
gh api repos/AreteA4/arete-vscode/dispatches \
-f event_type=arete-released
env:
GH_TOKEN: ${{ secrets.EXTENSION_DISPATCH_TOKEN }}
Architecture
Two strictly separated tiers:
| Tier |
Work |
Rule |
| Interactive |
completion, hover, diagnostics, quick fixes, inlay hints |
IDL JSON + parse only. Never invokes cargo, never touches the network. |
| Explicit |
AST rebuild, deploy, streaming |
Background, cached, honest about staleness. |
Blurring them is the fastest way to make the extension feel slow.
Two readers, split by tolerance
src/parser/stack.ts keeps two readers, and this is settled rather than half-migrated:
- Cursor-local work — completion and hover — uses a text scanner. These run mid-keystroke, against source that does not parse:
#[map(pump_sdk:: has no closing paren and no field yet. Tree-sitter yields an ERROR node with no attribute in it at all, so a parser cannot answer here.
- Whole-file work — diagnostics and inlay hints — uses tree-sitter-rust (WASM via
web-tree-sitter; a full parse of a real stack file costs 4–7 ms). Accuracy is the point: a commented-out attribute, an attribute-lookalike in a string literal, and a module binding no IDL must all be ignored, and a regex sweep gets each of them wrong.
A useful consequence is that diagnostics fail quiet — a half-typed attribute drops out of the parse tree, so it goes unchecked rather than being reported as broken.
License
Apache-2.0