diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt index b5a291a6076..9efd2dcdf8f 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt @@ -31,7 +31,6 @@ import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings import org.jetbrains.kotlin.idea.core.getResolutionScope -import org.jetbrains.kotlin.idea.core.EmptyValidator import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.psi.JetDeclaration @@ -142,7 +141,7 @@ class ParameterNameAndTypeCompletion( ProgressManager.checkCanceled() if (suggestionsByTypesAdded.contains(type)) return // don't add suggestions for the same with longer user prefix - val nameSuggestions = KotlinNameSuggester.getCamelNames(className, EmptyValidator, userPrefix.isEmpty()) + val nameSuggestions = KotlinNameSuggester.getCamelNames(className, { true }, userPrefix.isEmpty()) for (name in nameSuggestions) { if (prefixMatcher.prefixMatches(name)) { val lookupElement = MyLookupElement.create(userPrefix, name, type, lookupElementFactory) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt index 89da1d1bf6e..64701d63691 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt @@ -28,7 +28,6 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.completion.ExpectedInfos -import org.jetbrains.kotlin.idea.core.EmptyValidator import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.application.executeCommand @@ -103,7 +102,7 @@ private fun buildTemplate(lambdaType: JetType, explicitParameterTypes: Boolean, template.addTextSegment(", ") } //TODO: check for names in scope - template.addVariable(ParameterNameExpression(KotlinNameSuggester.suggestNames(parameterType, EmptyValidator, "p")), true) + template.addVariable(ParameterNameExpression(KotlinNameSuggester.suggestNames(parameterType, { true }, "p")), true) if (explicitParameterTypes) { template.addTextSegment(": " + IdeDescriptorRenderers.SOURCE_CODE.renderType(parameterType)) } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinNameSuggester.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinNameSuggester.kt index 6976cf3110e..dfdacb7b2cf 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinNameSuggester.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinNameSuggester.kt @@ -21,7 +21,6 @@ import com.intellij.openapi.util.text.StringUtil import com.intellij.util.ArrayUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.core.NameValidator import org.jetbrains.kotlin.lexer.JetLexer import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* @@ -30,16 +29,16 @@ import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.checker.JetTypeChecker -import java.util.ArrayList +import java.util.* import java.util.regex.Pattern public object KotlinNameSuggester { - private fun addName(result: ArrayList, name: String?, validator: NameValidator) { + private fun addName(result: ArrayList, name: String?, validator: (String) -> Boolean) { var name = name ?: return if ("class" == name) name = "clazz" if (!isIdentifier(name)) return - result.add(validator.validateName(name)) + result.add(validateName(name, validator)) } /** @@ -58,7 +57,7 @@ public object KotlinNameSuggester { * * * @return possible names */ - public fun suggestNames(expression: JetExpression, validator: NameValidator, defaultName: String?): Array { + public fun suggestNames(expression: JetExpression, validator: (String) -> Boolean, defaultName: String?): Array { val result = ArrayList() val bindingContext = expression.analyze(BodyResolveMode.FULL) @@ -72,20 +71,20 @@ public object KotlinNameSuggester { return ArrayUtil.toStringArray(result) } - public fun suggestNames(type: JetType, validator: NameValidator, defaultName: String?): Array { + public fun suggestNames(type: JetType, validator: (String) -> Boolean, defaultName: String?): Array { val result = ArrayList() addNamesForType(result, type, validator) if (result.isEmpty()) addName(result, defaultName, validator) return ArrayUtil.toStringArray(result) } - public fun suggestNamesForType(jetType: JetType, validator: NameValidator): Array { + public fun suggestNamesForType(jetType: JetType, validator: (String) -> Boolean): Array { val result = ArrayList() addNamesForType(result, jetType, validator) return ArrayUtil.toStringArray(result) } - jvmOverloads public fun suggestNamesForExpression(expression: JetExpression, validator: NameValidator, defaultName: String? = null): Array { + jvmOverloads public fun suggestNamesForExpression(expression: JetExpression, validator: (String) -> Boolean, defaultName: String? = null): Array { val result = ArrayList() addNamesForExpression(result, expression, validator) if (result.isEmpty()) addName(result, defaultName, validator) @@ -94,15 +93,15 @@ public object KotlinNameSuggester { private val COMMON_TYPE_PARAMETER_NAMES = arrayOf("T", "U", "V", "W", "X", "Y", "Z") - public fun suggestNamesForTypeParameters(count: Int, validator: NameValidator): Array { + public fun suggestNamesForTypeParameters(count: Int, validator: (String) -> Boolean): Array { val result = ArrayList() for (i in 0..count - 1) { - result.add(validator.validateNameWithVariants(*COMMON_TYPE_PARAMETER_NAMES)) + result.add(validateNameWithVariants(validator, *COMMON_TYPE_PARAMETER_NAMES)) } return ArrayUtil.toStringArray(result) } - private fun addNamesForType(result: ArrayList, jetType: JetType, validator: NameValidator) { + private fun addNamesForType(result: ArrayList, jetType: JetType, validator: (String) -> Boolean) { var jetType = jetType val builtIns = KotlinBuiltIns.getInstance() val typeChecker = JetTypeChecker.DEFAULT @@ -177,7 +176,7 @@ public object KotlinNameSuggester { } } - private fun addForClassType(result: ArrayList, jetType: JetType, validator: NameValidator) { + private fun addForClassType(result: ArrayList, jetType: JetType, validator: (String) -> Boolean) { val descriptor = jetType.getConstructor().getDeclarationDescriptor() if (descriptor != null) { val className = descriptor.getName() @@ -189,13 +188,13 @@ public object KotlinNameSuggester { private val ACCESSOR_PREFIXES = arrayOf("get", "is", "set") - public fun getCamelNames(name: String, validator: NameValidator, startLowerCase: Boolean): List { + public fun getCamelNames(name: String, validator: (String) -> Boolean, startLowerCase: Boolean): List { val result = ArrayList() addCamelNames(result, name, validator, startLowerCase) return result } - private fun addCamelNames(result: ArrayList, name: String, validator: NameValidator, startLowerCase: Boolean = true) { + private fun addCamelNames(result: ArrayList, name: String, validator: (String) -> Boolean, startLowerCase: Boolean = true) { if (name === "") return var s = deleteNonLetterFromString(name) @@ -255,7 +254,7 @@ public object KotlinNameSuggester { return matcher.replaceAll("") } - private fun addNamesForExpression(result: ArrayList, expression: JetExpression?, validator: NameValidator) { + private fun addNamesForExpression(result: ArrayList, expression: JetExpression?, validator: (String) -> Boolean) { if (expression == null) return expression.accept(object : JetVisitorVoid() { @@ -294,4 +293,55 @@ public object KotlinNameSuggester { lexer.advance() return lexer.getTokenType() == null } + + /** + * Validates name, and slightly improves it by adding number to name in case of conflicts + * @param name to check it in scope + * @return name or nameI, where I is number + */ + public fun validateName(name: String, validator: (String) -> Boolean): String { + if (validator.invoke(name)) return name + var i = 1 + while (!validator.invoke(name + i)) { + ++i + } + + return name + i + } + + /** + * Validates name using set of variants which are tried in succession (and extended with suffixes if necessary) + * For example, when given sequence of a, b, c possible names are tried out in the following order: a, b, c, a1, b1, c1, a2, b2, c2, ... + * @param names to check it in scope + * @return name or nameI, where name is one of variants and I is a number + */ + public fun validateNameWithVariants(validator: (String) -> Boolean, vararg names: String): String { + var i = 0 + while (true) { + for (name in names) { + val candidate = if (i > 0) name + i else name + if (validator.invoke(candidate)) return candidate + } + i++ + } + } +} + +public class CollectingNameValidator @jvmOverloads constructor( + existingNames: Collection = Collections.emptySet(), + val filter: (String) -> Boolean = { true } +): (String) -> Boolean { + private val existingNames = HashSet(existingNames) + + override fun invoke(name: String): Boolean { + if (name !in existingNames && filter(name)) { + existingNames.add(name) + return true + } + return false + } + + public fun addName(name: String) { + existingNames.add(name) + } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/NameValidator.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/NameValidator.kt deleted file mode 100644 index fb0f8c46da8..00000000000 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/NameValidator.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.core - -import java.util.HashSet -import java.util.Collections - -public abstract class NameValidator { - /** - * Validates name, and slightly improves it by adding number to name in case of conflicts - * @param name to check it in scope - * @return name or nameI, where I is number - */ - public open fun validateName(name: String): String { - if (validateInner(name)) return name - var i = 1 - while (!validateInner(name + i)) { - ++i - } - - return name + i - } - - /** - * Validates name using set of variants which are tried in succession (and extended with suffixes if necessary) - * For example, when given sequence of a, b, c possible names are tried out in the following order: a, b, c, a1, b1, c1, a2, b2, c2, ... - * @param names to check it in scope - * @return name or nameI, where name is one of variants and I is a number - */ - public fun validateNameWithVariants(vararg names: String): String { - var i = 0 - while (true) { - for (name in names) { - val candidate = if (i > 0) name + i else name - if (validateInner(candidate)) return candidate - } - i++ - } - } - - protected abstract fun validateInner(name: String): Boolean -} - -public object EmptyValidator : NameValidator() { - override fun validateInner(name: String): Boolean = true -} - -public open class CollectingValidator( - existingNames: Collection = Collections.emptySet(), - val filter: (String) -> Boolean = { true } -): NameValidator() { - private val suggestedSet = HashSet(existingNames) - - override fun validateInner(name: String): Boolean { - if (name !in suggestedSet && filter(name)) { - suggestedSet.add(name) - return true - } - return false - } -} - -// TODO: To be used from Java -public class SimpleCollectingValidator : CollectingValidator() diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/IterateExpressionIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/IterateExpressionIntention.kt index 0f5d802db5a..ebb9d78e421 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/IterateExpressionIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/IterateExpressionIntention.kt @@ -24,7 +24,6 @@ import com.intellij.psi.PsiDocumentManager import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.core.IterableTypesDetector -import org.jetbrains.kotlin.idea.core.EmptyValidator import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers @@ -60,7 +59,7 @@ public class IterateExpressionIntention : JetSelfTargetingIntention parameters = functionDescriptor.getValueParameters(); List arguments = callElement.getValueArguments(); - NameValidator validator = new SimpleCollectingValidator(); + CollectingNameValidator validator = new CollectingNameValidator(); for (int i = 0; i < arguments.size(); i ++) { ValueArgument argument = arguments.get(i); JetExpression expression = argument.getArgumentExpression(); if (i < parameters.size()) { - validator.validateName(parameters.get(i).getName().asString()); + validator.addName(parameters.get(i).getName().asString()); JetType argumentType = expression != null ? bindingContext.getType(expression) : null; JetType parameterType = parameters.get(i).getType(); diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralSignatureFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralSignatureFix.java index 3f337d3f229..d0d44871790 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralSignatureFix.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralSignatureFix.java @@ -25,9 +25,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.idea.JetBundle; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; +import org.jetbrains.kotlin.idea.core.CollectingNameValidator; import org.jetbrains.kotlin.idea.core.KotlinNameSuggester; -import org.jetbrains.kotlin.idea.core.NameValidator; -import org.jetbrains.kotlin.idea.core.SimpleCollectingValidator; import org.jetbrains.kotlin.idea.refactoring.changeSignature.*; import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.JetFunctionLiteral; @@ -68,7 +67,7 @@ public class ChangeFunctionLiteralSignatureFix extends ChangeFunctionSignatureFi new Function1() { @Override public Unit invoke(JetMutableMethodDescriptor descriptor) { - NameValidator validator = new SimpleCollectingValidator(); + CollectingNameValidator validator = new CollectingNameValidator(); descriptor.clearNonReceiverParameters(); for (JetType type : parameterTypes) { String name = KotlinNameSuggester.INSTANCE$.suggestNames(type, validator, "param")[0]; diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.java index ef288435f43..4559cb20280 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; +import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; @@ -35,9 +36,8 @@ import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.idea.JetBundle; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde; -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil; import org.jetbrains.kotlin.idea.core.KotlinNameSuggester; -import org.jetbrains.kotlin.idea.core.NameValidator; +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil; import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo; import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetValVar; import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; @@ -97,25 +97,25 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction validator) { ValueArgumentName argumentName = argument.getArgumentName(); JetExpression expression = argument.getArgumentExpression(); if (argumentName != null) { - return validator.validateName(argumentName.getAsName().asString()); + return KotlinNameSuggester.INSTANCE$.validateName(argumentName.getAsName().asString(), validator); } else if (expression != null) { return KotlinNameSuggester.INSTANCE$.suggestNames(expression, validator, "param")[0]; } - return validator.validateName("param"); + return KotlinNameSuggester.INSTANCE$.validateName("param", validator); } protected static JetParameterInfo getNewParameterInfo( FunctionDescriptor functionDescriptor, BindingContext bindingContext, ValueArgument argument, - NameValidator validator + Function1 validator ) { String name = getNewArgumentName(argument, validator); JetExpression expression = argument.getArgumentExpression(); diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt index 2379f67f176..c04b3c09049 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt @@ -32,7 +32,6 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.core.KotlinNameSuggester -import org.jetbrains.kotlin.idea.core.NameValidator import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention import org.jetbrains.kotlin.idea.intentions.setType import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers @@ -662,9 +661,9 @@ public abstract class DeprecatedSymbolUsageFixBase( } } - fun suggestName(validator: NameValidator): Name { + fun suggestName(validator: (String) -> Boolean): Name { val name = if (nameSuggestion != null) - validator.validateName(nameSuggestion) + KotlinNameSuggester.validateName(nameSuggestion, validator) else KotlinNameSuggester.suggestNamesForExpression(value, validator, "t").first() return Name.identifier(name) @@ -690,11 +689,9 @@ public abstract class DeprecatedSymbolUsageFixBase( } } - val name = suggestName(object : NameValidator() { - override fun validateInner(name: String): Boolean { - return resolutionScope.getLocalVariable(Name.identifier(name)) == null && !isNameUsed(name) - } - }) + val name = suggestName { name -> + resolutionScope.getLocalVariable(Name.identifier(name)) == null && !isNameUsed(name) + } var declaration = psiFactory.createDeclarationByPattern("val $0 = $1", name, value) declaration = block.addBefore(declaration, expressionToBeReplaced) as JetVariableDeclaration @@ -723,9 +720,7 @@ public abstract class DeprecatedSymbolUsageFixBase( psiFactory.createExpressionByPattern("$0${dot}let { $1 }", value, expression) } else { - val name = suggestName(object : NameValidator() { - override fun validateInner(name: String) = !isNameUsed(name) - }) + val name = suggestName { !isNameUsed(it) } replaceUsages(name) psiFactory.createExpressionByPattern("$0${dot}let { $1 -> $2 }", value, name, expression) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index 9314602dde0..0f3263b6019 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt @@ -45,8 +45,7 @@ import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils -import org.jetbrains.kotlin.idea.core.CollectingValidator -import org.jetbrains.kotlin.idea.core.EmptyValidator +import org.jetbrains.kotlin.idea.core.CollectingNameValidator import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.refactoring.* import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind @@ -396,7 +395,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { SourceElement.NO_SOURCE ) - val validator = CollectingValidator { scope.getClassifier(Name.identifier(it)) == null } + val validator = CollectingNameValidator { scope.getClassifier(Name.identifier(it)) == null } val parameterNames = KotlinNameSuggester.suggestNamesForTypeParameters(typeParameterCount, validator) val typeParameters = (0..typeParameterCount - 1).map { TypeParameterDescriptorImpl.createWithDefaultBound( @@ -622,8 +621,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } } - val validator = CollectingValidator { scope.getClassifier(Name.identifier(it)) == null } - val typeParameterNames = allTypeParametersNotInScope.map { validator.validateName(it.getName().asString()) } + val validator = CollectingNameValidator { scope.getClassifier(Name.identifier(it)) == null } + val typeParameterNames = allTypeParametersNotInScope.map { KotlinNameSuggester.validateName(it.getName().asString(), validator) } return allTypeParametersNotInScope.zip(typeParameterNames).toMap() } @@ -821,7 +820,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { // figure out suggested names for each type option val parameterTypeToNamesMap = HashMap>() typeCandidates[parameter.typeInfo]!!.forEach { typeCandidate -> - val suggestedNames = KotlinNameSuggester.suggestNamesForType(typeCandidate.theType, EmptyValidator) + val suggestedNames = KotlinNameSuggester.suggestNamesForType(typeCandidate.theType, { true }) parameterTypeToNamesMap[typeCandidate.renderedType!!] = suggestedNames } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt index 8691765a232..9cb75ec9116 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder import com.intellij.psi.PsiElement import com.intellij.util.ArrayUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.idea.core.EmptyValidator import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassInfo import org.jetbrains.kotlin.idea.util.makeNotNullable @@ -44,7 +43,7 @@ abstract class TypeInfo(val variance: Variance) { class ByExpression(val expression: JetExpression, variance: Variance): TypeInfo(variance) { override val possibleNamesFromExpression: Array by Delegates.lazy { - KotlinNameSuggester.suggestNamesForExpression(expression, EmptyValidator) + KotlinNameSuggester.suggestNamesForExpression(expression, { true }) } override fun getPossibleTypes(builder: CallableBuilder): List = diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/templateExpressions.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/templateExpressions.kt index 80bbd895874..0ea087336a0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/templateExpressions.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/templateExpressions.kt @@ -27,7 +27,8 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.idea.core.CollectingValidator +import org.jetbrains.kotlin.idea.core.CollectingNameValidator +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getValueParameters @@ -93,8 +94,8 @@ private class ParameterNameExpression( } // ensure there are no conflicts - val validator = CollectingValidator(parameterNames) - return names.map { LookupElementBuilder.create(validator.validateName(it)) }.toTypedArray() + val validator = CollectingNameValidator(parameterNames) + return names.map { LookupElementBuilder.create(KotlinNameSuggester.validateName(it, validator)) }.toTypedArray() } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/NameValidatorImpl.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/NameValidatorImpl.java deleted file mode 100644 index aa7cd968953..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/NameValidatorImpl.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.refactoring; - -import com.intellij.openapi.util.Ref; -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; -import org.jetbrains.kotlin.idea.core.CorePackage; -import org.jetbrains.kotlin.idea.core.NameValidator; -import org.jetbrains.kotlin.name.Name; -import org.jetbrains.kotlin.psi.JetElement; -import org.jetbrains.kotlin.psi.JetExpression; -import org.jetbrains.kotlin.psi.JetVisitorVoid; -import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode; -import org.jetbrains.kotlin.resolve.scopes.JetScope; - -import java.util.HashSet; -import java.util.Set; - -public class NameValidatorImpl extends NameValidator { - public static enum Target { - FUNCTIONS_AND_CLASSES, - PROPERTIES - } - - private final PsiElement myContainer; - private final PsiElement myAnchor; - private final Target myTarget; - - public NameValidatorImpl(PsiElement container, PsiElement anchor, Target target) { - myContainer = container; - myAnchor = anchor; - myTarget = target; - } - - @Override - protected boolean validateInner(String name) { - Set visitedScopes = new HashSet(); - - PsiElement sibling; - if (myAnchor != null) { - sibling = myAnchor; - } - else { - if (myContainer instanceof JetExpression) { - return checkElement(name, myContainer, visitedScopes); - } - sibling = myContainer.getFirstChild(); - } - - while (sibling != null) { - if (!checkElement(name, sibling, visitedScopes)) return false; - sibling = sibling.getNextSibling(); - } - - return true; - } - - private boolean checkElement(String name, PsiElement sibling, final Set visitedScopes) { - if (!(sibling instanceof JetElement)) return true; - - final BindingContext context = ResolvePackage.analyze((JetElement) sibling, BodyResolveMode.FULL); - final Name identifier = Name.identifier(name); - - final Ref result = new Ref(true); - JetVisitorVoid visitor = new JetVisitorVoid() { - @Override - public void visitElement(@NotNull PsiElement element) { - if (result.get()) { - element.acceptChildren(this); - } - } - - @Override - public void visitExpression(@NotNull JetExpression expression) { - JetScope resolutionScope = CorePackage.getResolutionScope(expression, context, ResolvePackage.getResolutionFacade(expression)); - - if (!visitedScopes.add(resolutionScope)) return; - - boolean noConflict; - if (myTarget == Target.PROPERTIES) { - noConflict = resolutionScope.getProperties(identifier).isEmpty() - && resolutionScope.getLocalVariable(identifier) == null; - } - else { - noConflict = resolutionScope.getFunctions(identifier).isEmpty() - && resolutionScope.getClassifier(identifier) == null; - } - - if (!noConflict) { - result.set(false); - return; - } - - super.visitExpression(expression); - } - }; - sibling.accept(visitor); - return result.get(); - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/NameValidatorImpl.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/NameValidatorImpl.kt new file mode 100644 index 00000000000..5cf6dfca52c --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/NameValidatorImpl.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring + +import com.intellij.openapi.util.Ref +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.caches.resolve.* +import org.jetbrains.kotlin.idea.core.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetVisitorVoid +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.JetScope + +import java.util.HashSet + +public class NameValidatorImpl( + private val container: PsiElement, + private val anchor: PsiElement?, + private val target: NameValidatorImpl.Target +) : (String) -> Boolean { + public enum class Target { + FUNCTIONS_AND_CLASSES, + PROPERTIES + } + + override fun invoke(name: String): Boolean { + val visitedScopes = HashSet() + + var sibling: PsiElement? + if (anchor != null) { + sibling = anchor + } + else { + if (container is JetExpression) { + return checkElement(name, container, visitedScopes) + } + sibling = container.getFirstChild() + } + + while (sibling != null) { + if (!checkElement(name, sibling, visitedScopes)) return false + sibling = sibling.getNextSibling() + } + + return true + } + + private fun checkElement(name: String, sibling: PsiElement, visitedScopes: MutableSet): Boolean { + if (sibling !is JetElement) return true + + val context = sibling.analyze(BodyResolveMode.FULL) + val identifier = Name.identifier(name) + + val result = Ref(true) + val visitor = object : JetVisitorVoid() { + override fun visitElement(element: PsiElement) { + if (result.get()) { + element.acceptChildren(this) + } + } + + override fun visitExpression(expression: JetExpression) { + val resolutionScope = expression.getResolutionScope(context, expression.getResolutionFacade()) + + if (!visitedScopes.add(resolutionScope)) return + + val noConflict: Boolean + if (target === Target.PROPERTIES) { + noConflict = resolutionScope.getProperties(identifier).isEmpty() && resolutionScope.getLocalVariable(identifier) == null + } + else { + noConflict = resolutionScope.getFunctions(identifier).isEmpty() && resolutionScope.getClassifier(identifier) == null + } + + if (!noConflict) { + result.set(false) + return + } + + super.visitExpression(expression) + } + } + sibling.accept(visitor) + return result.get() + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureData.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureData.kt index 434c1a87ac3..0c5bcac467e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureData.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureData.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.core.CollectingValidator +import org.jetbrains.kotlin.idea.core.CollectingNameValidator import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JetFunctionDefinitionUsage import org.jetbrains.kotlin.name.Name @@ -76,11 +76,11 @@ public class JetChangeSignatureData( val bodyScope = function.getBodyExpression()?.let { it.analyze()[BindingContext.RESOLUTION_SCOPE, it] } val paramNames = baseDescriptor.getValueParameters().map { it.getName().asString() } val validator = bodyScope?.let { bodyScope -> - CollectingValidator(paramNames) { + CollectingNameValidator(paramNames) { val name = Name.identifier(it) bodyScope.getLocalVariable(name) == null && bodyScope.getProperties(name).isEmpty() } - } ?: CollectingValidator(paramNames) + } ?: CollectingNameValidator(paramNames) val receiverType = baseDescriptor.getExtensionReceiverParameter()?.getType() ?: return null val receiverName = KotlinNameSuggester.suggestNames(receiverType, validator, "receiver").first() return JetParameterInfo(functionDescriptor = baseDescriptor, name = receiverName, type = receiverType) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index f252685a4a9..c07cbe44c9d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -744,7 +744,7 @@ private fun ExtractionData.inferParametersInfo( } val varNameValidator = NameValidatorImpl( - commonParent.getNonStrictParentOfType(), + commonParent.getNonStrictParentOfType()!!, originalElements.firstOrNull(), NameValidatorImpl.Target.PROPERTIES ) @@ -758,7 +758,7 @@ private fun ExtractionData.inferParametersInfo( if (currentName == null) { currentName = KotlinNameSuggester.suggestNames(getParameterType(options.allowSpecialClassNames), varNameValidator, "p").first() } - mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors) varNameValidator.validateName(name) else null + mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors) KotlinNameSuggester.validateName(name, varNameValidator) else null info.parameters.add(this) } } @@ -921,7 +921,7 @@ private fun ExtractionData.suggestFunctionNames(returnType: JetType): List val property = expr.getStrictParentOfType() if (property?.getInitializer() == expr) { - property?.getName()?.let { functionNames.add(validator.validateName("get" + it.capitalize())) } + property?.getName()?.let { functionNames.add(KotlinNameSuggester.validateName("get" + it.capitalize(), validator)) } } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/JetNameSuggesterTest.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/JetNameSuggesterTest.java deleted file mode 100644 index 60a4f5fa3f5..00000000000 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/JetNameSuggesterTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.refactoring.nameSuggester; - -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; -import org.jetbrains.kotlin.idea.core.EmptyValidator; -import org.jetbrains.kotlin.idea.core.KotlinNameSuggester; -import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil; -import org.jetbrains.kotlin.psi.JetExpression; -import org.jetbrains.kotlin.psi.JetFile; -import org.jetbrains.kotlin.test.JetTestUtils; - -import java.util.Arrays; - -public class JetNameSuggesterTest extends LightCodeInsightFixtureTestCase { - public void testArrayList() { - doTest(); - } - - public void testGetterSure() { - doTest(); - } - - public void testNameArrayOfClasses() { - doTest(); - } - - public void testNameArrayOfStrings() { - doTest(); - } - - public void testNamePrimitiveArray() { - doTest(); - } - - public void testNameCallExpression() { - doTest(); - } - - public void testNameClassCamelHump() { - doTest(); - } - - public void testNameLong() { - doTest(); - } - - public void testNameReferenceExpression() { - doTest(); - } - - public void testNameString() { - doTest(); - } - - public void testAnonymousObject() { - doTest(); - } - - public void testAnonymousObjectWithSuper() { - doTest(); - } - - public void testArrayOfObjectsType() { - doTest(); - } - - public void testURL() { - doTest(); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/refactoring/nameSuggester"); - } - - private void doTest() { - myFixture.configureByFile(getTestName(false) + ".kt"); - JetFile file = (JetFile) myFixture.getFile(); - final String expectedResultText = JetTestUtils.getLastCommentInFile(file); - try { - JetRefactoringUtil.selectExpression(myFixture.getEditor(), file, new JetRefactoringUtil.SelectExpressionCallback() { - @Override - public void run(@Nullable JetExpression expression) { - String[] names = KotlinNameSuggester.INSTANCE$.suggestNames(expression, EmptyValidator.INSTANCE$, "value"); - Arrays.sort(names); - String result = StringUtil.join(names, "\n").trim(); - assertEquals(expectedResultText, result); - } - }); - } catch (JetRefactoringUtil.IntroduceRefactoringException e) { - throw new AssertionError("Failed to find expression: " + e.getMessage()); - } - - } -} diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/JetNameSuggesterTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/JetNameSuggesterTest.kt new file mode 100644 index 00000000000..848a30579d7 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/nameSuggester/JetNameSuggesterTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.nameSuggester + +import com.intellij.openapi.util.text.StringUtil +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.test.JetTestUtils + +import java.util.Arrays +import kotlin.test.assertEquals + +public class JetNameSuggesterTest : LightCodeInsightFixtureTestCase() { + public fun testArrayList() { doTest() } + + public fun testGetterSure() { doTest() } + + public fun testNameArrayOfClasses() { doTest() } + + public fun testNameArrayOfStrings() { doTest() } + + public fun testNamePrimitiveArray() { doTest() } + + public fun testNameCallExpression() { doTest() } + + public fun testNameClassCamelHump() { doTest() } + + public fun testNameLong() { doTest() } + + public fun testNameReferenceExpression() { doTest() } + + public fun testNameString() { doTest() } + + public fun testAnonymousObject() { doTest() } + + public fun testAnonymousObjectWithSuper() { doTest() } + + public fun testArrayOfObjectsType() { doTest() } + + public fun testURL() { doTest() } + + override fun setUp() { + super.setUp() + myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/refactoring/nameSuggester") + } + + private fun doTest() { + myFixture.configureByFile(getTestName(false) + ".kt") + val file = myFixture.getFile() as JetFile + val expectedResultText = JetTestUtils.getLastCommentInFile(file) + try { + JetRefactoringUtil.selectExpression(myFixture.getEditor(), file, object : JetRefactoringUtil.SelectExpressionCallback { + override fun run(expression: JetExpression) { + val names = KotlinNameSuggester.suggestNames(expression, { true }, "value") + Arrays.sort(names) + val result = StringUtil.join(names, "\n").trim() + assertEquals(expectedResultText, result) + } + }) + } + catch (e: JetRefactoringUtil.IntroduceRefactoringException) { + throw AssertionError("Failed to find expression: " + e.getMessage()) + } + } +}