Skip to content

Commit

Permalink
Allow diacritics in names of classes, functions packages, and propert…
Browse files Browse the repository at this point in the history
…ies (#1780)

Closes #1757
  • Loading branch information
paul-dingemans authored Jan 16, 2023
1 parent 1bf9c46 commit 881042a
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ If you do want to ignore the value of `ktlint_experimental` as set by the user,
* Fix with array-syntax annotations on the same line as other annotations `annotation` ([#1765](https://github.com/pinterest/ktlint/issues/1765))
* Do not enable the experimental rules by default when `.editorconfig` properties `disabled_rules` or `ktlint_disabled_rules` are set. ([#1771](https://github.com/pinterest/ktlint/issues/1771))
* A function signature not having any parameters which exceeds the `max-line-length` should be ignored by rule `function-signature` ([#1773](https://github.com/pinterest/ktlint/issues/1773))
* Allow diacritics in names of classes, functions packages, and properties `class-naming`, `function-naming`, `package-name`, `property-naming` ([#1757](https://github.com/pinterest/ktlint/issues/1757))
* Prevent violation of `file-name` rule on code snippets ([#1768](https://github.com/pinterest/ktlint/issues/1768))
* Clarify that API Consumers have to enable experimental rules ([#1768](https://github.com/pinterest/ktlint/issues/1768))
* Trim spaces in the `.editorconfig` property `ij_kotlin_imports_layout`'s entries ([#1770](https://github.com/pinterest/ktlint/pull/1770))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
import com.pinterest.ktlint.core.ast.ElementType.OBJECT_DECLARATION
import com.pinterest.ktlint.ruleset.experimental.internal.regExIgnoringDiacriticsAndStrokesOnLetters
import org.jetbrains.kotlin.com.intellij.lang.ASTNode

/**
Expand Down Expand Up @@ -51,7 +52,7 @@ public class ClassNamingRule : Rule("$EXPERIMENTAL_RULE_SET_ID:class-naming") {
text.matches(BACK_TICKED_FUNCTION_NAME_REGEXP)

private companion object {
val VALID_CLASS_NAME_REGEXP = Regex("[A-Z][A-Za-z\\d]*")
val VALID_CLASS_NAME_REGEXP = "[A-Z][A-Za-z\\d]*".regExIgnoringDiacriticsAndStrokesOnLetters()
val BACK_TICKED_FUNCTION_NAME_REGEXP = Regex("`.*`")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.FUN
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
import com.pinterest.ktlint.core.ast.ElementType.IMPORT_DIRECTIVE
import com.pinterest.ktlint.ruleset.experimental.internal.regExIgnoringDiacriticsAndStrokesOnLetters
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtImportDirective
Expand Down Expand Up @@ -68,8 +69,8 @@ public class FunctionNamingRule : Rule("$EXPERIMENTAL_RULE_SET_ID:function-namin
.matches(VALID_FUNCTION_NAME_REGEXP)

private companion object {
val VALID_FUNCTION_NAME_REGEXP = Regex("[a-z][A-Za-z\\d]*")
val VALID_TEST_FUNCTION_NAME_REGEXP = Regex("(`.*`)|([a-z][A-Za-z\\d_]*)")
val VALID_FUNCTION_NAME_REGEXP = "[a-z][A-Za-z\\d]*".regExIgnoringDiacriticsAndStrokesOnLetters()
val VALID_TEST_FUNCTION_NAME_REGEXP = "(`.*`)|([a-z][A-Za-z\\d_]*)".regExIgnoringDiacriticsAndStrokesOnLetters()
private const val KOTLIN_TEST = "kotlin.test"
private const val ORG_JUNIT = "org.junit"
private const val ORG_TESTNG = "org.testng"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.pinterest.ktlint.core.ast.ElementType.PROPERTY
import com.pinterest.ktlint.core.ast.ElementType.PROPERTY_ACCESSOR
import com.pinterest.ktlint.core.ast.ElementType.VAL_KEYWORD
import com.pinterest.ktlint.core.ast.children
import com.pinterest.ktlint.ruleset.experimental.internal.regExIgnoringDiacriticsAndStrokesOnLetters
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType

Expand Down Expand Up @@ -136,8 +137,8 @@ public class PropertyNamingRule : Rule("$EXPERIMENTAL_RULE_SET_ID:property-namin
.any { it.text == identifier }

private companion object {
val LOWER_CAMEL_CASE_REGEXP = Regex("[a-z][a-zA-Z0-9]*")
val SCREAMING_SNAKE_CASE_REGEXP = Regex("[A-Z][_A-Z0-9]*")
val BACKING_PROPERTY_LOWER_CAMEL_CASE_REGEXP = Regex("_[a-z][a-zA-Z0-9]*")
val LOWER_CAMEL_CASE_REGEXP = "[a-z][a-zA-Z0-9]*".regExIgnoringDiacriticsAndStrokesOnLetters()
val SCREAMING_SNAKE_CASE_REGEXP = "[A-Z][_A-Z0-9]*".regExIgnoringDiacriticsAndStrokesOnLetters()
val BACKING_PROPERTY_LOWER_CAMEL_CASE_REGEXP = "_[a-z][a-zA-Z0-9]*".regExIgnoringDiacriticsAndStrokesOnLetters()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.pinterest.ktlint.ruleset.experimental.internal

/**
* Transforms a string containing regular expression ranges like "A-Z" and "a-z" to a RegEx which checks whether a
* unicode character has an uppercase versus a lowercase mapping to a letter. This function intents to keep the original
* expression more readable
*/
internal fun String.regExIgnoringDiacriticsAndStrokesOnLetters() =
replace("A-Z", "\\p{Lu}")
.replace("a-z", "\\p{Ll}")
.toRegex()
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,13 @@ class ClassNamingRuleTest {
classNamingRuleAssertThat(code).hasNoLintViolations()
}
}

@Test
fun `Issue 1757 - Given a class name containing diacritics is allowed`() {
val code =
"""
class ŸèśThîsIsAllowed123
""".trimIndent()
classNamingRuleAssertThat(code).hasNoLintViolations()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,39 @@ class FunctionNamingRuleTest {
""".trimIndent()
functionNamingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Issue 1757 - Given a function name containing diacritics then do not report a violation on the diacritics`() {
val code =
"""
fun ÿèśThîsIsAllowed123() = "foo"
""".trimIndent()
functionNamingRuleAssertThat(code).hasNoLintViolations()
}

@ParameterizedTest(name = "Junit import: {0}")
@ValueSource(
strings = [
"org.junit.jupiter.api.Test",
"org.junit.jupiter.api.*",
"org.junit.jupiter.*",
"org.junit.*",
"kotlin.test.*",
"org.testng.*",
],
)
fun `Given file which imports a class from the JUnit Jupiter then do not emit`(
import: String,
) {
val code =
"""
import $import
class FunTest {
@Test
fun `Ÿèś thîs is allowed 123`() {}
}
""".trimIndent()
functionNamingRuleAssertThat(code).hasNoLintViolations()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class PropertyNamingRuleTest {
"""
const val foo = "foo"
const val FOO_BAR_2 = "foo-bar-2"
const val ŸÈŠ_THÎS_IS_ALLOWED_123 = "Yes this is allowed"
""".trimIndent()
propertyNamingRuleAssertThat(code)
.hasLintViolationWithoutAutoCorrect(1, 11, "Property name should use the screaming snake case notation when the value can not be changed")
Expand Down Expand Up @@ -106,9 +107,23 @@ class PropertyNamingRuleTest {
val code =
"""
class Foo {
private val _element2List = mutableListOf<Element>()
private val _elementList = mutableListOf<Element>()
val element2List: List<Element>
val elementList: List<Element>
get() = _elementList
}
""".trimIndent()
propertyNamingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given a backing val property name containing diacritics having a custom get function and not in screaming case notation then do not emit`() {
val code =
"""
class Foo {
private val _elementŁîšt = mutableListOf<Element>()
val elementŁîšt: List<Element>
get() = _elementList
}
""".trimIndent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.PACKAGE_DIRECTIVE
import com.pinterest.ktlint.core.ast.nextCodeSibling
import com.pinterest.ktlint.ruleset.standard.internal.regExIgnoringDiacriticsAndStrokesOnLetters
import org.jetbrains.kotlin.com.intellij.lang.ASTNode

/**
Expand Down Expand Up @@ -33,6 +34,6 @@ public class PackageNameRule : Rule("package-name") {
}

private companion object {
val VALID_PACKAGE_NAME_REGEXP = Regex("[a-z_][a-zA-Z\\d_]*(\\.[a-z_][a-zA-Z\\d_]*)*")
val VALID_PACKAGE_NAME_REGEXP = "[a-z_][a-zA-Z\\d_]*(\\.[a-z_][a-zA-Z\\d_]*)*".regExIgnoringDiacriticsAndStrokesOnLetters()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,13 @@ class PackageNameRuleTest {
""".trimIndent()
packageNameRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Issue 1757 - Given a package name containing diacritics then do no report a violation`() {
val code =
"""
package ÿèś.thîs.can.be.used
""".trimIndent()
packageNameRuleAssertThat(code).hasNoLintViolations()
}
}

0 comments on commit 881042a

Please sign in to comment.