Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow diacritics in names of classes, functions packages, and properties #1780

Merged
merged 3 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
}
}