Skip to content

Conversation

@skydoves
Copy link
Owner

@skydoves skydoves commented Nov 24, 2025

Mark runtime stability for composables.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced stability analysis tooltips to display runtime parameter and receiver breakdowns
    • Added runtime stability explanatory information for composables with runtime-determined stability
  • Documentation

    • Updated gutter icon color descriptions to clarify runtime stability behavior

✏️ Tip: You can customize this high-level summary in your review settings.

@skydoves skydoves self-assigned this Nov 24, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

The pull request refactors the Compose stability analyzer's icon and tooltip generation by replacing discrete boolean parameters with a unified ComposableStabilityInfo analysis object. This enables runtime stability detection, displays runtime parameter and receiver breakdowns in tooltips, and introduces a new "Runtime Stability" explanatory section.

Changes

Cohort / File(s) Summary
Runtime Stability Analysis Refactoring
compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/StabilityLineMarkerProvider.kt
Refactored getIcon() method signature to accept ComposableStabilityInfo instead of discrete booleans. Enhanced icon and tooltip generation to compute and display runtime stability status. Added isRuntimeOnly detection for runtime-driven skippability. Included parameter and receiver breakdowns (stable, runtime, unstable counts) and new "Runtime Stability" explanatory block in tooltips.
Documentation Update
compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/settings/StabilitySettingsConfigurable.kt
Updated comment for runtime color in gutter icon colors panel to clarify that stability is determined at runtime and may vary between library versions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • StabilityLineMarkerProvider.kt: Careful review required for the refactored getIcon() method logic, isRuntimeOnly determination, and tooltip generation with runtime parameter/receiver breakdowns to ensure all stability computations and display logic are correct
  • StabilitySettingsConfigurable.kt: Minimal effort; documentation-only change

Poem

🐰 A rabbit hops with glee so bright,
Runtime stability in sight!
Colors dance, tooltips now tell the tale,
Of parameters that shimmer and sail.
No more booleans in our way,
Analysis flows—hip-hop, hooray! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is minimal and does not follow the required template structure with Goal, Implementation details, and Explain examples sections, nor does it include required preparation steps like spotlessApply and apiDump. Expand the description to include: 🎯 Goal section explaining the motivation, 🛠 Implementation details section describing the changes, ✍️ Explain examples section with code examples, and confirmation of running ./gradlew spotlessApply and ./gradlew apiDump.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Mark runtime stability for composables' clearly summarizes the main change: adding runtime stability marking to composables, which aligns with the refactored icon/tooltip logic in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/runtime-skippability

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/StabilityLineMarkerProvider.kt (1)

133-235: Runtime icon vs tooltip/explanation are inconsistent for receiver‑only runtime cases.

There’s a behavioral mismatch between how you classify runtime stability for the icon and how the tooltip header/explanation are computed:

  • getIcon treats both parameters and receivers as part of allParams, so an unstable/runtime receiver influences hasUnstable / hasRuntime and thus the color.
  • In buildTooltip, isRuntimeOnly and the runtime explanation block are based only on parameter counts:
val stableCount = analysis.parameters.count { it.stability == ParameterStability.STABLE }
val unstableCount = analysis.parameters.count { it.stability == ParameterStability.UNSTABLE }
val runtimeCount = analysis.parameters.count { it.stability == ParameterStability.RUNTIME }
// ...
val isRuntimeOnly = unstableCount == 0 && runtimeCount > 0
// ...
if (isRuntimeOnly || runtimeCount > 0) { /* Runtime Stability block */ }

Later you compute receiver counts (runtimeReceiverCount, etc.), but they don’t feed back into isRuntimeOnly or the runtime explanation condition.

Problematic scenario

  • No parameters are runtime (runtimeCount == 0), and no parameters are unstable.
  • At least one receiver is runtime (runtimeReceiverCount > 0), and no receivers are unstable.

Effect:

  • getIcon sees hasRuntime == true and hasUnstable == false → yellow runtime icon.
  • isRuntimeOnly == false and runtimeCount == 0 → header will not say “Runtime Stability”, and the “🟡 Runtime Stability” block won’t be shown.

So the gutter icon advertises runtime stability, but the tooltip never explains why for receiver‑only runtime.

Suggested fix: include receivers in runtime/unstable classification for the tooltip

You’re already computing receiver counts; reuse them when computing isRuntimeOnly and deciding whether to show the runtime block. One possible adjustment:

-      val stableCount = analysis.parameters.count { it.stability == ParameterStability.STABLE }
-      val unstableCount = analysis.parameters.count { it.stability == ParameterStability.UNSTABLE }
-      val runtimeCount = analysis.parameters.count { it.stability == ParameterStability.RUNTIME }
+      val stableCount = analysis.parameters.count { it.stability == ParameterStability.STABLE }
+      val unstableCount = analysis.parameters.count { it.stability == ParameterStability.UNSTABLE }
+      val runtimeCount = analysis.parameters.count { it.stability == ParameterStability.RUNTIME }
       val totalCount = analysis.parameters.size
