Skip to content

Feature/viewbinding support (#11) - #226

Draft
hikkidev wants to merge 1 commit into
Hessesian:mainfrom
hikkidev:feature/viewbinding
Draft

hikkidev wants to merge 1 commit into
Hessesian:mainfrom
hikkidev:feature/viewbinding

Conversation

@hikkidev

Copy link
Copy Markdown
Contributor

Part of #201

* Feature/viewbinding navigation (#10)

* feat: add layout XML indexing for ViewBinding navigation (PR 1)

Introduce the layout side index: tree-sitter-xml parsing of res/layout*
files, module-root derivation, workspace scan integration, file-watcher
routing, and disk-cache persistence (CACHE_VERSION 30).

Includes the ViewBinding design doc and implementation plan. No
user-visible LSP navigation yet — that lands in PR 4.

* Index generated ViewBinding classes so layout side-index entries can pair with their binding Java sources for upcoming navigation.

* feat: add server-side databinding poll watcher for ViewBinding freshness (PR 3)

Poll gitignored build/ directories for generated *Binding.java files so binding
index updates and open-file diagnostics self-clear after a Gradle build.

* Add ViewBinding navigation remap for definition and implementation (PR 4).

Wire post-resolution remapping from generated *Binding.java to layout XML,
binding-type implementation to raw Java, XML-side @+id/ tag navigation, and
layout document indexing on open/save/change.

* Document ViewBinding navigation PR 4 implementation plan.

Captures scope, dependencies on PRs 1–3, and design for definition and
implementation remapping now landed in 5a477ac.

* Add ViewBinding hover, references, and diagnostics (PRs 5–6).

Complete the ViewBinding navigation feature with Kotlin-style field hover,
receiver-verified references from Kotlin and XML, and import/staleness
diagnostics that self-clear after a build.

* Fix ViewBinding false positives and wrong-module answers by verifying this/it receivers, scoping stale-build diagnostics and field hover to the source module, and pruning deleted generated bindings on reindex.

* Close ViewBinding v1 review gaps with acceptance tests and receiver fixes.

The design doc now reflects shipped v1 behavior; tests cover the remaining
normative cases (import-line definition, bare scope receivers, chained
includes, watcher self-clear, layout watch routing) so regressions in
multi-module and post-build workflows are caught early.

* Fix four ViewBinding review findings: local shadowing, watcher hand-off, XML UTF-16 columns, and bare-field staleness.

Bare implicit-`this` member access (`with(binding) { title }`) now yields to a
nearer local val/var/parameter/lambda param, so a shadowing local is never
mistaken for a binding field. Navigation, reference verification, and the
staleness diagnostic all consult a shared CST scope walk
(`name_shadowed_by_local_declaration`) bounded by the enclosing function and
type bodies.

Installing the real databinding watcher handle now re-registers module roots
that ran discovery against the earlier noop handle, so modules discovered
during the initialize-time scan are actually polled once `initialized`
installs the watcher instead of being silently dropped.

Layout XML cursor lookups convert the LSP UTF-16 column to a byte offset before
building the tree-sitter Point (as the Kotlin paths already do), so a layout
line with a multi-byte character before the cursor resolves the correct node.

Stale-build diagnostics now cover bare receiver-scope members in addition to
qualified `binding.field`, matching the navigation support, while skipping
declaration names and shadowed locals to avoid false positives.

Each fix carries a regression test.

* Wire Zed extension for ViewBinding XML navigation.

Declare XML on kmp-lsp and document branch install, XML extension prerequisite, and settings so layout-to-Kotlin navigation works in Zed.

* Fix ViewBinding cold-start navigation to layout XML

On-demand module layout indexing closes the gap when bulk discovery
misses res/ files. Definition remap keeps generated Java as fallback
instead of returning nothing. Binding-field definition uses the same
direct layout lookup as references/hover. Layout XML references no
longer fall through to project-wide text search.

Adds viewbinding-prefixed info logs for Zed server-log debugging.

* Fix receiver-scoped ViewBinding resolution with scope-aware variable typing

File-global `binding:` lookup returned the wrong *Binding class when multiple
competing declarations existed in one class (property vs method params).
Receiver-scoped access (`with`/`apply`/`let`, bare and it-qualified) then
navigated to the wrong layout or fell back to references.

Add CST scope walk (function params → locals → class members) via
variable_type_at/find_var_type_at, thread cursor position through lambda
receiver inference, and harden resolve_expected_binding_class for it/this
qualifiers and bare field fallback.

* Fix ViewBinding navigation for chained receivers in scope-function lambdas.

Resolve dotted with/apply arguments and prefer import-scoped field lookup so holder.binding resolves to the correct *Binding class instead of falling back to references.

* Fix inherited generic property type inference for ViewBinding navigation.

Walk supertypes with generic argument substitution so `binding` in a
subclass of `ViewBindingAdapter<T>` resolves to the concrete binding type.

* fix(completion): offer implicit receiver members in bare completion

Inside with/apply/run lambdas, bare identifier completion now includes members of the inferred receiver type, matching explicit this. completion behavior.

* Resolve ViewBinding field types from layout XML first.

Add binding_field_type helper with module-scoped layout lookup, variant
consensus, include chains, and Java fallback; hook find_field_type_in_class
so dotted chains like binding.header.title infer from XML.

* Infer ViewBinding type from viewBinding property delegates.

Recognize by viewBinding<FooBinding>() and FooBinding::inflate/bind
delegate forms in line-scan inference so binding variables get the
correct *Binding type without an explicit annotation.

* Add layout-derived fields to ViewBinding dot-completion.

Prepend binding layout fields in complete_dot_expr with XML-first detail
and deduplicate against indexed Java members so binding. works without a
build while stale generated sources cannot override layout names.

* Support bare ViewBinding field access in with/apply scopes.

Resolve implicit binding-field types via find_this_context_in_lines and
layout XML inference so myView. works inside with(binding), bare lists
suggest layout fields, and local declarations keep shadowing priority.

* Add end-to-end regression coverage for ViewBinding field-access navigation; fix clippy dead-code

Investigated the reported "binding.myView go-to-definition broke" regression
against e6556c0/2ca5b8c/92debf7/89b1fe1 by tracing every navigation-relevant
function (find_binding_field_definition, resolve_expected_binding_class,
binding_class_for_receiver_chain, infer_receiver_type_at) and exercising it
against ~20 realistic and edge-case ViewBinding usage patterns (function
params, lateinit properties, property delegates, Fragment nullable-getter
bindings, chained/nested includes, with/apply/also/let scopes, cold-start
and gitignored layouts, anonymous inner classes) on both this branch and the
commit before the 4 patches. All scenarios resolve identically on both
revisions and the full 1546-test suite is green, so no functional regression
in binding-field-to-XML navigation was reproducible.

The literal reported scenario (go-to-definition on a `binding.field` access,
as opposed to the type annotation or hover, which were already covered) had
no end-to-end LSP-level test, so add one covering the common lateinit-property-
assigned-in-onCreate pattern to lock in current behavior and catch true
regressions going forward.

Also fix a `cargo clippy -- -D warnings` failure already present on the
branch: an unused `module_root` field on the new BindingFieldTypeFixture.

* Suppress diagnostics for all XML files and stop indexing them as Kotlin.

Route layout XML through the side index only, publish empty publishDiagnostics for every .xml file, and add regression tests for manifest and layout paths.

* fix(watcher): never cache empty databinding dirs

Empty discovery from a pre-build poll was cached permanently via
or_insert_with, so the first-build self-clear path never ran. Re-discover
when the cached list is empty and build/ now exists; only cache non-empty
results. Add a deterministic unit test for the rediscovery path.

* fix(viewbinding): gate references on generated Java field existence

Split layout vs generated-Java field checks so stale fields (removed from
XML but still in Java) keep receiver-verified references instead of falling
through to unverified text search. Add regression test for that case.

* fix(live_tree): replace thread-local RequestParseCacheGuard with explicit per-request cache

Thread-local parse memoization crossed await points under tokio work-stealing,
leaking trees across requests. Pass an explicit RequestParseCache from LSP
reference handlers through binding-field verification.

* fix(layout): finish secondary layout index

Populate layouts_by_module_and_name on cache-restore hits, deduplicate URIs
on re-index, and route layouts_for_binding_class through matching_layout_entries.
Add warm-restore and dedup regression tests.

* fix(layout): retry ensure_module_layouts_indexed when walk found nothing

Do not insert into layouts_indexed_modules before the walk; when zero layout
files exist on disk, leave the module unmarked so on-demand indexing can retry
after layouts appear.

* fix(scope): extend local shadow detection for control-flow bindings

Detect for-loop variables, catch parameters, when subjects, and
destructuring declarations when deciding whether a bare name shadows an
implicit receiver member. Add for/catch regression tests.

* fix(cursor): verify bare member exists before implicit-this qualifier

Only treat a bare lowercase identifier as implicit-this when the enclosing
receiver type actually declares that member, avoiding false binding-field
resolution for unrelated locals in receiver lambdas.

* chore(viewbinding): layout completion and delegate inference refinements

Skip Fragment tags in layout completion fields, map merge roots to View,
tighten viewBinding delegate word-boundary matching, and add nested-src
module-root derivation test.

* fix(viewbinding): store layout and diagnostic ranges as UTF-16 columns

tree-sitter reports byte columns; LSP expects UTF-16. Convert at index
time with ts_byte_col_to_utf16 and add multi-byte-prefix regression tests.

* fix(viewbinding): stop field-remap miss from falling back to class layout

Resolve binding constructors to layout XML, but when a generated field has
no @+id target pass the Java location through instead of remapping to the
class header.

* fix(watcher): register modules on cache restore; scan intermediates

Call watch_module when restoring generated bindings from disk cache, and
align databinding dir discovery with binding discovery by not pruning
build/intermediates.

* fix(watcher): compare binding metadata with nanos and file size

Extend GeneratedBindingEntry with sub-second mtime and size, use them in
poll baseline comparison, and treat an empty on-disk snapshot against a
populated index as a change (Gradle clean).

* fix(scope): unify local shadow and type walks; drop file-global fallback

Share one CST scope walk for shadow detection and variable type lookup,
including whole-function forward shadowing and function parameters. Remove
variable_type_at's infer_variable_type_raw fallback that picked competing
declarations from other files.

* fix(viewbinding): normalize rg reference columns to UTF-16 at ingestion

Convert ripgrep byte columns to UTF-16 when ingesting binding-field
reference candidates, then probe the CST with a single coordinate path.

* fix(viewbinding): resolve aliased databinding imports by full path

Import diagnostics derive the binding class from import.full_path so
aliased imports still pair with the correct layout and generated class.

* fix(viewbinding): prefer exact view-id matches over normalized fallback

Match @+id values exactly first; use camelCase/snake_case normalization
only when it yields a single unambiguous layout candidate.

* fix(scope): infer viewBinding delegate types from CST first

Resolve `by viewBinding<…>()` / `::inflate` delegate types during the CST
scope walk, keeping line-scan inference as fallback in infer_lines.

* fix(viewbinding): warn on viewBindingIgnore only when all variants opt out

Partial variant opt-out no longer emits the ignore diagnostic; the
build-required warning still applies when any variant remains active.

* perf(viewbinding): index binding classes by name for O(1) lookup

Add a reverse index from class name to module location and package so
hover and import pairing no longer scan every module's generated_bindings.

* perf(viewbinding): serve layout XML positions from side index

Stop re-parsing layout XML on definition, implementation, and references
requests; resolve view ids, tag names, and declaration positions from
LayoutFileData with indexed UTF-16 ranges.

* perf(viewbinding): scope binding field references to import graph

Narrow rg candidate files to workspace importers of the binding class FQN
instead of project-wide search, and centralize binding file URI resolution
on the indexer for reuse by hover and references.

* perf(viewbinding): target binding discovery and parallelize watcher

Pass known databinding directories into index_generated_bindings so
reindex walks only generated output trees, and snapshot registered
modules concurrently before batching reindex work.

* perf(live_tree): share request parse cache across LSP scope queries

Route on-demand parses through RequestParseCache at hover, definition,
implementation, and references entry points so shadow checks and binding
receiver verification reuse trees within one request.

* refactor(viewbinding): extract binding_receiver module

Single receiver-resolution path for cursor, references, definition, and
diagnostics — implicit-this detection, shadow checks, and receiver-type
inference no longer drift across four copies.

* refactor(infer): gate ViewBinding heuristics behind is_view_binding_class_name

Replace bare ends_with("Binding") checks in generic inference, scope
initializer walks, completion, and binding_field_type with the explicit
AGP naming helper so non-ViewBinding *Binding classes stay agnostic.

* refactor(layout): move on-demand layout indexing behind background worker

Read paths enqueue layout enumeration instead of walking the filesystem
during LSP requests; tests call index_module_layouts_blocking directly.

* fix(watcher): cancel on shutdown, route discovery via worker, await republish

Poll loop respects cancellation, compares snapshots against the index
(not a stale local cache), routes reindex through the discovery worker's
dedup, and awaits diagnostic republish instead of try_send.

* test(scope): add variable_type_at and shadowing precedence matrix

Direct unit coverage for local-over-member, param-over-member, lambda
shadowing, inner-lambda shadowing, and implicit-receiver non-shadow cases.

* perf(references): thread request parse cache through scope resolution

find_references_with_qualifier_cached shares the per-request parse cache
with enclosing_class_at_with_cache; handlers wire it for generic references.

* docs(viewbinding): document Java fallback on definition remap miss

Clarify that unresolved symbols stay silently empty while successful
resolution to generated Java keeps the Java location when XML remap fails.
Field misses must not fall back to the class layout header.

* perf(completion): thread request parse cache through scope analysis

Share one RequestParseCache across enclosing-class lookup, lambda param
detection, and CST scope walks during completion cache misses.

* test(viewbinding): lock in URLBinding and uRL name mapping roundtrips

Document the underscore-based acronym cases AGP uses today; full Inflector
parity for consecutive-capital ids remains deferred.

* Extract ViewBindingState and move indexer-side ViewBinding files.

Consolidate layout, discovery, and field_type modules under src/viewbinding/
with a dedicated ViewBindingState struct on Indexer, preserving reset semantics
and updating all import paths.

* Split ViewBindingIndex trait out of IndexRead.

Move the seven layout/binding query methods into viewbinding::index with
default stubs and Indexer/Arc impls; navigation and WorkspaceRead now use
IndexRead + ViewBindingIndex.

* Move ViewBinding navigation, receiver, diagnostics, and watcher into module.

Relocate feature/backend ViewBinding files under src/viewbinding/, extract
hover helpers, and update all call sites to the consolidated module surface.

* Extract ViewBinding inference heuristics into viewbinding::inference.

Move delegate/inflate/field-type heuristics out of scope, infer_lines, infer,
and complete into the viewbinding module with one-line call sites at origins.

* Finish ViewBinding module isolation and update documentation.

Clean up module re-exports, fix clippy warnings, and update
viewbinding-navigation.md to reflect the consolidated src/viewbinding/ layout.

* Added location-aware hop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant