From 213b98fefe86b822063450c8432770cd1f97dfe9 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Mon, 11 Mar 2019 15:45:12 +0300 Subject: [PATCH] Review changes: revert compiler behaviour, add test, remove inner classes --- .../load/java/propertiesConventionUtil.kt | 3 +- .../kotlin/utils/capitalizeDecapitalize.kt | 121 ++++++++++-------- .../completion/ReferenceVariantsCollector.kt | 3 +- .../NameReferenceExpressionForConstants.kt | 9 ++ .../nameSuggester/KotlinNameSuggesterTest.kt | 2 + 5 files changed, 83 insertions(+), 55 deletions(-) create mode 100644 idea/testData/refactoring/nameSuggester/NameReferenceExpressionForConstants.kt diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/propertiesConventionUtil.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/propertiesConventionUtil.kt index 595c653b6bc..6b34ddd8b62 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/propertiesConventionUtil.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/propertiesConventionUtil.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.java import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getPropertyNameCandidatesBySpecialGetterName +import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmartForCompiler fun propertyNameByGetMethodName(methodName: Name): Name? @@ -43,7 +44,7 @@ private fun propertyNameFromAccessorMethodName(methodName: Name, prefix: String, } if (!removePrefix) return methodName - val name = identifier.removePrefix(prefix).decapitalizeSmart(asciiOnly = true) + val name = identifier.removePrefix(prefix).decapitalizeSmartForCompiler(asciiOnly = true) if (!Name.isValidIdentifier(name)) return null return Name.identifier(name) } diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/capitalizeDecapitalize.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/capitalizeDecapitalize.kt index 42da285e0f0..a986d531d97 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/capitalizeDecapitalize.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/capitalizeDecapitalize.kt @@ -14,8 +14,28 @@ * limitations under the License. */ +@file:Suppress("PackageDirectoryMismatch") + package org.jetbrains.kotlin.util.capitalizeDecapitalize +/** + * "FooBar" -> "fooBar" + * "FOOBar" -> "fooBar" + * "FOO" -> "foo" + * "FOO_BAR" -> "foO_BAR" + */ +fun String.decapitalizeSmartForCompiler(asciiOnly: Boolean = false): String { + if (isEmpty() || !isUpperCaseCharAt(0, asciiOnly)) return this + + if (length == 1 || !isUpperCaseCharAt(1, asciiOnly)) { + return if (asciiOnly) decapitalizeAsciiOnly() else decapitalize() + } + + val secondWordStart = (indices.firstOrNull { !isUpperCaseCharAt(it, asciiOnly) } ?: return toLowerCase(this, asciiOnly)) - 1 + + return toLowerCase(substring(0, secondWordStart), asciiOnly) + substring(secondWordStart) +} + /** * "FooBar" -> "fooBar" * "FOOBar" -> "fooBar" @@ -24,52 +44,8 @@ package org.jetbrains.kotlin.util.capitalizeDecapitalize * "__F_BAR" -> "fBar" */ fun String.decapitalizeSmart(asciiOnly: Boolean = false): String { - fun isUpperCaseCharAt(index: Int): Boolean { - val c = this[index] - return if (asciiOnly) c in 'A'..'Z' else c.isUpperCase() - } - - /** - * FOOBAR -> null - * FOO_BAR -> "fooBar" - * FOO_BAR_BAZ -> "fooBarBaz" - * "__F_BAR" -> "fBar" - * "F_BAR" -> "fBar" - */ - fun decapitalizeConst(str: String): String? { - val words = str.split("_").filter { it.isNotEmpty() } - - if (words.size <= 1) return null - - val builder = StringBuilder() - - words.forEachIndexed { index, word -> - if (index == 0) { - builder.append(word.toLowerCase()) - } else { - builder.append(word.first().toUpperCase()) - builder.append(word.drop(1).toLowerCase()) - } - } - - return builder.toString() - } - - val constant = decapitalizeConst(this) - - if (constant != null) return constant - - if (isEmpty() || !isUpperCaseCharAt(0)) return this - - if (length == 1 || !isUpperCaseCharAt(1)) { - return if (asciiOnly) decapitalizeAsciiOnly() else decapitalize() - } - - fun toLowerCase(string: String) = if (asciiOnly) string.toLowerCaseAsciiOnly() else string.toLowerCase() - - val secondWordStart = (indices.firstOrNull { !isUpperCaseCharAt(it) } - ?: return toLowerCase(this)) - 1 - return toLowerCase(substring(0, secondWordStart)) + substring(secondWordStart) + return decapitalizeWithUnderscores(this, asciiOnly) + ?: decapitalizeSmartForCompiler(asciiOnly) } /** @@ -78,16 +54,55 @@ fun String.decapitalizeSmart(asciiOnly: Boolean = false): String { * "foo" -> "FOO" */ fun String.capitalizeFirstWord(asciiOnly: Boolean = false): String { - fun toUpperCase(string: String) = if (asciiOnly) string.toUpperCaseAsciiOnly() else string.toUpperCase() + val secondWordStart = indices.drop(1).firstOrNull { !isLowerCaseCharAt(it, asciiOnly) } + ?: return toUpperCase(this, asciiOnly) - fun isLowerCaseCharAt(index: Int): Boolean { - val c = this[index] - return if (asciiOnly) c in 'a'..'z' else c.isLowerCase() + return toUpperCase(substring(0, secondWordStart), asciiOnly) + substring(secondWordStart) +} + +/** + * FOOBAR -> null + * FOO_BAR -> "fooBar" + * FOO_BAR_BAZ -> "fooBarBaz" + * "__F_BAR" -> "fBar" + * "_F_BAR" -> "fBar" + * "F_BAR" -> "fBar" + */ +private fun decapitalizeWithUnderscores(str: String, asciiOnly: Boolean): String? { + val words = str.split("_").filter { it.isNotEmpty() } + + if (words.size <= 1) return null + + val builder = StringBuilder() + + words.forEachIndexed { index, word -> + if (index == 0) { + builder.append(toLowerCase(word, asciiOnly)) + } else { + builder.append(toUpperCase(word.first().toString(), asciiOnly)) + builder.append(toLowerCase(word.drop(1), asciiOnly)) + } } - val secondWordStart = indices.drop(1).firstOrNull { !isLowerCaseCharAt(it) } - ?: return toUpperCase(this) - return toUpperCase(substring(0, secondWordStart)) + substring(secondWordStart) + return builder.toString() +} + +private fun String.isUpperCaseCharAt(index: Int, asciiOnly: Boolean): Boolean { + val c = this[index] + return if (asciiOnly) c in 'A'..'Z' else c.isUpperCase() +} + +private fun String.isLowerCaseCharAt(index: Int, asciiOnly: Boolean): Boolean { + val c = this[index] + return if (asciiOnly) c in 'a'..'z' else c.isLowerCase() +} + +private fun toLowerCase(string: String, asciiOnly: Boolean): String { + return if (asciiOnly) string.toLowerCaseAsciiOnly() else string.toLowerCase() +} + +private fun toUpperCase(string: String, asciiOnly: Boolean): String { + return if (asciiOnly) string.toUpperCaseAsciiOnly() else string.toUpperCase() } fun String.capitalizeAsciiOnly(): String { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt index 35e747d9053..56c90f7a0d3 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart +import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmartForCompiler import java.util.* data class ReferenceVariants(val imported: Collection, val notImportedExtensions: Collection) @@ -121,7 +122,7 @@ class ReferenceVariantsCollector( val getOrSetPrefix = GET_SET_PREFIXES.firstOrNull { prefix.startsWith(it) } val additionalPropertyNameFilter: ((String) -> Boolean)? = getOrSetPrefix - ?.let { prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmart()).asStringNameFilter() } + ?.let { prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmartForCompiler()).asStringNameFilter() } val shadowedDeclarationsFilter = if (runtimeReceiver != null) ShadowedDeclarationsFilter(bindingContext, resolutionFacade, nameExpression, runtimeReceiver) diff --git a/idea/testData/refactoring/nameSuggester/NameReferenceExpressionForConstants.kt b/idea/testData/refactoring/nameSuggester/NameReferenceExpressionForConstants.kt new file mode 100644 index 00000000000..a432f370af8 --- /dev/null +++ b/idea/testData/refactoring/nameSuggester/NameReferenceExpressionForConstants.kt @@ -0,0 +1,9 @@ +fun a() { + val SOME_CONSTANT = 1 + SOME_CONSTANT +} +/* +constant +i +someConstant +*/ \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/KotlinNameSuggesterTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/KotlinNameSuggesterTest.kt index 2afc9c62bcc..89a8a16d254 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/KotlinNameSuggesterTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/KotlinNameSuggesterTest.kt @@ -38,6 +38,8 @@ class KotlinNameSuggesterTest : KotlinLightCodeInsightFixtureTestCase() { fun testNameReferenceExpression() = doTest() + fun testNameReferenceExpressionForConstants() = doTest() + fun testNameString() = doTest() fun testAnonymousObject() = doTest()