Skip to content

Commit

Permalink
Introduce allowedNames config for ComposeNaming check (#105)
Browse files Browse the repository at this point in the history
Resolves #104
  • Loading branch information
ZacSweers committed Oct 19, 2022
1 parent fe5e58b commit be08ccd
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 3 deletions.
9 changes: 9 additions & 0 deletions docs/ktlint.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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("allowedComposableFunctionNames", emptySet())
.any {
it.toRegex().matches(functionName)
}
if (isAllowed) return
emitter.report(function, ComposablesThatReturnResultsShouldBeLowercase)
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
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
import org.junit.jupiter.api.Test

class ComposeNamingCheckTest {

private val rule = ComposeNamingCheck(Config.empty)
private val testConfig = TestConfig(
"allowedComposableFunctionNames" to listOf(".*Presenter")
)
private val rule = ComposeNamingCheck(testConfig)

@Test
fun `passes when a composable that returns values is lowercase`() {
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,21 @@ val previewPublicOnlyIfParams: UsesEditorConfigProperties.EditorConfigProperty<B
),
defaultValue = true
)

val allowedComposeNamingNames: UsesEditorConfigProperties.EditorConfigProperty<String> =
UsesEditorConfigProperties.EditorConfigProperty(
type = PropertyType.LowerCasingPropertyType(
"twitter_compose_allowed_composable_function_names",
"A comma separated list of regexes of allowed composable function names",
PropertyType.PropertyValueParser.IDENTITY_VALUE_PARSER,
emptySet()
),
defaultValue = "",
propertyMapper = { property, _ ->
when {
property?.isUnset == true -> ""
property?.getValueAs<String>() != null -> property.getValueAs<String>()
else -> property?.getValueAs()
}
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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")
Expand Down

0 comments on commit be08ccd

Please sign in to comment.