From 80a76ad0ff5a60f1bc54eb392fb5bd0a4d1f0bf5 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Mon, 17 Oct 2022 16:10:48 -0400 Subject: [PATCH 1/3] Introduce `allowedNames` config for `ComposeNaming` check Wasn't sure what to call the editorconfig property, let me know! Resolves #104 --- docs/ktlint.md | 9 +++++++++ .../com/twitter/compose/rules/ComposeNaming.kt | 10 +++++++++- .../rules/detekt/ComposeNamingCheckTest.kt | 18 ++++++++++++++++-- .../rules/ktlint/EditorConfigProperties.kt | 18 ++++++++++++++++++ .../rules/ktlint/ComposeNamingCheckTest.kt | 16 ++++++++++++++++ 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/docs/ktlint.md b/docs/ktlint.md index 9aed6d85..be1b156d 100644 --- a/docs/ktlint.md +++ b/docs/ktlint.md @@ -66,6 +66,15 @@ In `preview-public-check`, only previews with a `@PreviewParameter` are required twitter_compose_preview_public_only_if_params = false ``` +### Allowing matching function names + +The `twitter-compose:naming-check` rule requires all composables that return a value to be lowercased. If you want to allow certain patterns though, you can configure a comma-separated list of matching regexes in your `.editorconfig` file: + +```editorconfig +[*.{kt,kts}] +twitter_compose_allowed_composable_function_names = .*Presenter,.*SomethingElse +``` + ## Disabling a specific rule To disable a rule you have to follow the [instructions from the ktlint documentation](https://github.com/pinterest/ktlint#how-do-i-suppress-an-errors-for-a-lineblockfile), and use the id of the rule you want to disable with the `twitter-compose` tag. diff --git a/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt b/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt index 23f64c74..c11e1c3c 100644 --- a/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt +++ b/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 package com.twitter.compose.rules +import com.twitter.rules.core.ComposeKtConfig.Companion.config import com.twitter.rules.core.ComposeKtVisitor import com.twitter.rules.core.Emitter import com.twitter.rules.core.report @@ -14,11 +15,18 @@ class ComposeNaming : ComposeKtVisitor { override fun visitComposable(function: KtFunction, autoCorrect: Boolean, emitter: Emitter) { // If it's a block we can't know if there is a return type or not from ktlint if (!function.hasBlockBody()) return - val firstLetter = function.name?.first() ?: return + val functionName = function.name?.takeUnless(String::isEmpty) ?: return + val firstLetter = functionName.first() if (function.returnsValue) { // If it returns value, the composable should start with a lowercase letter if (firstLetter.isUpperCase()) { + // If it's allowed, we don't report it + val isAllowed = function.config().getSet("allowedNames", emptySet()) + .any { + it.toRegex().matches(function.name ?: "") + } + if (isAllowed) return emitter.report(function, ComposablesThatReturnResultsShouldBeLowercase) } } else { diff --git a/rules/detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt b/rules/detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt index 423dc73e..723e6a38 100644 --- a/rules/detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt +++ b/rules/detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt @@ -3,8 +3,8 @@ package com.twitter.compose.rules.detekt import com.twitter.compose.rules.ComposeNaming -import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.SourceLocation +import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.lint import org.intellij.lang.annotations.Language @@ -12,7 +12,10 @@ import org.junit.jupiter.api.Test class ComposeNamingCheckTest { - private val rule = ComposeNamingCheck(Config.empty) + private val testConfig = TestConfig( + "allowedNames" to listOf(".*Presenter") + ) + private val rule = ComposeNamingCheck(testConfig) @Test fun `passes when a composable that returns values is lowercase`() { @@ -25,6 +28,17 @@ class ComposeNamingCheckTest { assertThat(rule.lint(code)).isEmpty() } + @Test + fun `passes when a composable that returns values is uppercase but allowed`() { + @Language("kotlin") + val code = + """ + @Composable + fun ProfilePresenter(): Something { } + """.trimIndent() + assertThat(rule.lint(code)).isEmpty() + } + @Test fun `passes when a composable that returns nothing or Unit is uppercase`() { @Language("kotlin") diff --git a/rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/EditorConfigProperties.kt b/rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/EditorConfigProperties.kt index 624c6404..c355742f 100644 --- a/rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/EditorConfigProperties.kt +++ b/rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/EditorConfigProperties.kt @@ -54,3 +54,21 @@ val previewPublicOnlyIfParams: UsesEditorConfigProperties.EditorConfigProperty = + UsesEditorConfigProperties.EditorConfigProperty( + type = PropertyType.LowerCasingPropertyType( + "twitter_compose_allowed_composable_function_names", + "A comma separated list of regexes of allowed composable fuinction names", + PropertyType.PropertyValueParser.IDENTITY_VALUE_PARSER, + emptySet() + ), + defaultValue = "", + propertyMapper = { property, _ -> + when { + property?.isUnset == true -> "" + property?.getValueAs() != null -> property.getValueAs() + else -> property?.getValueAs() + } + } + ) diff --git a/rules/ktlint/src/test/kotlin/com/twitter/compose/rules/ktlint/ComposeNamingCheckTest.kt b/rules/ktlint/src/test/kotlin/com/twitter/compose/rules/ktlint/ComposeNamingCheckTest.kt index 1bcc6477..13071a86 100644 --- a/rules/ktlint/src/test/kotlin/com/twitter/compose/rules/ktlint/ComposeNamingCheckTest.kt +++ b/rules/ktlint/src/test/kotlin/com/twitter/compose/rules/ktlint/ComposeNamingCheckTest.kt @@ -5,6 +5,7 @@ package com.twitter.compose.rules.ktlint import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule import com.pinterest.ktlint.test.LintViolation import com.twitter.compose.rules.ComposeNaming +import com.twitter.rules.core.ktlint.allowedComposeNamingNames import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Test @@ -23,6 +24,21 @@ class ComposeNamingCheckTest { namingRuleAssertThat(code).hasNoLintViolations() } + @Test + fun `passes when a composable that returns values is uppercase but allowed`() { + @Language("kotlin") + val code = + """ + @Composable + fun ProfilePresenter(): Something { } + """.trimIndent() + namingRuleAssertThat(code) + .withEditorConfigOverride( + allowedComposeNamingNames to ".*Presenter" + ) + .hasNoLintViolations() + } + @Test fun `passes when a composable that returns nothing or Unit is uppercase`() { @Language("kotlin") From 3a427abf2f42f46c1f2d5f8b68f3e46f72124f16 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Mon, 17 Oct 2022 20:46:00 -0400 Subject: [PATCH 2/3] Update rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt --- .../src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt b/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt index c11e1c3c..5ccbf693 100644 --- a/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt +++ b/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt @@ -24,7 +24,7 @@ class ComposeNaming : ComposeKtVisitor { // If it's allowed, we don't report it val isAllowed = function.config().getSet("allowedNames", emptySet()) .any { - it.toRegex().matches(function.name ?: "") + it.toRegex().matches(functionName) } if (isAllowed) return emitter.report(function, ComposablesThatReturnResultsShouldBeLowercase) From e4de34edbf53562776f02e3f8a6728fa02166eaa Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Tue, 18 Oct 2022 20:04:47 -0400 Subject: [PATCH 3/3] Consolidate naming --- .../src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt | 2 +- .../com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt | 2 +- .../com/twitter/compose/rules/ktlint/EditorConfigProperties.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt b/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt index 5ccbf693..1b59eaf3 100644 --- a/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt +++ b/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeNaming.kt @@ -22,7 +22,7 @@ class ComposeNaming : ComposeKtVisitor { // If it returns value, the composable should start with a lowercase letter if (firstLetter.isUpperCase()) { // If it's allowed, we don't report it - val isAllowed = function.config().getSet("allowedNames", emptySet()) + val isAllowed = function.config().getSet("allowedComposableFunctionNames", emptySet()) .any { it.toRegex().matches(functionName) } diff --git a/rules/detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt b/rules/detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt index 723e6a38..3a5b43b4 100644 --- a/rules/detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt +++ b/rules/detekt/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeNamingCheckTest.kt @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test class ComposeNamingCheckTest { private val testConfig = TestConfig( - "allowedNames" to listOf(".*Presenter") + "allowedComposableFunctionNames" to listOf(".*Presenter") ) private val rule = ComposeNamingCheck(testConfig) diff --git a/rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/EditorConfigProperties.kt b/rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/EditorConfigProperties.kt index c355742f..f66a911b 100644 --- a/rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/EditorConfigProperties.kt +++ b/rules/ktlint/src/main/kotlin/com/twitter/compose/rules/ktlint/EditorConfigProperties.kt @@ -59,7 +59,7 @@ val allowedComposeNamingNames: UsesEditorConfigProperties.EditorConfigProperty