-
-      // Check if all non-stable parameters are runtime
-      val isRuntimeOnly = unstableCount == 0 && runtimeCount > 0
+      // Receiver information
+      val stableReceiverCount = analysis.receivers.count { it.stability == ParameterStability.STABLE }
+      val unstableReceiverCount = analysis.receivers.count { it.stability == ParameterStability.UNSTABLE }
+      val runtimeReceiverCount = analysis.receivers.count { it.stability == ParameterStability.RUNTIME }
+      val totalReceiverCount = analysis.receivers.size
+
+      // Check if all non-stable *parameters or receivers* are runtime
+      val hasAnyRuntime = runtimeCount > 0 || runtimeReceiverCount > 0
+      val hasAnyUnstable = unstableCount > 0 || unstableReceiverCount > 0
+      val isRuntimeOnly = hasAnyRuntime && !hasAnyUnstable
@@
-          isRuntimeOnly ->
-            "🟡 Runtime Stability (skippability determined at runtime)"
+          isRuntimeOnly ->
+            "🟡 Runtime Stability (skippability determined at runtime)"
@@
-      // Receiver information
-      val stableReceiverCount = analysis.receivers.count {
-        it.stability == ParameterStability.STABLE
-      }
-      val unstableReceiverCount = analysis.receivers.count {
-        it.stability == ParameterStability.UNSTABLE
-      }
-      val runtimeReceiverCount = analysis.receivers.count {
-        it.stability == ParameterStability.RUNTIME
-      }
-      val totalReceiverCount = analysis.receivers.size
+      // Receiver information (reuse counts above)
@@
-      // Runtime stability explanation
-      if (isRuntimeOnly || runtimeCount > 0) {
+      // Runtime stability explanation (trigger if any runtime param or receiver)
+      if (hasAnyRuntime) {
         append("\n\n🟡 Runtime Stability:")
         append("\nStability is determined at runtime based on")
         append("\nactual parameter values and their implementations.")
         append("\nSkippability may change between library versions")
         append("\nor when parameter implementations change.")
       }

This keeps your parameter/receiver breakdown display as-is, but ensures:

  • Yellow icon ⇔ runtime‑aware header and explanation, whether the runtime classification comes from parameters, receivers, or both.
  • “Runtime Stability” label is consistent with the same combined notion of runtime/unstable you already use for icons.
🧹 Nitpick comments (2)
compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/settings/StabilitySettingsConfigurable.kt (1)

132-135: Runtime gutter color description looks good (small wording nit only).

Text correctly explains that stability is runtime-determined and version-dependent. If you want to be extra precise, you could say “no unstable parameters and at least one runtime parameter” instead of “only runtime parameters,” but that’s optional and doesn’t affect behavior.

compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/StabilityLineMarkerProvider.kt (1)

79-124: Align icon color logic with intent; simplify when branches.

The getIcon logic is mostly sound, but there are two points worth tightening:

  1. Redundant isSkippable branches

Currently:

val color = when {
  analysis.isSkippable && allStable -> Color(settings.stableGutterColorRGB)
  analysis.isSkippable -> Color(settings.stableGutterColorRGB)
  hasUnstable -> Color(settings.unstableGutterColorRGB)
  hasRuntime -> Color(settings.runtimeGutterColorRGB)
  else -> Color(settings.unstableGutterColorRGB)
}

The first two branches produce the same result, so allStable is effectively unused. Either:

  • Drop the first branch to reduce noise, or
  • If you intend to only show green when everything is actually stable (and treat other skippable-but-not-fully-stable cases differently in the future), adjust the second branch to reflect that.

A minimal simplification:

-    val color = when {
-      analysis.isSkippable && allStable -> Color(settings.stableGutterColorRGB)
-      analysis.isSkippable -> Color(settings.stableGutterColorRGB)
-      hasUnstable -> Color(settings.unstableGutterColorRGB)
-      hasRuntime -> Color(settings.runtimeGutterColorRGB)
-      else -> Color(settings.unstableGutterColorRGB)
-    }
+    val color = when {
+      analysis.isSkippable -> Color(settings.stableGutterColorRGB)
+      hasUnstable -> Color(settings.unstableGutterColorRGB)
+      hasRuntime -> Color(settings.runtimeGutterColorRGB)
+      else -> Color(settings.unstableGutterColorRGB)
+    }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82daefc and 09bb682.

📒 Files selected for processing (2)
  • compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/StabilityLineMarkerProvider.kt (7 hunks)
  • compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/settings/StabilitySettingsConfigurable.kt (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Runtime Module Tests
  • GitHub Check: Spotless check
  • GitHub Check: API check
  • GitHub Check: Test IntelliJ Plugin
  • GitHub Check: Build IntelliJ Plugin
  • GitHub Check: Build and Tests

@skydoves skydoves merged commit e94b9a1 into main Nov 24, 2025
12 checks passed
@skydoves skydoves deleted the fix/runtime-skippability branch November 24, 2025 05:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants