Skip to content

Commit

Permalink
0.4.17-STC5
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmedalkhashab committed Oct 26, 2024
1 parent 160f555 commit 5bb9ee3
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 3 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ allprojects {
plugins.withType<JavaPlugin> {
extensions.configure<JavaPluginExtension> {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
languageVersion.set(JavaLanguageVersion.of(17))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ systemProp.org.gradle.internal.http.socketTimeout=240000
SONATYPE_HOST=DEFAULT
RELEASE_SIGNING_ENABLED=true
GROUP=io.nlopez.compose.rules
VERSION_NAME=0.4.17-SNAPSHOT
VERSION_NAME=0.4.17
POM_DESCRIPTION=Jetpack Compose Rules
POM_INCEPTION_YEAR=2022
POM_URL=https://github.com/mrmans0n/compose-rules/
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class ComposeRuleSetProvider :
RuleProvider { UnstableCollectionsCheck() },
RuleProvider { ViewModelForwardingCheck() },
RuleProvider { ViewModelInjectionCheck() },
RuleProvider { EnforceIconTextButtonRule() },
RuleProvider { EnforceIconTextButtonRule22() },
)

private companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package io.nlopez.compose.rules.ktlint

import com.pinterest.ktlint.rule.engine.core.api.ElementType
import com.pinterest.ktlint.rule.engine.core.api.Rule
import com.pinterest.ktlint.rule.engine.core.api.RuleId
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.startOffset

/**
* Custom ktlint rule to enforce the use of IconTextButton instead of Button to maintain consistency across the UI.
*
* Maintainer: My stc Team
* Repository URL: https://github.com/your-repository-url
* Issue Tracker: https://github.com/your-repository-url/issues
*
* ## Non-Compliant Example:
* ```
* @Composable
* fun Example() {
* Button(onClick = { /* Do something */ }) {
* Text("Click Me")
* }
* }
* ```
*
* ## Compliant Example:
* ```
* @Composable
* fun Example() {
* IconTextButton(onClick = { /* Do something */ }) {
* Text("Click Me")
* }
* }
* ```
*/
class EnforceIconTextButtonRule :
Rule(
ruleId = RuleId("enforce-icon-text-button"),
about =
About(
maintainer = "My stc Team",
repositoryUrl = "https://github.com/your-repository-url",
issueTrackerUrl = "https://github.com/your-repository-url/issues",
),
) {
override fun beforeFirstNode(editorConfig: EditorConfig) {
// Reset state before processing the file.
}

@Deprecated(
"Marked for removal in Ktlint 2.0. Please implement interface RuleAutocorrectApproveHandler.",
)
override fun beforeVisitChildNodes(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
) {
val psi = node.psi
when (node.elementType) {
ElementType.CALL_EXPRESSION -> {
val callExpression = psi as? KtCallExpression ?: return
visitCallExpression(callExpression, autoCorrect, emit)
}
}
}

private fun visitCallExpression(
callExpression: KtCallExpression,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
) {
val calleeExpression = callExpression.calleeExpression?.text ?: return

// Check if the call expression is a Button composable.
if (calleeExpression == "Button") {
emit(
callExpression.startOffset,
"Use IconTextButton instead of Button to maintain consistency in the UI.",
autoCorrect,
)

// Optional auto-correct logic to replace Button with IconTextButton
if (autoCorrect) {
replaceButtonWithIconTextButton(callExpression)
}
}
}

private fun replaceButtonWithIconTextButton(callExpression: KtCallExpression) {
val factory = KtPsiFactory(callExpression.project)
val newCallee = factory.createExpression("IconTextButton")
callExpression.calleeExpression?.replace(newCallee)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.nlopez.compose.rules.ktlint

import io.nlopez.compose.rules.KtlintRule
import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig
import io.nlopez.compose.core.ComposeKtVisitor
import io.nlopez.compose.core.Decision
import io.nlopez.compose.core.Emitter
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.psiUtil.startOffset

class EnforceIconTextButtonRule22 :
KtlintRule(
id = "compose:enforce-icon-text-button",
editorConfigProperties = emptySet(),
), ComposeKtVisitor {
private lateinit var properties: EditorConfig

override fun beforeFirstNode(editorConfig: EditorConfig) {
properties = editorConfig
}

override fun beforeVisitChildNodes(
node: ASTNode,
emit: (
offset: Int,
errorMessage: String,
canBeAutoCorrected: Boolean,
) -> AutocorrectDecision,
) {
val psi = node.psi
if (psi is KtCallExpression) {
visitCallExpression(psi, emit.toEmitter())
}
}

private fun visitCallExpression(
callExpression: KtCallExpression,
emitter: Emitter,
) {
val calleeExpression = callExpression.calleeExpression?.text ?: return

// Check if the call expression is a Button composable.
if (calleeExpression == "Button") {
emitter.report(
callExpression,
"Use IconTextButton instead of Button to maintain consistency in the UI.",
canBeAutoCorrected = true,
)
}
}

private fun ((Int, String, Boolean) -> AutocorrectDecision).toEmitter() =
Emitter { element, errorMessage, canBeAutoCorrected ->
val offset = element.startOffset
when (invoke(offset, errorMessage, canBeAutoCorrected)) {
AutocorrectDecision.ALLOW_AUTOCORRECT -> Decision.Fix
AutocorrectDecision.NO_AUTOCORRECT -> Decision.Ignore
}
}
}

0 comments on commit 5bb9ee3

Please sign in to comment.