Refactored NameValidator getting rid of it and replacing with a function

This commit is contained in:
Valentin Kipyatkov
2015-06-26 18:37:34 +03:00
parent 79a453f12c
commit a02c86bac9
18 changed files with 287 additions and 371 deletions
@@ -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)
@@ -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))
}
@@ -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<String>, name: String?, validator: NameValidator) {
private fun addName(result: ArrayList<String>, 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<String> {
public fun suggestNames(expression: JetExpression, validator: (String) -> Boolean, defaultName: String?): Array<String> {
val result = ArrayList<String>()
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<String> {
public fun suggestNames(type: JetType, validator: (String) -> Boolean, defaultName: String?): Array<String> {
val result = ArrayList<String>()
addNamesForType(result, type, validator)
if (result.isEmpty()) addName(result, defaultName, validator)
return ArrayUtil.toStringArray(result)
}
public fun suggestNamesForType(jetType: JetType, validator: NameValidator): Array<String> {
public fun suggestNamesForType(jetType: JetType, validator: (String) -> Boolean): Array<String> {
val result = ArrayList<String>()
addNamesForType(result, jetType, validator)
return ArrayUtil.toStringArray(result)
}
jvmOverloads public fun suggestNamesForExpression(expression: JetExpression, validator: NameValidator, defaultName: String? = null): Array<String> {
jvmOverloads public fun suggestNamesForExpression(expression: JetExpression, validator: (String) -> Boolean, defaultName: String? = null): Array<String> {
val result = ArrayList<String>()
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<String> {
public fun suggestNamesForTypeParameters(count: Int, validator: (String) -> Boolean): Array<String> {
val result = ArrayList<String>()
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<String>, jetType: JetType, validator: NameValidator) {
private fun addNamesForType(result: ArrayList<String>, 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<String>, jetType: JetType, validator: NameValidator) {
private fun addForClassType(result: ArrayList<String>, 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<String> {
public fun getCamelNames(name: String, validator: (String) -> Boolean, startLowerCase: Boolean): List<String> {
val result = ArrayList<String>()
addCamelNames(result, name, validator, startLowerCase)
return result
}
private fun addCamelNames(result: ArrayList<String>, name: String, validator: NameValidator, startLowerCase: Boolean = true) {
private fun addCamelNames(result: ArrayList<String>, 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<String>, expression: JetExpression?, validator: NameValidator) {
private fun addNamesForExpression(result: ArrayList<String>, 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<String> = 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)
}
}
@@ -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<String> = 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()
@@ -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<JetExpressio
val elementType = data(element)!!.elementType
//TODO: base on expression too
//TODO: name validation
val names = KotlinNameSuggester.suggestNames(elementType, EmptyValidator, "e")
val names = KotlinNameSuggester.suggestNames(elementType, { true }, "e")
var forExpression = JetPsiFactory(element).createExpressionByPattern("for($0 in $1) {\nx\n}", names[0], element) as JetForExpression
forExpression = element.replaced(forExpression)
@@ -31,8 +31,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.idea.JetBundle;
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
import org.jetbrains.kotlin.idea.core.NameValidator;
import org.jetbrains.kotlin.idea.core.SimpleCollectingValidator;
import org.jetbrains.kotlin.idea.core.CollectingNameValidator;
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*;
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
import org.jetbrains.kotlin.psi.JetCallElement;
@@ -130,14 +129,14 @@ public class AddFunctionParametersFix extends ChangeFunctionSignatureFix {
public Unit invoke(JetMutableMethodDescriptor descriptor) {
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
List<? extends ValueArgument> 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();
@@ -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<JetMutableMethodDescriptor, Unit>() {
@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];
@@ -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<PsiE
return true;
}
protected static String getNewArgumentName(ValueArgument argument, NameValidator validator) {
protected static String getNewArgumentName(ValueArgument argument, Function1<String, Boolean> 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<String, Boolean> validator
) {
String name = getNewArgumentName(argument, validator);
JetExpression expression = argument.getArgumentExpression();
@@ -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<JetVariableDeclaration>("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)
}
@@ -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<String, Array<String>>()
typeCandidates[parameter.typeInfo]!!.forEach { typeCandidate ->
val suggestedNames = KotlinNameSuggester.suggestNamesForType(typeCandidate.theType, EmptyValidator)
val suggestedNames = KotlinNameSuggester.suggestNamesForType(typeCandidate.theType, { true })
parameterTypeToNamesMap[typeCandidate.renderedType!!] = suggestedNames
}
@@ -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<String> by Delegates.lazy {
KotlinNameSuggester.suggestNamesForExpression(expression, EmptyValidator)
KotlinNameSuggester.suggestNamesForExpression(expression, { true })
}
override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
@@ -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()
}
}
@@ -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<JetScope> visitedScopes = new HashSet<JetScope>();
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<JetScope> visitedScopes) {
if (!(sibling instanceof JetElement)) return true;
final BindingContext context = ResolvePackage.analyze((JetElement) sibling, BodyResolveMode.FULL);
final Name identifier = Name.identifier(name);
final Ref<Boolean> result = new Ref<Boolean>(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();
}
}
@@ -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<JetScope>()
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<JetScope>): 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()
}
}
@@ -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)
@@ -744,7 +744,7 @@ private fun ExtractionData.inferParametersInfo(
}
val varNameValidator = NameValidatorImpl(
commonParent.getNonStrictParentOfType<JetExpression>(),
commonParent.getNonStrictParentOfType<JetExpression>()!!,
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<Strin
getExpressions().singleOrNull()?.let { expr ->
val property = expr.getStrictParentOfType<JetProperty>()
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)) }
}
}
@@ -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());
}
}
}
@@ -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())
}
}
}