Skip to content

ADFA-4747: Fix Add-import code action not appearing on unresolved references#1547

Merged
itsaky-adfa merged 10 commits into
stagefrom
feat/ADFA-4747
Jul 24, 2026
Merged

ADFA-4747: Fix Add-import code action not appearing on unresolved references#1547
itsaky-adfa merged 10 commits into
stagefrom
feat/ADFA-4747

Conversation

@itsaky-adfa

@itsaky-adfa itsaky-adfa commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Jira: ADFA-4747

What & why

The "Add import" code action (shipped in ADFA-3754) never appeared on an unresolved-reference diagnostic - dead since it shipped. KtSymbolIndex.findSymbolBySimpleName ended with Sequence.take(limit) and its only caller passed limit = 0, so take(0) returned empty and prepare() always went invisible.

  1. Fix the dead action - treat limit <= 0 as unbounded, honouring the ReadableIndex.query contract. Extract computeImportCandidates (non-suspend, runCatching, KtFile before project.read); drop the no-op CMD_FORMAT_CODE.
  2. No main-thread I/O in prepare() - decide from the in-memory unresolvedReference marker; index resolution moves entirely to the background execAction.
  3. Surface the empty case - "No imports found" (flashError) when a shown action resolves nothing.

Tests

:lsp:kotlin:testV7DebugUnitTest --tests AddImportActionTest 5/5 (incl. the take(0)-unbounded regression guard); EditExtsTest covers sorted-insert/dedup; :lsp:kotlin:assembleV8Debug builds.

Merge gate (manual QA)

On-device: "No imports found" on a typo; single-candidate auto-apply vs multi-candidate picker; no main-thread jank.

Follow-ups (out of scope)

  • insertImport does not dedup a concrete import already covered by a wildcard (no impact today).
  • Uniform long-press/tooltip help across the Kotlin code actions.

Supersedes the PR originally opened under ADFA-4610 (that subtask tracks the original feature, delivered via ADFA-3754); the same commits are re-attached here under the correctly-scoped bug ADFA-4747.

…nces

AddImportAction.prepare() gated visibility on findSymbolBySimpleName(name, 0),
but the helper ended with Sequence.take(limit) and take(0) returns empty, so
the action was invisible for every unresolved-reference diagnostic - dead since
ADFA-3754. Treat limit <= 0 as unbounded, honoring the documented
ReadableIndex.query contract the helper was violating.

Also extract hasImportableClassifier and a non-suspend computeImportCandidates
(runCatching, KtFile fetched before project.read) for testability and
crash-safety, and drop the no-op CMD_FORMAT_CODE. Adds AddImportActionTest
(visibility-gate regression) and EditExtsTest.
Replace the SQLite-backed visibility gate with an in-memory check on the
unresolved-reference marker. prepare() runs synchronously on the UI thread
during menu build, so resolving against the symbol index there was
main-thread disk I/O; that resolution now happens only in the background
execAction.
@itsaky-adfa itsaky-adfa self-assigned this Jul 17, 2026
@itsaky-adfa
itsaky-adfa requested a review from a team July 17, 2026 14:47

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 633f8c0a-2d5a-4e14-8cab-7cd28a5d2b86

📥 Commits

Reviewing files that changed from the base of the PR and between a4d900f and 9858bb3.

📒 Files selected for processing (1)
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/NullSafetyAction.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/NullSafetyAction.kt

📝 Walkthrough
  • Fixed Kotlin “Add import” code action for unresolved references by making action visibility and candidate resolution depend on the in-memory unresolved-reference diagnostic marker (rather than symbol-index lookups returning zero candidates).
  • Corrected Kotlin symbol lookup semantics: KtSymbolIndex.findSymbolBySimpleName now treats limit <= 0 as “unbounded,” aligning with the ReadableIndex.query contract.
  • Moved import-candidate computation from synchronous prepare() to background execAction() to avoid UI-thread work.
  • Improved user feedback: when the action is shown but no imports resolve, it now reports “No imports found.”
  • Refactored Kotlin diagnostic extra handling for code actions:
    • Added shared helpers in BaseKotlinCodeAction to extract typed KotlinDiagnosticExtra from selection diagnostics.
    • Updated KotlinDiagnosticExtra to a covariant generic type with safe casting via asAction().
    • Updated NullSafetyAction to use the shared diagnostic-extra retrieval flow.
  • Updated related Kotlin fix-imports UI/tooltip support by adding a dedicated tooltip tag constant and string resource.
  • Added/updated regression tests:
    • AddImportActionTest for import candidate resolution and the limit=0 unbounded behavior.
    • EditExtsTest for import insertion/formatting scenarios.

Risks / best-practice concerns

  • Action visibility is now coupled to the presence/freshness of the in-memory unresolved-reference diagnostic extra; if diagnostics aren’t produced or are stale, the “Add import” action may not appear as expected until diagnostics refresh.

Walkthrough

The Kotlin code-action flow now uses typed diagnostic extras for add-import and null-safety actions. Symbol lookup treats non-positive limits as unbounded, add-import failures display an error, tooltip metadata is specialized, and coverage expands for candidate resolution and import insertion.

Changes

Kotlin code-action resolution

