Skip to content

Commit e94b9a1

Browse files
authored
Merge pull request #64 from skydoves/fix/runtime-skippability
Mark runtime stability for composables
2 parents 80a325f + 09bb682 commit e94b9a1

File tree

2 files changed

+80
-15
lines changed

2 files changed

+80
-15
lines changed

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

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
7676
return null
7777
}
7878

79-
val icon = getIcon(analysis.isSkippable, analysis.isRestartable)
79+
val icon = getIcon(analysis)
8080
val tooltip = buildTooltip(analysis)
8181

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

9393
/**
9494
* Gets the appropriate icon based on stability status.
95+
*
96+
* Colors:
97+
* - Green (stable): All parameters are stable, composable is skippable
98+
* - Yellow (runtime): All non-stable parameters are RUNTIME (stability decided at runtime)
99+
* - Red (unstable): Has at least one UNSTABLE parameter
95100
*/
96-
private fun getIcon(isSkippable: Boolean, isRestartable: Boolean): Icon {
101+
private fun getIcon(analysis: ComposableStabilityInfo): Icon {
102+
val allParams = analysis.parameters + analysis.receivers.map {
103+
com.skydoves.compose.stability.runtime.ParameterStabilityInfo(
104+
name = it.type,
105+
type = it.type,
106+
stability = it.stability,
107+
reason = null,
108+
)
109+
}
110+
111+
val hasUnstable = allParams.any { it.stability == ParameterStability.UNSTABLE }
112+
val hasRuntime = allParams.any { it.stability == ParameterStability.RUNTIME }
113+
val allStable = allParams.all { it.stability == ParameterStability.STABLE }
114+
97115
val color = when {
98-
isSkippable -> Color(settings.stableGutterColorRGB)
99-
// isRestartable -> Color(settings.runtimeGutterColorRGB)
116+
analysis.isSkippable && allStable -> Color(settings.stableGutterColorRGB)
117+
analysis.isSkippable -> Color(settings.stableGutterColorRGB)
118+
hasUnstable -> Color(settings.unstableGutterColorRGB)
119+
hasRuntime -> Color(settings.runtimeGutterColorRGB)
100120
else -> Color(settings.unstableGutterColorRGB)
101121
}
102122

@@ -110,6 +130,14 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
110130
analysis: ComposableStabilityInfo,
111131
): String {
112132
return buildString {
133+
val stableCount = analysis.parameters.count { it.stability == ParameterStability.STABLE }
134+
val unstableCount = analysis.parameters.count { it.stability == ParameterStability.UNSTABLE }
135+
val runtimeCount = analysis.parameters.count { it.stability == ParameterStability.RUNTIME }
136+
val totalCount = analysis.parameters.size
137+
138+
// Check if all non-stable parameters are runtime
139+
val isRuntimeOnly = unstableCount == 0 && runtimeCount > 0
140+
113141
append(
114142
when {
115143
analysis.isSkippableInStrongSkippingMode ->
@@ -118,6 +146,9 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
118146
analysis.isSkippable ->
119147
"✅ Skippable (all parameters are stable)"
120148

149+
isRuntimeOnly ->
150+
"🟡 Runtime Stability (skippability determined at runtime)"
151+
121152
analysis.isRestartable ->
122153
"⚠️ Restartable (not skippable)"
123154

@@ -126,13 +157,27 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
126157
},
127158
)
128159

129-
// Parameter count
130-
val unstableCount = analysis.parameters.count { it.stability != ParameterStability.STABLE }
131-
val totalCount = analysis.parameters.size
132-
160+
// Parameter count with breakdown
133161
if (totalCount > 0) {
134162
append("\n")
135-
append("Parameters: ${totalCount - unstableCount}/$totalCount stable")
163+
append("Parameters: $stableCount/$totalCount stable")
164+
if (runtimeCount > 0) {
165+
append(", $runtimeCount runtime")
166+
}
167+
if (unstableCount > 0) {
168+
append(", $unstableCount unstable")
169+
}
170+
}
171+
172+
// List runtime parameters
173+
if (runtimeCount > 0) {
174+
append("\n")
175+
append("Runtime: ")
176+
append(
177+
analysis.parameters
178+
.filter { it.stability == ParameterStability.RUNTIME }
179+
.joinToString(", ") { it.name },
180+
)
136181
}
137182

138183
// List unstable parameters
@@ -141,21 +186,29 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
141186
append("Unstable: ")
142187
append(
143188
analysis.parameters
144-
.filter { it.stability != ParameterStability.STABLE }
189+
.filter { it.stability == ParameterStability.UNSTABLE }
145190
.joinToString(", ") { it.name },
146191
)
147192
}
148193

149194
// Receiver information
195+
val stableReceiverCount = analysis.receivers.count {
196+
it.stability == ParameterStability.STABLE
197+
}
150198
val unstableReceiverCount = analysis.receivers.count {
151-
it.stability != ParameterStability.STABLE
199+
it.stability == ParameterStability.UNSTABLE
200+
}
201+
val runtimeReceiverCount = analysis.receivers.count {
202+
it.stability == ParameterStability.RUNTIME
152203
}
153204
val totalReceiverCount = analysis.receivers.size
154205

155206
if (totalReceiverCount > 0) {
156207
append("\n")
157-
val stableReceivers = totalReceiverCount - unstableReceiverCount
158-
append("Receivers: $stableReceivers/$totalReceiverCount stable")
208+
append("Receivers: $stableReceiverCount/$totalReceiverCount stable")
209+
if (runtimeReceiverCount > 0) {
210+
append(", $runtimeReceiverCount runtime")
211+
}
159212
}
160213

161214
// List unstable receivers
@@ -164,13 +217,22 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
164217
append("Unstable receivers: ")
165218
append(
166219
analysis.receivers
167-
.filter { it.stability != ParameterStability.STABLE }
220+
.filter { it.stability == ParameterStability.UNSTABLE }
168221
.joinToString(", ") {
169222
"${it.receiverKind.name.lowercase()}: ${it.type}"
170223
},
171224
)
172225
}
173226

227+
// Runtime stability explanation
228+
if (isRuntimeOnly || runtimeCount > 0) {
229+
append("\n\n🟡 Runtime Stability:")
230+
append("\nStability is determined at runtime based on")
231+
append("\nactual parameter values and their implementations.")
232+
append("\nSkippability may change between library versions")
233+
append("\nor when parameter implementations change.")
234+
}
235+
174236
// Additional info for strong skipping mode
175237
if (analysis.isSkippableInStrongSkippingMode) {
176238
append("\n\n💡 Strong Skipping Mode: All composables are skippable")

compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/settings/StabilitySettingsConfigurable.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,10 @@ public class StabilitySettingsConfigurable(
129129
row("Runtime color:") {
130130
runtimeGutterColorPanel = ColorPanel()
131131
cell(runtimeGutterColorPanel)
132-
.comment("Color for runtime-determined stability (currently not used)")
132+
.comment(
133+
"Color for composables with only runtime parameters. " +
134+
"Stability is determined at runtime and may vary between library versions.",
135+
)
133136
}
134137
}
135138

0 commit comments

Comments
 (0)