- Low-level call hierarchy edges —
callHierarchy/outgoingCallsnow surfaces.call(),.staticcall(), and.delegatecall()as synthetic call edges, covering both SolidityMemberAccessnodes (which have noreferencedDeclaration) and Yul call opcodes (which have no ASTid); selection ranges point at the.call/ opcode site (#211) - Alias-aware navigation —
textDocument/definition,textDocument/declaration,textDocument/implementation,textDocument/references, andtextDocument/renameall work correctly through import aliases (e.g.import {Foo as Bar}) (#197) - Goto-implementation fallback — when no implementations are found for a function, falls back to goto-definition instead of returning nothing (#200)
- Live buffer compilation —
textDocument/didChangecompiles the editor buffer directly, providing cross-file diagnostics for unsaved edits - Startup update check — the server checks GitHub releases on startup and shows a
window/showMessagenotification when a newer version is available; controlled by thecheckForUpdatessetting (default:true)
- Stale results in 10 LSP handlers — handlers returned
Ok(None)(JSONnull) instead ofOk(Some(vec![]))(JSON[]) when they computed zero results; LSP clients (notably Neovim) interpretednullas "server couldn't compute" and kept displaying stale data; affected handlers:inlayHint,references,workspace/symbol,documentSymbol,documentHighlight,documentLink,foldingRange,selectionRange,codeAction(#201, #202) solidity.clearCachefull reset — the command now wipes all in-memory caches (ast_cache,completion_cache,sub_caches,semantic_token_cache,path_interner) in addition to the on-disk cache directory; previously only the project-root entry was evicted fromast_cache- Empty build protection — cross-file solc errors no longer replace a valid cached AST with an empty build
- Alias goto-implementation — import alias at usage site now targets the original definition, not the alias itself
- 647 tests, 0 failures, 0 warnings
- Precise
fromRangesin call hierarchy —callHierarchy/incomingCallsandcallHierarchy/outgoingCallsnow prefermember_location(the identifier-only span) oversrc(the full expression span) forMemberAccessnodes; e.g.slot0.protocolFee().getZeroForOneFee()producesfromRangespointing at justprotocolFeeandgetZeroForOneFeeinstead of the entire chain (#191) - Cross-build node ID mismatch in
textDocument/implementation— the handler used the file build'starget_idto look upbase_function_implementationin the project build, which could resolve to a completely different function; now re-resolves the target bybyte_to_id()per build (#193)
- Neovim call hierarchy handler snippet — outgoing calls now use
ctx.params.item.urifor the file path instead ofvim.api.nvim_buf_get_name(ctx.bufnr)(which returns the wrong file whenprepareCallHierarchyresolves to a different file); both handlers sort quickfix items by line then column
- 622 tests, 0 failures, 0 warnings
- Project-wide
PathInternerfor canonical file IDs — solc assigns file IDs sequentially based on input order, causing cross-compilation reference bugs; the interner assigns deterministic IDs so all builds share the same ID space (#185) textDocument/hover— show AST node ID (NodeId: \`) in hover tooltips for debugging; references show ``NodeId: \` referencedDeclaration: `<decl_id>` ``textDocument/references— qualifier-aware reference resolution; "Find All References" on a container (contract/library/interface) now includes qualified type paths where the container appears as a prefix (e.g.,PoolinPool.State) (#187)textDocument/definition— qualifier segment goto; clicking the qualifier in a qualified type path (e.g.,PoolinPool.State) navigates to the container declaration instead of the struct/enum (#187)CachedBuildgainsqualifier_refsfield (HashMap<NodeId, Vec<NodeId>>) — maps container declaration IDs toIdentifierPathnode IDs used as qualifiers; built at cache time bybuild_qualifier_refs()and merged across builds viamerge_missing_from()(#187)NodeInfogainsscopefield — the containing declaration's node ID from the ASTscopeproperty; used to resolve qualified type path containers (#187)dedup_locations()removes both exact and contained-range duplicates from reference results, preventing qualified type paths from producing duplicate entries (#187)- Two-phase project index — phase 1 compiles src-closure for fast time-to-first-reference, phase 2 compiles full closure (src + test + script) in background (#189)
- Parallel sub-cache compilation — library sub-projects now build concurrently via
JoinSetinstead of sequentially; Asyncswap benchmark: 53.5s to 15.8s (3.4x speedup) (#190) solidity/subCachesLoadedprogress token — emitted when all library sub-caches are built and loaded, allowing benchmarks and editors to detect full pipeline completion (#190)- AST-only sub-cache compilation — sub-cache builds request only AST output (no codegen settings, no contract outputs), letting solc skip codegen entirely (#190)
- Semaphore-based concurrency cap for parallel sub-cache builds — limits parallelism to
available_parallelism()to avoid thrashing on smaller machines (#190) Displayimpl forPragmaConstraint— log messages now show>=0.8.0 <0.9.0instead of debug-format dumps (#190)textDocument/prepareCallHierarchy+callHierarchy/incomingCalls+callHierarchy/outgoingCalls— navigate call graphs across contracts and libraries; tracks function calls (FunctionCallwithkind == "functionCall"), modifier invocations, and base constructor specifiers; container aggregation (incoming/outgoing on a contract = union of all its callables); narrowfromRangesusingexpression.srcfor direct calls andmemberLocationfor member-access calls; call hierarchy index built at cache time alongside existing goto/references indexes (#191)textDocument/implementation— "Go to Implementation" jumps from interface/abstract function declarations to their concrete implementations; builds a bidirectionalbase_function_implementationindex from solc'sbaseFunctions/baseModifiersAST fields at cache time; searches file, project, and sub-cache builds (#193)textDocument/references— interface/implementation equivalence; "Find All References" onPoolManager.swapnow also returns call sites that referenceIPoolManager.swap, and vice versa; uses thebase_function_implementationindex to expand the target ID set (#193)callHierarchy/incomingCalls— interface/implementation equivalence; incoming callers via interface-typed references (e.g.,manager.swap()wheremanagerisIPoolManager) are now included when querying the implementing function (#193)
- Remove
evm.gasEstimatesfrom solc output selection — gas estimation consumed 88% of compile time (SmarDex 510-file project: 56s to 6s). Removedgas.rs,GasKey,gas_index, gas inlay hints, gas hover, andgasEstimatessetting (#189, #190) - Clean up LSP log messages — remove redundant/noisy logs (per-lib build/load lines, discovered N files, compiled with no mismatches, cache saved, path interner count), simplify cache hit/miss messages, suppress zero-count diagnostic messages (#190)
- Fix stale-offset duplicate references after editing — "Find All References" no longer produces bogus duplicates (e.g. 734 instead of 729 results) when the project cache has stale byte offsets for the current file;
goto_references_for_target()now excludes the current file from the project-cache scan since the fresh file-level build already covers it (#185) CompletionCachefile IDs are now canonicalized through thePathInternerremap, preventing scope-range mismatches across compilations (#185)- Removed dead
remap_src_file_idandremap_node_info_file_idsfunctions; simplifiedmerge_scoped_cached_build()by removing ~50 lines ofid_remapmachinery (#185) - Sub-cache loading flag was never reset to
falseafter completion — now correctly cleared (#190)
- 621 tests, 0 failures, 0 warnings
| Project | Files | Before | After | Speedup |
|---|---|---|---|---|
| SmarDex | 510 | ~56s | 8.9s | 6.3x |
| Asyncswap | 108 + 1108 lib | ~57s | 22.1s | 2.6x |
textDocument/codeAction— JSON-driven quickfix engine; handlesunused-importforge-lint diagnostic with a "Remove unused import" action (#168)workspace/executeCommand—solidity.clearCachedeletes the on-disk cache and wipes in-memory AST, forcing a clean rebuild;solidity.reindexevicts in-memory AST and triggers a fast background reindex from the warm disk cache (#175)textDocument/completion— import path completions inside import strings (#173)- Non-blocking
didSavewith per-URI watch channel serialisation — rapid saves collapse instead of stacking (#171)
- Skip redundant solc rebuild on save when file content is unchanged — eliminates ~5s recompilation from format-on-save loops on large projects (#181)
- Wrap
collect_import_pragmasinspawn_blocking— the transitive import FS crawl no longer blocks the tokio async runtime, preventing request timeouts on first save of large files (#181) textDocument/referencesnow returns results for state variables and other declarations (was returning null) — 11 refs forShop.sol, 67 forPoolManager.t.sol- Guarantee full rebuild when reindex races with a running didSave worker (#178)
- Tree-sitter import/assembly string completion guards (#177)
- Use
project_cache_key()in executeCommand and wake reindex worker correctly (#176)
Shop.sol: 26/26 methods, references 11 results, rename 11 edits, codeAction workingPoolManager.t.sol: 26/26 methods, references 67 results, 1082 inlay hints at 9.3ms
- Drop v1 cache path; v2 cache is now the only supported mode
- Auto-create
.gitignorein the cache directory on first write - Cache fingerprint now includes
lsp_versionso upgrading the server binary automatically invalidates stale caches
- Pull settings via
workspace/configurationwheninitializationOptionsis absent — fixes Neovim which sends settings only via pull requests, not asinitializationOptions(#164) - Use import-closure for project index: BFS from source/test/script roots via forge remappings compiles only files the project actually imports (~510 vs 1788 for v4-core), cross-file
referencedDeclarationIDs now populated correctly (#165) - Resolve solc version from transitive import pragmas: intersects pragma constraints across the full import graph so a wildcard pragma file importing an exact-version file picks a compatible solc, fixing empty ASTs and broken goto-definition (#162)
- Use tree-sitter for import parsing in pragma graph walk, correctly handling multi-line import directives
- Correct
textDocument/renamefor aliased imports (import {A as B}andimport './X.sol' as Y) (#167) - Fix config profile fallback and lint parsing
- Incremental reindex safety with threshold gating and scoped cache merge; warm-load reconciles changed files and persists cache aggressively (#156)
- Add
--stdioto Neovimcmdsnippets inDOCS.md - Add
projectIndexsettings to all editor config examples - Add missing
willCreate/didCreate/willDelete/didDeletecapabilities toneovim-plugin-lsp.lua - Fix
CONTRIBUTING.md: correct fixture name, test count (600), and project structure - Fix stale benchmark config references across profiling and completions docs
projectIndex.fullProjectScansetting to enable full project source indexing at startup (#152)- Persistent reference cache with atomic writes and single-flight sync (#153)
- Auto-create
.gitignorein cache directory (#154) - Incremental reindex safety with threshold gating and scoped cache merge (#156)
- Drop v1 cache path; v2 cache is now the only supported mode
- Pull settings via
workspace/configurationwheninitializationOptionsis absent (#164) - Resolve solc version from transitive import pragmas (#162)
- Use import-closure for project index with full cross-file references (#165)
- Correct
textDocument/renamefor aliased imports (import {A as B}andimport './X.sol' as Y) (#167)
- 600 total tests, 0 warnings
- File operation behaviors are configurable via:
fileOperations.templateOnCreatefileOperations.updateImportsOnRenamefileOperations.updateImportsOnDelete
- Default file-operation settings are enabled.
- Template/scaffolding naming standardized to
templateOnCreate.
- Improve file creation scaffolding flow to avoid missing scaffold content on new files.
- Fix duplicate/incorrect scaffold insertion timing during create-file lifecycle.
- Improve auto-import completion behavior for top-level symbols and import edit attachment.
- Added/updated benchmark coverage for file-operation lifecycle flows (
willCreateFiles,willRenameFiles,willDeleteFiles) and auto-import scenarios.
- Replace clone-then-strip with build-filtered-map in
walk_and_extract()(#132)build_filtered_decl()/build_filtered_contract()iterate borrowed node fields and only clone fields that pass the STRIP_FIELDS filter, skipping heavy subtrees (body,modifiers,value,overrides, etc.)- Eliminates 234 MB of transient allocations (629→395 MB total, -37%)
- RSS: 310 MB → 254 MB (down from 394 MB pre-optimization)
- Pre-size HashMaps with
with_capacity()incache_ids(),extract_decl_nodes(),build_completion_cache(),build_hint_index(),build_constructor_index()(#132) - Remove dead
goto_references()andgoto_references_with_index()functions (#132) - Gate
SolcOutput/SourceEntrybehind#[cfg(test)](#132)
- Fix cross-file references contamination: use
nameLocationinstead ofsrcinresolve_target_location()so "Find All References" onIPoolManager managerreturns references tomanager, not to theIPoolManagerinterface (#131) - Fix non-deterministic hover on inherited contracts:
byte_to_id()now prefers nodes withreferencedDeclarationwhen two nodes share the same span length (#131)
| State | RSS | vs v0.1.24 |
|---|---|---|
| v0.1.24 baseline | 230 MB | — |
| Before optimization | 394 MB | +164 MB |
| v0.1.25 | 254 MB | +24 MB |
DHAT profiling (poolmanager-t-full.json, 95 files):
| Metric | v0.1.24 | v0.1.25 | Delta |
|---|---|---|---|
| Total allocated | 629 MB | 395 MB | -37% |
| Peak (t-gmax) | 277 MB | 243 MB | -12% |
| Retained (t-end) | 60 MB | 60 MB | unchanged |
- 458 total tests, 0 warnings
Updated for Shop.sol (all competitors), Pool.sol (v0.1.25 vs v0.1.24), and PoolManager.t.sol (v0.1.25 vs v0.1.24).
- Project-wide source indexing for cross-file references (#115, #119)
- Semantic tokens range and delta support
- LSP settings configuration (#112)
- Benchmark configs with server registry and didChange snapshots (#121)
textDocument/documentLinkreturns only import links, not every identifier (#122)- Drop optimizer and conditionally exclude gasEstimates from solc input (#117)
- Handle
{value: ...}/{gas: ...}modifier calls in inlay hints and signature help (#125, #116) - Correct signatureHelp cursor positions to inside function call parens
- Remap all tests from forge to solc fixture (#123)
- CI: checkout submodules so fixture-based tests can find v4-core
- 466 total tests, 0 warnings
| Method | p95 |
|---|---|
| initialize | 12.9ms |
| completion | 0.4ms |
| hover | 23.1ms |
| definition | 11.1ms |
| references | 19.4ms |
| rename | 21.2ms |
| inlayHint | 2.8ms |
| signatureHelp | 11.7ms |
| semanticTokens/full | 3.7ms |
Scorecard: 15/18 wins vs solc, nomicfoundation, juanfranblanco, qiuxiang
textDocument/signatureHelp— shows function signature and active parameter while typing (#110)- Opt-in gas estimates via
@custom:lsp-enable gas-estimatesNatSpec tag (#109)
- Use
svm-rsas a library for solc version management (#106, #105)svm::installed_versions()for discovering installed solc versionssvm::version_binary()for resolving solc binary pathssvm::install()for auto-installing missing versions (async, native)- No longer shells out to the
svmCLI or manually walks the filesystem
- Auto-detect solc version from
pragma solidityand resolve matching binary (#93, #95)- Parses pragma constraints: exact (
0.8.26), caret (^0.8.0), gte (>=0.8.0), range (>=0.6.2 <0.9.0) - Scans svm-rs and solc-select install directories for matching versions
- Auto-installs missing versions via
svm-rslibrary - Cross-platform support (macOS, Linux, Windows)
- Cached version list (scanned once per session)
- Parses pragma constraints: exact (
- Solc version resolution respects both pragma and foundry.toml (#103)
- Exact pragmas (
=0.7.6) always honoured — foundry.toml cannot override - Wildcard pragmas (
^0.8.0) use foundry.toml version if it satisfies the constraint - No pragma falls back to foundry.toml, then system solc
- Exact pragmas (
- Foundry config support for compiler settings (#103)
- Reads
via_ir,optimizer,optimizer_runs,evm_versionfromfoundry.toml - Passes settings to solc standard JSON (
viaIR,optimizer,evmVersion) - Reads
ignored_error_codesto suppress matching diagnostics - Fixes "Stack too deep" errors for projects requiring
via_ir(e.g. EkuboProtocol/evm-contracts)
- Reads
- Callsite parameter documentation on hover (#103)
- Hovering over arguments in function/event calls shows
@paramdoc from the called definition - Uses tree-sitter on the live buffer to find enclosing call and argument index
- Resolves via
HintIndex(exact offset or(name, arg_count)fallback) for param name anddecl_id - Looks up
@paramdoc fromDocIndexor raw NatSpec with@inheritdocresolution
- Hovering over arguments in function/event calls shows
- Gas estimates in hover, inlay hints, and code lens (#91, #94)
GasIndexbuilt from solc contract output (creation + external/internal costs)- Hover shows gas cost for functions and deploy cost for contracts
- Fire icon (🔥) with formatted numbers (e.g.
125,432)
- Use solc directly for AST + diagnostics, 11x faster on large projects (#90)
- Use solc userdoc/devdoc for hover documentation (#99)
DocIndexbuilt from solc contract output at cache time with pre-resolved@inheritdoc- Hover on parameters and return values shows
@param/@returndocs from parent function - Works at both declaration site and any usage inside the function body
- Structured rendering: notice,
@devdetails, params table, returns table - Typed selectors:
FuncSelector,EventSelector,Selectorenum,MethodIdnewtype - Replaces raw
Stringselectors throughout gas, hover, completion, and inlay hints
- Gas inlay hints use tree-sitter positions from the live buffer (#96)
- Fixes hints drifting to wrong positions during editing
- Function gas hints support opening/closing brace placement (
FN_GAS_HINT_POSITIONconstant) - Contract deploy hints show
codeDepositCostwhentotalCostis infinite - Libraries and interfaces now show deploy cost hints
- Remove code lens — gas info is covered by inlay hints and hover (#96)
- Improve natspec tag formatting in hover (#98)
@devnow renders with a bold**@dev**header above italic content@custom:tags and other unknown@tags render with bold label and italic content
- Bound
foundry.tomlsearch at git repo root (#89) - Hover works when file has compilation errors (#92)
- 423 total tests, 0 warnings
- Tree-sitter enhanced goto definition (#66, #79)
- Inlay hints v2 — tree-sitter positions + AST semantics (#61, #81)
- Respect
foundry.tomllint ignore andlint_on_buildconfig (#84, #87) - Introduce
NodeId/FileIdnewtypes and sharedSourceLocparser (#86)
- Goto definition returns wrong result after unsaved edits (#83)
- Bound
foundry.tomlsearch at git repo root (#89)
- Rewrite
textDocument/documentSymbolandworkspace/symbolto use tree-sitter instead of Forge AST (#77, #78)- Symbols no longer depend on
forge build— works on any Solidity file immediately documentSymbolreads from text_cache with disk fallbackworkspace/symbolscans open files only
- Symbols no longer depend on
- Clean up semantic tokens to avoid overriding tree-sitter highlights (#78)
- Remove modifiers, builtin types, pragmas, variables, and member expressions from LSP tokens
- Prevents
@lsp.typemod.*priority 126-127 from overriding tree-sitter colors
textDocument/documentSymbol3.2x faster (3.24ms → 1.02ms)workspace/symbol6.4x faster (6.08ms → 0.95ms)
- Add
textDocument/semanticTokens/fullvia tree-sitter (#75, #76)
The documentSymbol response returns hierarchical symbols with the following kind mappings:
| Solidity construct | LSP SymbolKind |
|---|---|
contract |
CLASS |
interface |
INTERFACE |
library |
NAMESPACE |
function |
FUNCTION |
constructor |
CONSTRUCTOR |
fallback / receive |
FUNCTION |
state variable |
FIELD |
event |
EVENT |
error |
EVENT |
modifier |
METHOD |
struct |
STRUCT |
struct member |
FIELD |
enum |
ENUM |
enum value |
ENUM_MEMBER |
using ... for |
PROPERTY |
type ... is ... |
TYPE_PARAMETER |
pragma |
STRING |
import |
MODULE |
Functions include a detail string with parameters and return types (e.g. (address to, uint256 amount) returns (bool)).
Contracts, structs, and enums are returned as parent symbols with their members nested as children. Top-level declarations (pragma, import, free functions, free structs/enums) appear at root level.
LSP semantic tokens in Neovim have higher priority (125-127) than tree-sitter highlights (100). When both emit tokens for the same range, LSP wins. This causes problems when @lsp.typemod.* groups fall back to the generic @lsp highlight with undesirable colors.
The approach taken here: only emit semantic tokens where the LSP adds value that tree-sitter cannot provide. Let tree-sitter handle syntax it already highlights well (builtins, variables, member access, pragmas, modifiers). The LSP focuses on declaration identifiers, parameters, type references, and call targets where semantic knowledge matters.
- Context-sensitive
type(X).completions (#70)- Typing
type(ContractName).now showscreationCode,runtimeCode,name,interfaceId - Typing
type(IntegerType).showsmin,max
- Typing
- Skip using-for completions on contract/library/interface names (#71, #72)
Lock.no longer returns Pool and SafeCast library functions fromusing Pool for *andusing SafeCast for *- Using-for completions now only appear when completing on a value of a matching type, not on a contract/library/interface name
- Fix SIMD chunk selection skipping past target column in
position_to_byte_offset(#73, #74)- The SIMD-accelerated position calculation introduced in #68 could pick a chunk boundary past the target column on long lines, returning the wrong byte offset
- Go-to-definition on
AlreadyUnlockedin PoolManager.sol resolved torevertWithin CustomRevert.sol instead of the correcterror AlreadyUnlocked()in IPoolManager.sol
- SIMD-accelerated position calculation via
lintspec-coreTextIndex (#68)position_to_byte_offsetandbyte_offset_to_positionnow use a single SIMD pass over 128-byte chunks instead of a full linear scan- Short linear walk (at most 128 bytes) from the nearest chunk to the exact position
- Rewrite position conversion to use
lintspec-corecompute_indicesandTextIndex(#64, #68) - Simplify conversion functions, use builtin traits and constructors
- Improved identifier validation for Solidity keywords
- Simplify diagnostic path matching to fix silent drop (#63)
- Scope-aware completion with inheritance resolution (#57)
- Completions are now filtered by the current scope (contract, function, block)
- Inherited members from parent contracts are resolved and included
- Replaces the previous
fast/fullcompletion mode split with a single unified engine
- Use relative path to filter out diagnostics (#55)
- Build diagnostic filtering now correctly matches files using relative paths
- Fixes cases where diagnostics from dependency files were incorrectly included
--completion-modeflag is deprecated (#59)- The
fast/fullsplit is no longer needed — scope-aware completions are always active
- The
- @libkakashi — chore: add Zed editor setup section in docs (#60)
- Refactor build module: simplify diagnostic filtering, extract path comparison helper
- Add Zed editor setup section in docs (#60)
- 272 total tests, 0 warnings
- AST cache now updates when build has warnings but no errors (#41)
- The
build_succeededcheck useddiagnostics.is_empty()which blocked cache updates for files with unused variables or other warnings - Changed to only block on
DiagnosticSeverity::ERROR, so warnings pass through
- The
- Cross-file rename reads from in-memory editor buffers instead of disk (#50)
rename_symbolacceptstext_buffersparameter reflecting unsaved editor state- No more disk writes behind the editor's back
- Full
WorkspaceEditreturned to client for all files (#50)- Previously split edits between client (current file) and server-side
fs::write(other files) - Now the complete edit set is returned to the client
- Previously split edits between client (current file) and server-side
nameLocationsindex fallback in references (#50)- Nodes without
nameLocationsarray now correctly fall through tonameLocationorsrc
- Nodes without
- Stale AST range correction during rename (#50)
find_identifier_on_linescans the current line to correct shifted column positions after unsaved edits
- All LSP handlers read source from
text_cacheinstead ofstd::fs::read(#50) - Respect
includeDeclarationintextDocument/references(#49) - Use cached AST for
workspace/symbolinstead of rebuilding (#46) - Clear caches on
did_closeto free memory (#45) - Encoding-aware UTF-16 position conversion (#39)
- Remove document version when publishing diagnostics (#40)
- Pass
--ignore-eip-3860and--ignored-error-codes 5574to forge build (#11)
- Announce full version string in LSP
initializeresponse (#51)- e.g.
0.1.15+commit.abc1234.macos.aarch64
- e.g.
- 10 new regression tests for bugs fixed in #41 and #50
tests/build.rs: warning-only builds succeed, error builds fail, empty diagnostics succeedtests/rename.rs: nameLocations fallback, text_buffers usage, cross-file WorkspaceEdit, stale AST correction, identifier extraction
- Solidity fixture files in
example/for rename tests (A.sol, B.sol, C.sol, Counter.sol) - 186 total tests, 0 warnings
- @beeb — fix: remove document version when publishing diagnostics (#40), filed issues #32, #33, #34, #35, #36, #37, #38, #41
rustfmt.tomlandcargo fmtacross codebase (#42)- Benchmark config updated with all implemented LSP methods
textDocument/definitionandtextDocument/declarationnow return proper range width instead of zero-width ranges (#30)goto_bytes()returns(file_path, byte_offset, length)— extracts the length field fromnameLocationorsrcgoto_declaration()computesendfrombyte_offset + length, so editors correctly highlight the target symbol- Previously
start == endin the returnedLocation, making it impossible for the editor to highlight the target
- 4 new goto range-length tests:
Hooks(len 5),Pool(len 4),SafeCast(len 8), Yul external reference (nonzero) - 168 total tests, 0 warnings
textDocument/documentLink— every reference in a file is a clickable link- Import paths link to the imported file (resolves
absolutePathfrom AST) - All nodes with
referencedDeclarationlink to their definition viaid_to_location - Uses pre-indexed
CachedBuild.nodes— no extra AST traversal at request time
--versionnow shows commit hash when installed from crates.io (reads.cargo_vcs_info.jsonas fallback)
- 11 document link tests (CI-safe, real fixture data from
pool-manager-ast.json) - 164 total tests, 0 warnings
- Cross-file "Find All References" — scans all cached AST builds to find usages across files that don't share a build scope
- Cross-file "Rename" — renames symbols across all cached builds, not just the current file's dependency tree
CachedBuildstruct — pre-computescache_ids()once per cache insert instead of N+1 times per request
cache_ids()no longer called at request time — all node indexing happens on file saveget_or_fetch_build()deduplicates cache-miss logic across goto, references, rename, hover, and document symbol handlers
- 12 new cross-file reference tests (CI-safe, hardcoded AST values from fixture)
- 153 total tests, 0 warnings
--version/-Vflag with full build metadata: version, commit hash, OS, architecture- GPG-signed release checksums for binary verification
CONTRIBUTING.mdwith project structure and development workflow
- Remove redundant timestamp from tracing log output
- Add
build.rsto embed git commit hash at compile time - Add
public-key.ascfor release signature verification - Updated README with CLI usage examples, all flags, and verification instructions
textDocument/hover— show Solidity signatures, NatSpec documentation, and selectors on hover- Signature generation for functions, contracts, structs, enums, errors, events, modifiers, variables, UDVTs
- NatSpec formatting:
@notice,@param,@return,@devrendered as structured markdown - Display
functionSelector,errorSelector,eventSelectorfrom AST in hover output @inheritdocresolution viafunctionSelectormatching between implementation and parent interface — correctly handles overloaded functions- 25 hover tests against Uniswap v4 PoolManager AST
- Full completion engine with chain resolution, using-for directives, and type casts (~1400 lines, 86 tests)
--completion-modeflag:fast(default) pre-built completions,fullper-request scope filtering- Dot-completion for structs, contracts, libraries, magic globals (
msg,block,tx,abi,type) - Chain completions through function return types, mappings, type casts
using-fordirective support (e.g.PoolKey.toId(),BalanceDelta.amount0())- Method identifier completions with 4-byte selectors and full signatures
- Keyword, global function, ether/time unit completions
- Arc-based zero-copy AST cache — eliminates 7MB+ deep clones per handler request
- Non-blocking completion cache — returns static completions immediately while cache builds in background
document_symbolusesast_cacheinstead of shelling out toforge aston every request- Removed blocking
log_messagefrom completion handler to fix cancel+re-trigger lag
- Yul
externalReferencessupport for goto-definition and find-references
- Yul externalReferences support for goto-definition and find-references
- Completion engine with chain resolution, using-for, and type casts
- Fix rename in tests
- Fix: ignore bytecode size warnings for all sol files
- Enable goto definition for import statement strings
- Handle ImportDirective nodes in goto definition
- Add absolute_path field to NodeInfo struct
- Fix: only update AST cache when build succeeds
- Fix: preserve AST cache on file changes to keep go-to-definition working during errors