Layer / File(s) Summary
Typed diagnostic action contract
lsp/kotlin/src/main/java/.../KotlinDiagnosticProvider.kt, lsp/kotlin/src/main/java/.../BaseKotlinCodeAction.kt, lsp/kotlin/src/main/java/.../NullSafetyAction.kt
Diagnostic extras carry typed actions, shared lookup helpers select matching diagnostics, and null-safety preparation and execution use the typed lookup flow.
Add-import resolution and feedback
lsp/kotlin/src/main/java/.../AddImportAction.kt, lsp/kotlin/src/main/java/.../KtSymbolIndex.kt, resources/src/main/res/values/strings.xml, idetooltips/src/main/java/.../TooltipTag.kt
Add-import availability uses ResolveReference metadata, symbol lookup supports unbounded limits, tooltip metadata is specialized, and empty results display “No imports found”.
Candidate and edit validation
lsp/kotlin/src/test/java/.../AddImportActionTest.kt, lsp/kotlin/src/test/java/.../EditExtsTest.kt
Tests cover classifier filtering, multiple candidates, unknown references, lookup limits, import ordering, duplicate imports, and package-only files.
Implement-members logging cleanup
lsp/kotlin/src/main/java/.../ImplementMembersAction.kt
The local logger factory import and companion logger declaration are removed while warning calls remain.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DiagnosticProvider
  participant AddImportAction
  participant SymbolIndex
  participant SourceFile
  DiagnosticProvider->>AddImportAction: provide ResolveReference extra
  AddImportAction->>SymbolIndex: find classifier candidates
  SymbolIndex-->>AddImportAction: return matching symbols
  AddImportAction->>SourceFile: compute import edits
  SourceFile-->>AddImportAction: return candidate edits
  AddImportAction->>AddImportAction: flash error when result is empty
Loading

Possibly related PRs

Suggested reviewers: jatezzz

Poem

A rabbit made imports hop in line,
Typed clues now guide them fine.
Classes bloom from indexed ground,
Neat edits gather all around.
When none are found, a message appears—
“No imports found,” says Bunny with cheers!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main fix: the Add import action not showing for unresolved references.
Description check ✅ Passed The description matches the PR changes and explains the fix, tests, and follow-up details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ADFA-4747

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Signed-off-by: Akash Yadav <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportActionTest.kt (1)

49-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the changed action lifecycle paths.

This suite tests candidate computation but not the changed prepare() visibility branches or postExec()’s msg_no_imports_found path. Add focused tests for valid/invalid diagnostic metadata and empty execution results.

As per coding guidelines, changed non-UI code should cover error and edge paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportActionTest.kt`
around lines 49 - 107, Add focused tests for AddImportAction.prepare() covering
both valid diagnostic metadata and invalid or missing metadata visibility
branches, then add a postExec() test that exercises an empty execution result
and verifies the msg_no_imports_found path. Reuse the existing test environment
and action setup, keeping the tests narrowly scoped to these changed lifecycle
behaviors.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportActionTest.kt`:
- Around line 11-13: Migrate both AddImportActionTest and EditExtsTest from
JUnit 4 to JUnit Jupiter by replacing their test imports and converting all
assertions to Truth assertions; apply the corresponding changes in
lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportActionTest.kt
lines 11-13 and
lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/utils/EditExtsTest.kt
lines 6-8.

---

Nitpick comments:
In
`@lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportActionTest.kt`:
- Around line 49-107: Add focused tests for AddImportAction.prepare() covering
both valid diagnostic metadata and invalid or missing metadata visibility
branches, then add a postExec() test that exercises an empty execution result
and verifies the msg_no_imports_found path. Reuse the existing test environment
and action setup, keeping the tests narrowly scoped to these changed lifecycle
behaviors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 69d65373-72a2-44b1-8dc0-135ce64243ef

📥 Commits

Reviewing files that changed from the base of the PR and between aade24f and db93af5.

📒 Files selected for processing (5)
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportAction.kt
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/index/KtSymbolIndex.kt
  • lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportActionTest.kt
  • lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/utils/EditExtsTest.kt
  • resources/src/main/res/values/strings.xml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/NullSafetyAction.kt`:
- Around line 49-53: Update NullSafetyAction.execAction so cancellation is never
converted into an empty result: preserve the existing runCatching recovery for
specific recoverable failures, but rethrow any CancellationException caught
within it before returning the fallback list.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 984f8f54-427c-4b19-ab67-d5a4f540323b

📥 Commits

Reviewing files that changed from the base of the PR and between db93af5 and a80541d.

📒 Files selected for processing (6)
  • idetooltips/src/main/java/com/itsaky/androidide/idetooltips/TooltipTag.kt
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportAction.kt
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/BaseKotlinCodeAction.kt
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/ImplementMembersAction.kt
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/NullSafetyAction.kt
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/diagnostic/KotlinDiagnosticProvider.kt
💤 Files with no reviewable changes (1)
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/ImplementMembersAction.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/actions/AddImportAction.kt

@itsaky-adfa
itsaky-adfa merged commit b0e63b0 into stage Jul 24, 2026
4 checks passed
@itsaky-adfa
itsaky-adfa deleted the feat/ADFA-4747 branch July 24, 2026 14:41
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.

2 participants