Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
return null
}

val icon = getIcon(analysis.isSkippable, analysis.isRestartable)
val icon = getIcon(analysis)
val tooltip = buildTooltip(analysis)

return LineMarkerInfo(
Expand All @@ -92,11 +92,31 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {

/**
* Gets the appropriate icon based on stability status.
*
* Colors:
* - Green (stable): All parameters are stable, composable is skippable
* - Yellow (runtime): All non-stable parameters are RUNTIME (stability decided at runtime)
* - Red (unstable): Has at least one UNSTABLE parameter
*/
private fun getIcon(isSkippable: Boolean, isRestartable: Boolean): Icon {
private fun getIcon(analysis: ComposableStabilityInfo): Icon {
val allParams = analysis.parameters + analysis.receivers.map {
com.skydoves.compose.stability.runtime.ParameterStabilityInfo(
name = it.type,
type = it.type,
stability = it.stability,
reason = null,
)
}

val hasUnstable = allParams.any { it.stability == ParameterStability.UNSTABLE }
val hasRuntime = allParams.any { it.stability == ParameterStability.RUNTIME }
val allStable = allParams.all { it.stability == ParameterStability.STABLE }

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

Expand All @@ -110,6 +130,14 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
analysis: ComposableStabilityInfo,
): String {
return buildString {
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

append(
when {
analysis.isSkippableInStrongSkippingMode ->
Expand All @@ -118,6 +146,9 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
analysis.isSkippable ->
"✅ Skippable (all parameters are stable)"

isRuntimeOnly ->
"🟡 Runtime Stability (skippability determined at runtime)"

analysis.isRestartable ->
"⚠️ Restartable (not skippable)"

Expand All @@ -126,13 +157,27 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
},
)

// Parameter count
val unstableCount = analysis.parameters.count { it.stability != ParameterStability.STABLE }
val totalCount = analysis.parameters.size

// Parameter count with breakdown
if (totalCount > 0) {
append("\n")
append("Parameters: ${totalCount - unstableCount}/$totalCount stable")
append("Parameters: $stableCount/$totalCount stable")
if (runtimeCount > 0) {
append(", $runtimeCount runtime")
}
if (unstableCount > 0) {
append(", $unstableCount unstable")
}
}

// List runtime parameters
if (runtimeCount > 0) {
append("\n")
append("Runtime: ")
append(
analysis.parameters
.filter { it.stability == ParameterStability.RUNTIME }
.joinToString(", ") { it.name },
)
}

// List unstable parameters
Expand All @@ -141,21 +186,29 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
append("Unstable: ")
append(
analysis.parameters
.filter { it.stability != ParameterStability.STABLE }
.filter { it.stability == ParameterStability.UNSTABLE }
.joinToString(", ") { it.name },
)
}

// Receiver information
val stableReceiverCount = analysis.receivers.count {
it.stability == ParameterStability.STABLE
}
val unstableReceiverCount = analysis.receivers.count {
it.stability != ParameterStability.STABLE
it.stability == ParameterStability.UNSTABLE
}
val runtimeReceiverCount = analysis.receivers.count {
it.stability == ParameterStability.RUNTIME
}
val totalReceiverCount = analysis.receivers.size

if (totalReceiverCount > 0) {
append("\n")
val stableReceivers = totalReceiverCount - unstableReceiverCount
append("Receivers: $stableReceivers/$totalReceiverCount stable")
append("Receivers: $stableReceiverCount/$totalReceiverCount stable")
if (runtimeReceiverCount > 0) {
append(", $runtimeReceiverCount runtime")
}
}

// List unstable receivers
Expand All @@ -164,13 +217,22 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
append("Unstable receivers: ")
append(
analysis.receivers
.filter { it.stability != ParameterStability.STABLE }
.filter { it.stability == ParameterStability.UNSTABLE }
.joinToString(", ") {
"${it.receiverKind.name.lowercase()}: ${it.type}"
},
)
}

// Runtime stability explanation
if (isRuntimeOnly || runtimeCount > 0) {
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.")
}

// Additional info for strong skipping mode
if (analysis.isSkippableInStrongSkippingMode) {
append("\n\n💡 Strong Skipping Mode: All composables are skippable")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ public class StabilitySettingsConfigurable(
row("Runtime color:") {
runtimeGutterColorPanel = ColorPanel()
cell(runtimeGutterColorPanel)
.comment("Color for runtime-determined stability (currently not used)")
.comment(
"Color for composables with only runtime parameters. " +
"Stability is determined at runtime and may vary between library versions.",
)
}
}

Expand Down