-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ComposePreviewNaming rule and support multipreview annotations (#93)
Multipreview annotations weren't correctly supported, which caused issues with the `ModifierMissing` rule. This patch does two things to address that: - Have the annotations for multipreviews to be properly named (add Preview/s suffix) - Expand the preview checks to look for `@Preview` / `@WhateverPreview(s)` Fixes #91
- Loading branch information
Showing
14 changed files
with
348 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
core-common/src/main/kotlin/com/twitter/rules/core/util/Previews.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.rules.core.util | ||
|
||
import org.jetbrains.kotlin.psi.KtAnnotated | ||
import org.jetbrains.kotlin.psi.KtAnnotationEntry | ||
|
||
val KtAnnotated.isPreview: Boolean | ||
get() = annotationEntries.any { it.isPreviewAnnotation } | ||
|
||
val KtAnnotationEntry.isPreviewAnnotation: Boolean | ||
get() = calleeExpression?.text?.let { PreviewNameRegex.matches(it) } == true | ||
|
||
val KtAnnotated.isPreviewParameter: Boolean | ||
get() = annotationEntries.any { it.calleeExpression?.text == "PreviewParameter" } | ||
|
||
val PreviewNameRegex by lazy { | ||
Regex(".*Preview[s]*$") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
rules/common/src/main/kotlin/com/twitter/compose/rules/ComposePreviewNaming.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules | ||
|
||
import com.twitter.rules.core.ComposeKtVisitor | ||
import com.twitter.rules.core.Emitter | ||
import com.twitter.rules.core.report | ||
import com.twitter.rules.core.util.isPreview | ||
import com.twitter.rules.core.util.isPreviewAnnotation | ||
import org.jetbrains.kotlin.psi.KtClass | ||
|
||
class ComposePreviewNaming : ComposeKtVisitor { | ||
override fun visitClass(clazz: KtClass, autoCorrect: Boolean, emitter: Emitter) { | ||
if (!clazz.isAnnotation()) return | ||
if (!clazz.isPreview) return | ||
|
||
// We know here that we are in an annotation that either has a @Preview or other preview annotations | ||
val count = clazz.annotationEntries.count { it.isPreviewAnnotation } | ||
val name = clazz.nameAsSafeName.asString() | ||
if (count == 1 && !name.endsWith("Preview")) { | ||
emitter.report(clazz, createMessage(count, "Preview")) | ||
} else if (count > 1 && !name.endsWith("Previews")) { | ||
emitter.report(clazz, createMessage(count, "Previews")) | ||
} | ||
} | ||
|
||
companion object { | ||
fun createMessage(count: Int, suggestedSuffix: String): String = """ | ||
Preview annotations with $count preview annotations should end with the `$suggestedSuffix` suffix. | ||
See https://twitter.github.io/compose-rules/rules/#naming-multipreview-annotations-properly for more information. | ||
""".trimIndent() | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
rules/detekt/src/main/kotlin/com/twitter/compose/rules/detekt/ComposePreviewNamingCheck.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules.detekt | ||
|
||
import com.twitter.compose.rules.ComposePreviewNaming | ||
import com.twitter.rules.core.ComposeKtVisitor | ||
import com.twitter.rules.core.detekt.TwitterDetektRule | ||
import io.gitlab.arturbosch.detekt.api.Config | ||
import io.gitlab.arturbosch.detekt.api.Debt | ||
import io.gitlab.arturbosch.detekt.api.Issue | ||
import io.gitlab.arturbosch.detekt.api.Severity | ||
|
||
class ComposePreviewNamingCheck(config: Config) : | ||
TwitterDetektRule(config), | ||
ComposeKtVisitor by ComposePreviewNaming() { | ||
|
||
override val issue: Issue = Issue( | ||
id = "PreviewNaming", | ||
severity = Severity.CodeSmell, | ||
description = "Multipreview annotations should end with the `Previews` suffix", | ||
debt = Debt.FIVE_MINS | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
.../detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposePreviewNamingCheckTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules.detekt | ||
|
||
import com.twitter.compose.rules.ComposePreviewNaming | ||
import io.gitlab.arturbosch.detekt.api.Config | ||
import io.gitlab.arturbosch.detekt.api.SourceLocation | ||
import io.gitlab.arturbosch.detekt.test.assertThat | ||
import io.gitlab.arturbosch.detekt.test.lint | ||
import org.intellij.lang.annotations.Language | ||
import org.junit.jupiter.api.Test | ||
|
||
class ComposePreviewNamingCheckTest { | ||
|
||
private val rule = ComposePreviewNamingCheck(Config.empty) | ||
|
||
@Test | ||
fun `passes for non-preview annotations`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
annotation class Banana | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors).isEmpty() | ||
} | ||
|
||
@Test | ||
fun `passes for preview annotations with the proper names`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Preview | ||
annotation class BananaPreview | ||
@BananaPreview | ||
annotation class DoubleBananaPreview | ||
@Preview | ||
@Preview | ||
annotation class ApplePreviews | ||
@Preview | ||
@ApplePreviews | ||
annotation class CombinedApplePreviews | ||
@BananaPreview | ||
@ApplePreviews | ||
annotation class FruitBasketPreviews | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors).isEmpty() | ||
} | ||
|
||
@Test | ||
fun `errors when a multipreview annotation is not correctly named for 1 preview`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Preview | ||
annotation class Banana | ||
@Preview | ||
annotation class BananaPreviews | ||
@BananaPreview | ||
annotation class WithBananaPreviews | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors).hasSourceLocations( | ||
SourceLocation(2, 18), | ||
SourceLocation(4, 18), | ||
SourceLocation(6, 18) | ||
) | ||
for (error in errors) { | ||
assertThat(error).hasMessage(ComposePreviewNaming.createMessage(1, "Preview")) | ||
} | ||
} | ||
|
||
@Test | ||
fun `errors when a multipreview annotation is not correctly named for multi previews`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Preview | ||
@Preview | ||
annotation class BananaPreview | ||
@BananaPreview | ||
@BananaPreview | ||
annotation class BananaPreview | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors).hasSourceLocations( | ||
SourceLocation(3, 18), | ||
SourceLocation(6, 18) | ||
) | ||
for (error in errors) { | ||
assertThat(error).hasMessage(ComposePreviewNaming.createMessage(2, "Previews")) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/ComposePreviewNamingCheck.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules.ktlint | ||
|
||
import com.twitter.compose.rules.ComposePreviewNaming | ||
import com.twitter.rules.core.ComposeKtVisitor | ||
import com.twitter.rules.core.ktlint.TwitterKtlintRule | ||
|
||
class ComposePreviewNamingCheck : | ||
TwitterKtlintRule("twitter-compose:preview-naming"), | ||
ComposeKtVisitor by ComposePreviewNaming() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.