Inlay Hints
What this page covers
This page documents the current inlay-hint implementation:
- parameter hints for calls and emits,
- how live-buffer positions are kept accurate,
- how settings filter hint kinds at request time.
Terms used in this page
HintIndex:HashMap<abs_path, HintLookup>prebuilt from AST at build-cache creation time.HintLookup: public struct with two private lookup maps and two public methods (resolve_callsite_with_skip,resolve_callsite_param). Callers use the methods, not the maps directly.by_offset(private): exactcall_start_byte → CallSitemap (best when AST offsets are fresh).by_name(private): fallback(name, arg_count) → CallSitemap (works when offsets drift).
CallSite: private struct with three fields:info: ParamInfo,name: String,decl_id: u64.ParamInfo: private struct nested insideCallSite— holdsnames: Vec<String>(parameter names) andskip: usize.ResolvedCallSite: public struct returned byresolve_callsite_param— holdsparam_name: Stringanddecl_id: u64.skip: number of leading params to skip for hint labels (1 forusing-forlibrary calls, 0 otherwise).ConstructorIndex:HashMap<u64, ConstructorInfo>— built as an intermediate fromdecl_indexand passed tobuild_hint_index(). Not stored onCachedBuild; discarded after the hint index is built.
Why this design exists
Parameter names come from compiler AST semantics, but cursor/argument positions must follow live edits in the editor.
A single source cannot solve both well.
So the implementation splits responsibilities:
- AST snapshot (
HintIndex) for semantic mapping, - tree-sitter on live buffer for real-time argument positions.
Runtime flow
In src/lsp.rs::inlay_hint:
- Read source bytes for the requested URI.
- Load the cached build snapshot.
- Generate raw hints.
- Filter parameter hints (
InlayHintKind::PARAMETER) based on settings. - Return hints, or
Noneif empty.
Inside inlay_hints(...):
How callsite mapping is built
CachedBuild::new() in goto.rs builds the hint index in two steps:
build_constructor_index(&decl_index)— scansdecl_indexfor contract declarations with constructors and buildsConstructorIndex(HashMap<contract_id, ConstructorInfo>). This is a temporary; it is not stored onCachedBuild.build_hint_index(sources, &decl_index, &constructor_index)— for each source file:- Walk call-like AST nodes (
FunctionCallandEmitStatement). - Resolve declaration via
referencedDeclaration. - Extract parameter metadata from typed declarations (
decl_index). - For
new ContractName(args)expressions, look up constructor info fromconstructor_index. - Store both exact-offset (
by_offset) and name/arity fallback (by_name) entries inHintLookup.
- Walk call-like AST nodes (
ConstructorIndex is discarded after build_hint_index returns. The final HintIndex is stored on CachedBuild.hint_index.
This is why request-time hinting is mostly lookup work, not full AST recomputation.
Parameter hint behavior
Hints are emitted for:
- normal function calls,
- member calls,
- emit statements,
- constructor-style
new Contract(args)when constructor info exists.
Special cases handled:
- using-for calls:
skip = 1when receiver is implicit and arg count is smaller than param count. - named-arg struct constructors: skipped (names are already visible at call site).
- stale offsets: fallback to
(name, arg_count)map.
Refresh behavior
Inlay hint refresh is triggered asynchronously (tokio::spawn) in two places:
- after successful build/update in
on_change, - after
did_change_configuration.
This avoids blocking request/diagnostic flow while still asking the client to re-request hints.
Known tradeoffs
- Exact offset matching can drift after edits/formatting; fallback improves resilience but can be less precise for overloaded same-name/same-arity cases.
- Request-time accuracy depends on
HintIndexfreshness from the latest successful cached build. - Filtering happens in
lsp.rs, soinlay_hints(...)may generate more hints than ultimately returned.
Test coverage and confidence
src/inlay_hints.rs includes strong helper-level coverage:
- tree-sitter call/event/name extraction,
- call argument indexing and byte-position mapping,
newexpression handling,resolve_callsite_parambehavior (including skip and bounds).
This gives good confidence in the core extraction and lookup mechanics.
Recommended explicit additions
Useful request-level additions:
- end-to-end
textDocument/inlayHinttests that validate settings filtering by kind, - stale-offset overload scenario test through request path,
- configuration-change refresh behavior test (ensuring client refresh is triggered).