Conversation
* 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #201