Skip to content

ADFA-4922: Disable text action in search - #1605

Closed
dara-abijo-adfa wants to merge 11 commits into
stagefrom
ADFA-4922-disable-text-action-in-search
Closed

ADFA-4922: Disable text action in search#1605
dara-abijo-adfa wants to merge 11 commits into
stagefrom
ADFA-4922-disable-text-action-in-search

Conversation

@dara-abijo-adfa

Copy link
Copy Markdown
Contributor

Disable text actions (Select, all, Copy, Help) when highlighting search matches. Implemented for file searches (Find in file) and log searches.

@dara-abijo-adfa dara-abijo-adfa self-assigned this Jul 31, 2026

@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.

@dara-abijo-adfa
dara-abijo-adfa changed the base branch from stage to bug/ADFA-4854-filter-bar-invisible July 31, 2026 09:22
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough
  • Disable Select All, Copy, and Help actions while users highlight search matches in file and log searches.
  • Track source content separately from filtered empty state.
  • Disable output search and filter actions when the source is empty.
  • Show no-match messages for filtered logs and completed editor searches.
  • Prevent stale output renders after restored or delayed updates.
  • Hide the keyboard when search or filter controls close.
  • Add localized messages and tests for empty states, no-match transitions, and buffer state.
  • Risk: The expanded state synchronization and delayed-render handling increase lifecycle complexity.
  • Risk: Search and filter actions depend on accurate source-empty tracking across view recreation.

Walkthrough

The change separates source emptiness from filtered layout emptiness. Output views now preserve filter state, prevent stale renders, and report no-match results. Editor actions observe source content and suppress menus during search.

Changes

Empty-state and interaction feedback

Layer / File(s) Summary
Source emptiness state contracts
app/src/main/java/com/itsaky/androidide/fragments/EmptyStateFragment.kt, app/src/main/java/com/itsaky/androidide/viewmodel/*, app/src/main/java/com/itsaky/androidide/logs/LogBuffer.kt, app/src/test/java/com/itsaky/androidide/{fragments,viewmodel,logs}/*
Source emptiness and layout emptiness now use separate state values. Buffer and ViewModel tests cover state transitions. Long-press coordinates now apply editor selection before the default action.
Filter-aware output rendering
app/src/main/java/com/itsaky/androidide/fragments/output/*, common/src/main/java/com/itsaky/androidide/utils/KeyboardUtils.kt, resources/src/main/res/values/strings.xml, app/src/test/java/com/itsaky/androidide/fragments/output/FilterNoMatchTrackerTest.kt
Build and log output recompute empty-state presentation from source content and filter activity. Restored and deferred renders use content generations. No-match notifications use transition tracking and localized messages.
Content-based editor actions
app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
The bottom sheet observes source-empty state and gates search, filter, share, and clear actions based on content and active sharing.
Search interaction feedback
editor/src/main/java/com/itsaky/androidide/editor/ui/EditorActionsMenu.kt, editor/src/main/java/com/itsaky/androidide/editor/ui/EditorSearchLayout.kt, editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt
Search state now suppresses the editor action menu. Search close hides the keyboard. Completed searches with no matches display an informational message.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: itsaky-adfa, daniel-adfa, jomen-adfa

Poem

A rabbit checks the source-flow bright,
Filters hide the lines from sight.
Stale renders hop away,
No-match notes now gently say,
“Search is clear”—then naps till night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly identifies the main change: disabling text actions during search.
Description check ✅ Passed The description accurately summarizes disabling text actions for file and log searches.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ADFA-4922-disable-text-action-in-search

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.

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/itsaky/androidide/fragments/output/LogFilterBarController.kt (1)

64-80: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Notify onVisibilityChanged for the initial shown state too.

toggle() and hide() notify onVisibilityChanged, but init does not notify it when the bar first becomes visible after stub.inflate(). The filter bar layout has no explicit android:visibility="gone", so createFilterBar() in both fragments updates isFilterActive without triggering updateEmptyState, leaving the empty-state UI stale until a later toggle or filter change runs it.

Invoke onVisibilityChanged?.invoke(binding.root.isVisible) once after the filter bar state finishes initializing in init.

🤖 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
`@app/src/main/java/com/itsaky/androidide/fragments/output/LogFilterBarController.kt`
around lines 64 - 80, Update LogFilterBarController.init to invoke
onVisibilityChanged with binding.root.isVisible once after all filter bar
initialization and listeners are configured, ensuring the initial visible state
is propagated without changing toggle() or hide() behavior.
🧹 Nitpick comments (5)
editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt (2)

97-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document and test the new no-match contract.

isSearchCompleteWithNoMatches() is a new public API. Add KDoc that defines the valid-query, valid-result, and empty-result conditions. Add tests for no query, invalid results, empty results, and non-empty results.

As per coding guidelines, public functions and non-obvious logic must have KDoc, and changed non-UI logic needs unit coverage.

Proposed KDoc
+	/**
+	 * Returns true when the current non-empty query has valid results and no matches.
+	 */
 	fun isSearchCompleteWithNoMatches(): Boolean {
🤖 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 `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`
around lines 97 - 101, The new public method is missing documentation and unit
coverage for its no-match contract. Add KDoc to
IDEEditorSearcher.isSearchCompleteWithNoMatches() describing that it returns
true only when a query exists, the search result is valid, and the result set is
empty; add tests covering no query, invalid results, empty results, and
non-empty results.

Source: Coding guidelines


35-40: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Narrow the reflection catch.

IDEEditorSearcher.getEditor() uses reflection, so catch the reflection failures you can meaningfully translate, such as NoSuchFieldException, IllegalAccessException, IllegalArgumentException, and ClassCastException. Catching Throwable also traps fatal Error values and unrelated failures like the assertion-style failures in IDEEditorSearcher; let those preserve their exact shape.

🤖 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 `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`
around lines 35 - 40, Update IDEEditorSearcher.getEditor() to catch only the
reflection and cast failures it can translate: NoSuchFieldException,
IllegalAccessException, IllegalArgumentException, and ClassCastException. Keep
the existing RuntimeException wrapping for those failures, while allowing fatal
Error values and unrelated assertion-style failures to propagate unchanged.

Source: Learnings

app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt (1)

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

Document the setEmptyState contract.

Add KDoc that defines isEmpty as layout state and isSourceEmpty as source-data state. Callers can otherwise invert these values.

As per coding guidelines, “Public classes, functions, and non-obvious logic must have KDoc documenting contracts...”.

🤖 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
`@app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt`
around lines 49 - 54, Document the public setEmptyState function with KDoc,
explicitly defining isEmpty as the layout state and isSourceEmpty as the
source-data state. Describe both parameters so callers pass the values without
confusing or reversing their meanings.

Source: Coding guidelines

app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt (1)

20-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use JUnit Jupiter and Truth for the new tests.

  • app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt#L20-L22: replace JUnit 4 imports with JUnit Jupiter and Truth imports.
  • app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt#L23-L23: replace the added JUnit 4 assertion import.
  • app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt#L98-L108: migrate the new test to JUnit Jupiter and Truth. Convert the enclosing class if required.

The existing test dependencies include JUnit Jupiter and Truth, but these tests still use JUnit 4 imports/assertions instead of the required test framework.

🤖 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
`@app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt`
around lines 20 - 22, The new tests use JUnit 4 assertions instead of the
required JUnit Jupiter and Truth APIs. In
app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt
lines 20-22, replace the assertion imports with JUnit Jupiter and Truth imports;
in app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt line 23,
replace the added JUnit 4 assertion import; and in lines 98-108, migrate the new
test and enclosing class as needed to JUnit Jupiter and Truth while preserving
its assertions and behavior.

Source: Coding guidelines

app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt (1)

65-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated filter-active/no-match wiring. Both fragments independently declare a noMatchTracker = FilterNoMatchTracker() and an isFilterActive getter with the same "filter source is non-empty or bar is visible" shape; this duplication is new in this PR and the two copies can drift.

  • app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt#L65-L69: extract noMatchTracker and the isFilterActive pattern (parameterized by buildOutputViewModel.filterText.value.isNotEmpty()) into a shared helper.
  • app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt#L148-L152: reuse the same shared helper, parameterized by viewModel.filter.value != LogFilter.NONE.
🤖 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
`@app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt`
around lines 65 - 69, Extract the duplicated no-match tracking and filter-active
logic into a shared helper, parameterized by the filter-source predicate and the
relevant filter-bar visibility. Update
app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
lines 65-69 to use it with buildOutputViewModel.filterText.value.isNotEmpty(),
and app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt
lines 148-152 to use it with viewModel.filter.value != LogFilter.NONE; both
sites should reuse the shared noMatchTracker and isFilterActive behavior.
🤖 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 `@app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt`:
- Around line 241-248: Update the share completion cleanup around shareJob so it
clears the shared state and calls updateActionButtonsEnabledState only when
shareJob still references the current coroutine job. Preserve the current-tab
re-read and empty-state calculation, but prevent an older cancelled share from
clearing or enabling actions for a newer share.

In `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`:
- Around line 48-58: Update IDEEditorSearcher.search and the
navigation/replacement overrides that call markSearching before superclass
validation to stop immediately for empty queries, and reset isSearching when the
superclass throws for invalid patterns. Keep the searching state aligned with
Sora validation while preserving normal successful search behavior.
- Around line 97-101: Update IDEEditorSearcher.isSearchCompleteWithNoMatches()
to rely on the current query’s published search result rather than
isResultValid() and potentially stale lastResults. Track or validate a
query/result generation through the result publication lifecycle, and only
report no matches after the current query’s result has been published; return
false while pending, cancelled, or associated with an older query.

In `@resources/src/main/res/values/strings.xml`:
- Line 689: Update the msg_no_filter_matches string resource to use
output-neutral wording, such as “No matching entries found.”, so it is
appropriate for both log and build-output panels.

---

Outside diff comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/output/LogFilterBarController.kt`:
- Around line 64-80: Update LogFilterBarController.init to invoke
onVisibilityChanged with binding.root.isVisible once after all filter bar
initialization and listeners are configured, ensuring the initial visible state
is propagated without changing toggle() or hide() behavior.

---

Nitpick comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt`:
- Around line 65-69: Extract the duplicated no-match tracking and filter-active
logic into a shared helper, parameterized by the filter-source predicate and the
relevant filter-bar visibility. Update
app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
lines 65-69 to use it with buildOutputViewModel.filterText.value.isNotEmpty(),
and app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt
lines 148-152 to use it with viewModel.filter.value != LogFilter.NONE; both
sites should reuse the shared noMatchTracker and isFilterActive behavior.

In
`@app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt`:
- Around line 49-54: Document the public setEmptyState function with KDoc,
explicitly defining isEmpty as the layout state and isSourceEmpty as the
source-data state. Describe both parameters so callers pass the values without
confusing or reversing their meanings.

In
`@app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt`:
- Around line 20-22: The new tests use JUnit 4 assertions instead of the
required JUnit Jupiter and Truth APIs. In
app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt
lines 20-22, replace the assertion imports with JUnit Jupiter and Truth imports;
in app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt line 23,
replace the added JUnit 4 assertion import; and in lines 98-108, migrate the new
test and enclosing class as needed to JUnit Jupiter and Truth while preserving
its assertions and behavior.

In `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`:
- Around line 97-101: The new public method is missing documentation and unit
coverage for its no-match contract. Add KDoc to
IDEEditorSearcher.isSearchCompleteWithNoMatches() describing that it returns
true only when a query exists, the search result is valid, and the result set is
empty; add tests covering no query, invalid results, empty results, and
non-empty results.
- Around line 35-40: Update IDEEditorSearcher.getEditor() to catch only the
reflection and cast failures it can translate: NoSuchFieldException,
IllegalAccessException, IllegalArgumentException, and ClassCastException. Keep
the existing RuntimeException wrapping for those failures, while allowing fatal
Error values and unrelated assertion-style failures to propagate unchanged.
🪄 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: 9197f347-cf3d-42e3-a005-ac4f6e2e49ac

📥 Commits

Reviewing files that changed from the base of the PR and between d48c1be and 0b467fc.

📒 Files selected for processing (18)
  • app/src/main/java/com/itsaky/androidide/fragments/EmptyStateFragment.kt
  • app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
  • app/src/main/java/com/itsaky/androidide/fragments/output/FilterNoMatchTracker.kt
  • app/src/main/java/com/itsaky/androidide/fragments/output/LogFilterBarController.kt
  • app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt
  • app/src/main/java/com/itsaky/androidide/logs/LogBuffer.kt
  • app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/LogViewModel.kt
  • app/src/test/java/com/itsaky/androidide/fragments/output/FilterNoMatchTrackerTest.kt
  • app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt
  • app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt
  • app/src/test/java/com/itsaky/androidide/viewmodel/LogViewModelTest.kt
  • common/src/main/java/com/itsaky/androidide/utils/KeyboardUtils.kt
  • editor/src/main/java/com/itsaky/androidide/editor/ui/EditorActionsMenu.kt
  • editor/src/main/java/com/itsaky/androidide/editor/ui/EditorSearchLayout.kt
  • editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt
  • resources/src/main/res/values/strings.xml

Comment on lines +48 to +58
override fun search(
query: String,
searchOptions: SearchOptions,
) {
if (query.isNotEmpty()) {
markSearching()
} else {
isSearching = false
}
super.search(query, searchOptions)
}

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

artifact="$(find "$HOME/.gradle/caches" -type f \( -iname '*sora*.aar' -o -iname '*sora*.jar' \) -print -quit)"
if [[ -z "$artifact" ]]; then
	echo "No resolved Sora artifact found" >&2
	exit 2
fi

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

if [[ "$artifact" == *.aar ]]; then
	unzip -p "$artifact" classes.jar > "$tmp/classes.jar"
else
	cp "$artifact" "$tmp/classes.jar"
fi

javap -classpath "$tmp/classes.jar" -c -p io.github.rosemoe.sora.widget.EditorSearcher |
	rg -n -C 10 'search|stopSearch|gotoNext|gotoPrevious|checkState'

rg -n -C 5 \
	'\bsearcher\.(search|replaceAll|replaceThis|gotoNext|gotoPrevious)\(' \
	--glob '*.{kt,java}' .

Repository: appdevforall/CodeOnTheGo

Length of output: 227


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching IDEEditorSearcher / EditorSearcher:"
fd -i 'EditorSearcher|IDEEditorSearcher' . || true

target="$(fd -i 'IDEEditorSearcher' . | head -n 1 || true)"
echo "target=$target"
if [[ -n "$target" ]]; then
  wc -l "$target"
  ast-grep outline "$target" --view compact || true
  cat -n "$target"
fi

echo "\nSora dependency mentions:"
rg -n "sora|editor|io.github.rosemoe.sora" --glob '*.toml' --glob '*.gradle*' --glob '*.properties' . || true

echo "\nUsages of searcher.*search(...):"
rg -n -C 5 'searcher\.(search\(|replaceAll\(|replaceThis\(|gotoNext\(|gotoPrevious\()' --glob '*.{kt,java}' . || true

Repository: appdevforall/CodeOnTheGo

Length of output: 13503


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching IDEEditorSearcher / EditorSearcher:"
fd -i 'IDEEditorSearcher|EditorSearcher' . || true

target="$(fd -i 'IDEEditorSearcher' . | head -n 1 || true)"
echo "target=$target"
if [[ -n "$target" ]]; then
  wc -l "$target"
  ast-grep outline "$target" --view compact || true
  cat -n "$target"
fi

echo "\nSora dependency mentions:"
rg -n "sora|editor|io.github.rosemoe.sora" --glob '*.toml' --glob '*.gradle*' --glob '*.properties' . || true

echo "\nUsages of searcher.*search(...):"
rg -n -C 5 'searcher\.(search\(|replaceAll\(|replaceThis\(|gotoNext\(|gotoPrevious\()' --glob '*.{kt,java}' . || true

Repository: appdevforall/CodeOnTheGo

Length of output: 13503


Keep isSearching aligned with Sora validation.

If super.search(query, searchOptions) validates empty or invalid patterns and throws, this method can leave isSearching == true while the search has no active query/update path. Stop the search explicitly for empty query inputs and reset isSearching in a catch block if Sora throws on invalid regular expressions. This also affects the navigation/replacement overrides because those also call markSearching() before superclass validation.

🤖 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 `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`
around lines 48 - 58, Update IDEEditorSearcher.search and the
navigation/replacement overrides that call markSearching before superclass
validation to stop immediately for empty queries, and reset isSearching when the
superclass throws for invalid patterns. Keep the searching state aligned with
Sora validation while preserving normal successful search behavior.

Source: Coding guidelines

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/itsaky/androidide/fragments/output/LogFilterBarController.kt (1)

64-80: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Notify onVisibilityChanged for the initial shown state too.

toggle() and hide() notify onVisibilityChanged, but init does not notify it when the bar first becomes visible after stub.inflate(). The filter bar layout has no explicit android:visibility="gone", so createFilterBar() in both fragments updates isFilterActive without triggering updateEmptyState, leaving the empty-state UI stale until a later toggle or filter change runs it.

Invoke onVisibilityChanged?.invoke(binding.root.isVisible) once after the filter bar state finishes initializing in init.

🤖 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
`@app/src/main/java/com/itsaky/androidide/fragments/output/LogFilterBarController.kt`
around lines 64 - 80, Update LogFilterBarController.init to invoke
onVisibilityChanged with binding.root.isVisible once after all filter bar
initialization and listeners are configured, ensuring the initial visible state
is propagated without changing toggle() or hide() behavior.
🧹 Nitpick comments (5)
editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt (2)

97-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document and test the new no-match contract.

isSearchCompleteWithNoMatches() is a new public API. Add KDoc that defines the valid-query, valid-result, and empty-result conditions. Add tests for no query, invalid results, empty results, and non-empty results.

As per coding guidelines, public functions and non-obvious logic must have KDoc, and changed non-UI logic needs unit coverage.

Proposed KDoc
+	/**
+	 * Returns true when the current non-empty query has valid results and no matches.
+	 */
 	fun isSearchCompleteWithNoMatches(): Boolean {
🤖 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 `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`
around lines 97 - 101, The new public method is missing documentation and unit
coverage for its no-match contract. Add KDoc to
IDEEditorSearcher.isSearchCompleteWithNoMatches() describing that it returns
true only when a query exists, the search result is valid, and the result set is
empty; add tests covering no query, invalid results, empty results, and
non-empty results.

Source: Coding guidelines


35-40: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Narrow the reflection catch.

IDEEditorSearcher.getEditor() uses reflection, so catch the reflection failures you can meaningfully translate, such as NoSuchFieldException, IllegalAccessException, IllegalArgumentException, and ClassCastException. Catching Throwable also traps fatal Error values and unrelated failures like the assertion-style failures in IDEEditorSearcher; let those preserve their exact shape.

🤖 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 `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`
around lines 35 - 40, Update IDEEditorSearcher.getEditor() to catch only the
reflection and cast failures it can translate: NoSuchFieldException,
IllegalAccessException, IllegalArgumentException, and ClassCastException. Keep
the existing RuntimeException wrapping for those failures, while allowing fatal
Error values and unrelated assertion-style failures to propagate unchanged.

Source: Learnings

app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt (1)

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

Document the setEmptyState contract.

Add KDoc that defines isEmpty as layout state and isSourceEmpty as source-data state. Callers can otherwise invert these values.

As per coding guidelines, “Public classes, functions, and non-obvious logic must have KDoc documenting contracts...”.

🤖 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
`@app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt`
around lines 49 - 54, Document the public setEmptyState function with KDoc,
explicitly defining isEmpty as the layout state and isSourceEmpty as the
source-data state. Describe both parameters so callers pass the values without
confusing or reversing their meanings.

Source: Coding guidelines

app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt (1)

20-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use JUnit Jupiter and Truth for the new tests.

  • app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt#L20-L22: replace JUnit 4 imports with JUnit Jupiter and Truth imports.
  • app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt#L23-L23: replace the added JUnit 4 assertion import.
  • app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt#L98-L108: migrate the new test to JUnit Jupiter and Truth. Convert the enclosing class if required.

The existing test dependencies include JUnit Jupiter and Truth, but these tests still use JUnit 4 imports/assertions instead of the required test framework.

🤖 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
`@app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt`
around lines 20 - 22, The new tests use JUnit 4 assertions instead of the
required JUnit Jupiter and Truth APIs. In
app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt
lines 20-22, replace the assertion imports with JUnit Jupiter and Truth imports;
in app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt line 23,
replace the added JUnit 4 assertion import; and in lines 98-108, migrate the new
test and enclosing class as needed to JUnit Jupiter and Truth while preserving
its assertions and behavior.

Source: Coding guidelines

app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt (1)

65-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated filter-active/no-match wiring. Both fragments independently declare a noMatchTracker = FilterNoMatchTracker() and an isFilterActive getter with the same "filter source is non-empty or bar is visible" shape; this duplication is new in this PR and the two copies can drift.

  • app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt#L65-L69: extract noMatchTracker and the isFilterActive pattern (parameterized by buildOutputViewModel.filterText.value.isNotEmpty()) into a shared helper.
  • app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt#L148-L152: reuse the same shared helper, parameterized by viewModel.filter.value != LogFilter.NONE.
🤖 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
`@app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt`
around lines 65 - 69, Extract the duplicated no-match tracking and filter-active
logic into a shared helper, parameterized by the filter-source predicate and the
relevant filter-bar visibility. Update
app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
lines 65-69 to use it with buildOutputViewModel.filterText.value.isNotEmpty(),
and app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt
lines 148-152 to use it with viewModel.filter.value != LogFilter.NONE; both
sites should reuse the shared noMatchTracker and isFilterActive behavior.
🤖 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 `@app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt`:
- Around line 241-248: Update the share completion cleanup around shareJob so it
clears the shared state and calls updateActionButtonsEnabledState only when
shareJob still references the current coroutine job. Preserve the current-tab
re-read and empty-state calculation, but prevent an older cancelled share from
clearing or enabling actions for a newer share.

In `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`:
- Around line 48-58: Update IDEEditorSearcher.search and the
navigation/replacement overrides that call markSearching before superclass
validation to stop immediately for empty queries, and reset isSearching when the
superclass throws for invalid patterns. Keep the searching state aligned with
Sora validation while preserving normal successful search behavior.
- Around line 97-101: Update IDEEditorSearcher.isSearchCompleteWithNoMatches()
to rely on the current query’s published search result rather than
isResultValid() and potentially stale lastResults. Track or validate a
query/result generation through the result publication lifecycle, and only
report no matches after the current query’s result has been published; return
false while pending, cancelled, or associated with an older query.

In `@resources/src/main/res/values/strings.xml`:
- Line 689: Update the msg_no_filter_matches string resource to use
output-neutral wording, such as “No matching entries found.”, so it is
appropriate for both log and build-output panels.

---

Outside diff comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/output/LogFilterBarController.kt`:
- Around line 64-80: Update LogFilterBarController.init to invoke
onVisibilityChanged with binding.root.isVisible once after all filter bar
initialization and listeners are configured, ensuring the initial visible state
is propagated without changing toggle() or hide() behavior.

---

Nitpick comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt`:
- Around line 65-69: Extract the duplicated no-match tracking and filter-active
logic into a shared helper, parameterized by the filter-source predicate and the
relevant filter-bar visibility. Update
app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
lines 65-69 to use it with buildOutputViewModel.filterText.value.isNotEmpty(),
and app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt
lines 148-152 to use it with viewModel.filter.value != LogFilter.NONE; both
sites should reuse the shared noMatchTracker and isFilterActive behavior.

In
`@app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt`:
- Around line 49-54: Document the public setEmptyState function with KDoc,
explicitly defining isEmpty as the layout state and isSourceEmpty as the
source-data state. Describe both parameters so callers pass the values without
confusing or reversing their meanings.

In
`@app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt`:
- Around line 20-22: The new tests use JUnit 4 assertions instead of the
required JUnit Jupiter and Truth APIs. In
app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt
lines 20-22, replace the assertion imports with JUnit Jupiter and Truth imports;
in app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt line 23,
replace the added JUnit 4 assertion import; and in lines 98-108, migrate the new
test and enclosing class as needed to JUnit Jupiter and Truth while preserving
its assertions and behavior.

In `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`:
- Around line 97-101: The new public method is missing documentation and unit
coverage for its no-match contract. Add KDoc to
IDEEditorSearcher.isSearchCompleteWithNoMatches() describing that it returns
true only when a query exists, the search result is valid, and the result set is
empty; add tests covering no query, invalid results, empty results, and
non-empty results.
- Around line 35-40: Update IDEEditorSearcher.getEditor() to catch only the
reflection and cast failures it can translate: NoSuchFieldException,
IllegalAccessException, IllegalArgumentException, and ClassCastException. Keep
the existing RuntimeException wrapping for those failures, while allowing fatal
Error values and unrelated assertion-style failures to propagate unchanged.
🪄 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: 9197f347-cf3d-42e3-a005-ac4f6e2e49ac

📥 Commits

Reviewing files that changed from the base of the PR and between d48c1be and 0b467fc.

📒 Files selected for processing (18)
  • app/src/main/java/com/itsaky/androidide/fragments/EmptyStateFragment.kt
  • app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt
  • app/src/main/java/com/itsaky/androidide/fragments/output/FilterNoMatchTracker.kt
  • app/src/main/java/com/itsaky/androidide/fragments/output/LogFilterBarController.kt
  • app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt
  • app/src/main/java/com/itsaky/androidide/logs/LogBuffer.kt
  • app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/LogViewModel.kt
  • app/src/test/java/com/itsaky/androidide/fragments/output/FilterNoMatchTrackerTest.kt
  • app/src/test/java/com/itsaky/androidide/logs/LogBufferTest.kt
  • app/src/test/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModelTest.kt
  • app/src/test/java/com/itsaky/androidide/viewmodel/LogViewModelTest.kt
  • common/src/main/java/com/itsaky/androidide/utils/KeyboardUtils.kt
  • editor/src/main/java/com/itsaky/androidide/editor/ui/EditorActionsMenu.kt
  • editor/src/main/java/com/itsaky/androidide/editor/ui/EditorSearchLayout.kt
  • editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt
  • resources/src/main/res/values/strings.xml
🛑 Comments failed to post (3)
app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt (1)

241-248: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep shareJob ownership when a share completes.

Line 245 clears shareJob without confirming that this coroutine still owns it. A cancelled job can finish after reattachment and clear a newer share job. Line 248 then enables share and clear actions during the newer share.

Only update the shared job state when shareJob is the current coroutine job.

Proposed fix
 						} finally {
-							if (isAttachedToWindow) {
+							val completedJob = coroutineContext[Job]
+							if (isAttachedToWindow && shareJob === completedJob) {
 								shareJob = null
 								val current = pagerAdapter.getFragmentAtIndex<Fragment>(binding.tabs.selectedTabPosition)
 								val isCurrentEmpty = (current as? EmptyStateFragment<*>)?.isSourceEmpty == true
 								updateActionButtonsEnabledState(isSourceEmpty = isCurrentEmpty)
 							}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

						} finally {
							val completedJob = coroutineContext[Job]
							if (isAttachedToWindow && shareJob === completedJob) {
								// The share job is still active while its own finally runs, so it must be
								// cleared first or updateActionButtonsEnabledState would keep the share and
								// clear actions disabled. Also re-read the current tab: it may have changed
								// since the share started.
								shareJob = null
								val current = pagerAdapter.getFragmentAtIndex<Fragment>(binding.tabs.selectedTabPosition)
								val isCurrentEmpty = (current as? EmptyStateFragment<*>)?.isSourceEmpty == true
								updateActionButtonsEnabledState(isSourceEmpty = isCurrentEmpty)
							}
🤖 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 `@app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt` around lines
241 - 248, Update the share completion cleanup around shareJob so it clears the
shared state and calls updateActionButtonsEnabledState only when shareJob still
references the current coroutine job. Preserve the current-tab re-read and
empty-state calculation, but prevent an older cancelled share from clearing or
enabling actions for a newer share.
editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt (1)

97-101: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 \
	'isSearchCompleteWithNoMatches|isResultValid|lastResults|PublishSearchResultEvent|currentThread' \
	--glob '*.{kt,java}' .

artifact="$(find "$HOME/.gradle/caches" -type f \( -iname '*sora*.aar' -o -iname '*sora*.jar' \) -print -quit)"
if [[ -z "$artifact" ]]; then
	echo "No resolved Sora artifact found" >&2
	exit 2
fi

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

if [[ "$artifact" == *.aar ]]; then
	unzip -p "$artifact" classes.jar > "$tmp/classes.jar"
else
	cp "$artifact" "$tmp/classes.jar"
fi

javap -classpath "$tmp/classes.jar" -c -p io.github.rosemoe.sora.widget.EditorSearcher |
	rg -n -C 12 'isResultValid|lastResults|currentThread|postInLifecycle'

Repository: appdevforall/CodeOnTheGo

Length of output: 50381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
git ls-files | rg 'IDEEditorSearcher\.kt|EditorSearcher\.java|EditorSearchLayout\.kt' || true

echo "== IDEEditorSearcher.kt =="
fd -a IDEEditorSearcher.kt . | while read -r f; do
  echo "--- ${f#"$PWD/"} ---"
  wc -l "$f"
  sed -n '1,220p' "$f"
done

echo "== relevant local searches =="
rg -n -C 6 \
  'isSearchCompleteWithNoMatches|isResultValid|hasQuery|lastResults|PublishSearchResultEvent|currentThread|postInLifecycle|onSearchResult|gotoPrevious|gotoNext' \
  --glob '*.{kt,java}' \
  editor termux 2>/dev/null || true

echo "== Gradle deps mentioning sora/editor artifacts =="
rg -n 'sora|IDEEditorSearcher|ros.*earch|EditorSearcher' \
  gradle libs.versions.toml \
  --glob '*.{toml,kts,gradle,properties}' || true

Repository: appdevforall/CodeOnTheGo

Length of output: 19937


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

echo "== Gradle versions/dependencies =="
cat gradle/libs.versions.toml | sed -n '150,168p'

echo "== Search sora artifacts in Gradle cache =="
find "$HOME/.gradle/caches" \
  \( -path '*transforms*' -o -path '*modules-2*' -o -path '*artifacts*' \) \
  -type f \( -iname 'io.github.Rosemoe.sora-editor*.jar' -o -iname 'io.github.Rosemoe.sora-editor*.aar' -o -iname 'sora-editor-*.jar' -o -iname 'sora-editor-*.aar' \
  -o -iname '*EditorSearcher*.jar' -o -iname '*EditorSearcher*.aar' \) \
  2>/dev/null | head -50

for artifact in \
  $(find "$HOME/.gradle/caches/modules-2/files-2.1/io.github.Rosemoe.sora-editor" -type f 2>/dev/null | head -20)
do
  echo "--- artifact $artifact ---"
  case "$artifact" in
    *.aar)
      unzip -l "$artifact" | rg 'classes\.jar|EditorSearcher\.class|resources\.arsc|androidmanifest\.xml' || true
      unzip -p "$artifact" classes.jar > "$tmp/classes.jar"
      ;;
    *.jar)
      cp "$artifact" "$tmp/classes.jar"
      unzip -l "$artifact" | rg 'EditorSearcher\.class|resources\.properties|resources\.csv' || true
      ;;
  esac

  echo "--- javap EditorSearcher relevant methods ---"
  javap -classpath "$tmp/classes.jar" -p io.github.rosemoe.sora.widget.EditorSearcher \
    | rg -n -C 3 'EditorSearcher|hasQuery|isResultValid|lastResults|currentThread|postInLifecycle|gotoPrevious|gotoNext|search' || true

  echo "--- bytecode methods around relevant names ---"
  javap -classpath "$tmp/classes.jar" -c -p io.github.rosemoe.sora.widget.EditorSearcher \
    | rg -n -C 16 'isResultValid|lastResults|currentThread|postInLifecycle|onSearchResult|PublishSearchResultEvent|gotoPrevious|gotoNext' || true
done

Repository: appdevforall/CodeOnTheGo

Length of output: 1593


Read the published search results instead of isResultValid().

isSearchCompleteWithNoMatches() returns false while the published match list has not arrived, then it can use lastResults that belongs to a previous search if the query changes or the worker is cancelled before the lifecycle callback runs. Track a query/result generation id, or consume the result publication callback before reading lastResults, so the no-match message applies only to the current query.

🤖 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 `@editor/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt`
around lines 97 - 101, Update IDEEditorSearcher.isSearchCompleteWithNoMatches()
to rely on the current query’s published search result rather than
isResultValid() and potentially stale lastResults. Track or validate a
query/result generation through the result publication lifecycle, and only
report no matches after the current query’s result has been published; return
false while pending, cancelled, or associated with an older query.

Source: Learnings

resources/src/main/res/values/strings.xml (1)

689-689: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the no-match message output-neutral.

BuildOutputFragment also uses msg_no_filter_matches. The build-output panel will display “No matching log entries found.” Change this text to “No matching entries found.” or add a separate build-output resource.

🤖 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 `@resources/src/main/res/values/strings.xml` at line 689, Update the
msg_no_filter_matches string resource to use output-neutral wording, such as “No
matching entries found.”, so it is appropriate for both log and build-output
panels.

Base automatically changed from bug/ADFA-4854-filter-bar-invisible to stage July 31, 2026 09:54
@dara-abijo-adfa
dara-abijo-adfa deleted the ADFA-4922-disable-text-action-in-search branch July 31, 2026 10:04
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