diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index ea639d0056d..7be13575f6c 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.idea.completion import com.intellij.codeInsight.completion.* +import com.intellij.codeInsight.completion.impl.CamelHumpMatcher import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.patterns.ElementPattern @@ -119,11 +120,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess kotlinIdentifierPartPattern or singleCharPattern('@'), kotlinIdentifierStartPattern) - protected val resultSet: CompletionResultSet = resultSet - .withPrefixMatcher(prefix) - .addKotlinSorting(parameters) - - protected val prefixMatcher: PrefixMatcher = this.resultSet.getPrefixMatcher() + protected val prefixMatcher: PrefixMatcher = CamelHumpMatcher(prefix) protected val isVisibleFilter: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it) } @@ -157,7 +154,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess else LookupElementsCollector.Context.NORMAL - protected val collector: LookupElementsCollector = LookupElementsCollector(prefixMatcher, parameters, resolutionFacade, lookupElementFactory, inDescriptor, collectorContext) + protected val collector: LookupElementsCollector = LookupElementsCollector(prefixMatcher, parameters, resultSet, resolutionFacade, lookupElementFactory, inDescriptor, collectorContext) protected val originalSearchScope: GlobalSearchScope = ResolutionFacade.getResolveScope(parameters.getOriginalFile() as JetFile) @@ -181,7 +178,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess } protected fun flushToResultSet() { - collector.flushToResultSet(resultSet) + collector.flushToResultSet() } public fun complete(): Boolean { @@ -399,11 +396,12 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, } if (completionKind != CompletionKind.KEYWORDS_ONLY) { + flushToResultSet() + if (!configuration.completeNonImportedDeclarations && isNoQualifierContext()) { - JavaCompletionContributor.advertiseSecondCompletion(project, resultSet) + collector.advertiseSecondCompletion() } - flushToResultSet() addNonImported(completionKind) if (position.getContainingFile() is JetCodeFragment) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt index ac3de4ad93a..f45ae823b6d 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt @@ -80,7 +80,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters, .filter { it.getName().asString() !in documentedParameters } descriptors.forEach { - resultSet.addElement(lookupElementFactory.createLookupElement(it, false)) + collector.addElement(lookupElementFactory.createLookupElement(it, false)) } } @@ -88,7 +88,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters, val scope = getResolutionScope(resolutionFacade, declarationDescriptor) scope.getDescriptors(nameFilter = prefixMatcher.asNameFilter()).forEach { val element = lookupElementFactory.createLookupElement(it, false) - resultSet.addElement(object: LookupElementDecorator(element) { + collector.addElement(object: LookupElementDecorator(element) { override fun handleInsert(context: InsertionContext?) { // insert only plain name here, no qualifier/parentheses/etc. } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt index 551777abb4d..aa4c2291db0 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.idea.completion -import com.intellij.codeInsight.completion.CompletionParameters -import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.codeInsight.completion.InsertionContext -import com.intellij.codeInsight.completion.PrefixMatcher +import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.lookup.LookupElementPresentation @@ -32,10 +29,12 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.completion.handlers.* import java.util.ArrayList +import java.util.LinkedHashMap class LookupElementsCollector( - private val prefixMatcher: PrefixMatcher, + private val defaultPrefixMatcher: PrefixMatcher, private val completionParameters: CompletionParameters, + resultSet: CompletionResultSet, private val resolutionFacade: ResolutionFacade, private val lookupElementFactory: LookupElementFactory, private val inDescriptor: DeclarationDescriptor?, @@ -47,11 +46,21 @@ class LookupElementsCollector( INFIX_CALL } - private val elements = ArrayList() + private val elements = LinkedHashMap>() - public fun flushToResultSet(resultSet: CompletionResultSet) { + private val defaultResultSet = resultSet + .withPrefixMatcher(defaultPrefixMatcher) + .addKotlinSorting(completionParameters) + + public fun flushToResultSet() { if (!elements.isEmpty()) { - resultSet.addAllElements(elements) + for ((prefixMatcher, elements) in elements) { + val resultSet = if (prefixMatcher == defaultPrefixMatcher) + defaultResultSet + else + defaultResultSet.withPrefixMatcher(prefixMatcher) + resultSet.addAllElements(elements) + } elements.clear() isResultEmpty = false } @@ -137,7 +146,7 @@ class LookupElementsCollector( } } - public fun addElement(element: LookupElement) { + public fun addElement(element: LookupElement, prefixMatcher: PrefixMatcher = defaultPrefixMatcher) { if (prefixMatcher.prefixMatches(element)) { val decorated = object : LookupElementDecorator(element) { override fun handleInsert(context: InsertionContext) { @@ -158,10 +167,12 @@ class LookupElementsCollector( } } + if (suppressItemSelectionByCharsOnTyping) { decorated.putUserData(KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING, Unit) } - elements.add(decorated) + + elements.getOrPut(prefixMatcher) { ArrayList() }.add(decorated) } } @@ -184,4 +195,8 @@ class LookupElementsCollector( public fun addElements(elements: Iterable) { elements.forEach { addElement(it) } } + + public fun advertiseSecondCompletion() { + JavaCompletionContributor.advertiseSecondCompletion(completionParameters.getOriginalFile().getProject(), defaultResultSet) + } } 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 842b7c9fa55..bb344783fb7 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 @@ -20,11 +20,13 @@ import com.intellij.codeInsight.completion.CompletionInitializationContext import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.codeInsight.completion.InsertionContext import com.intellij.codeInsight.completion.PrefixMatcher +import com.intellij.codeInsight.completion.impl.CamelHumpMatcher import com.intellij.codeInsight.lookup.* import com.intellij.openapi.util.Key import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.codeStyle.CodeStyleSettingsManager +import com.intellij.psi.codeStyle.NameUtil import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -49,21 +51,42 @@ import java.util.* class ParameterNameAndTypeCompletion( private val collector: LookupElementsCollector, private val lookupElementFactory: LookupElementFactory, - private val prefixMatcher: PrefixMatcher, + defaultPrefixMatcher: PrefixMatcher, private val resolutionFacade: ResolutionFacade ) { - private val modifiedPrefixMatcher = prefixMatcher.cloneWithPrefix(prefixMatcher.getPrefix().capitalize()) + private val parametersInCurrentFilePrefixMatcher = MyPrefixMatcher(defaultPrefixMatcher.getPrefix()) + + private val prefixWords: Array + private val allPrefixes: List // prefixes to use to generate parameter names from class names + + init { + val prefix = defaultPrefixMatcher.getPrefix() + prefixWords = NameUtil.splitNameIntoWords(prefix) + + allPrefixes = if (prefix.isEmpty() || prefix[0].isUpperCase()) + emptyList() + else + prefixWords.indices.map { if (it == 0) prefix else prefixWords.drop(it).join("") } + } + + private val allPrefixMatchers = allPrefixes.map { MyPrefixMatcher(it) } + + private val userPrefixes = allPrefixes.indices.map { prefixWords.take(it).join("") } + + private val suggestionsByTypesAdded = HashSet() public fun addFromImportedClasses(position: PsiElement, bindingContext: BindingContext, visibilityFilter: (DeclarationDescriptor) -> Boolean) { - if (prefixMatcher.getPrefix().isEmpty()) return + for ((i, prefixMatcher) in allPrefixMatchers.withIndex()) { + val resolutionScope = position.getResolutionScope(bindingContext) + val classifiers = resolutionScope.getDescriptorsFiltered(DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS, prefixMatcher.toClassifierNamePrefixMatcher().asNameFilter()) - val resolutionScope = position.getResolutionScope(bindingContext) - val classifiers = resolutionScope.getDescriptorsFiltered(DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS, modifiedPrefixMatcher.asNameFilter()) - - for (classifier in classifiers) { - if (visibilityFilter(classifier)) { - addSuggestionsForClassifier(classifier) + for (classifier in classifiers) { + if (visibilityFilter(classifier)) { + addSuggestionsForClassifier(classifier, userPrefixes[i], prefixMatcher) + } } + + collector.flushToResultSet() } } @@ -89,24 +112,31 @@ class ParameterNameAndTypeCompletion( } public fun addFromAllClasses(parameters: CompletionParameters, indicesHelper: KotlinIndicesHelper) { - if (prefixMatcher.getPrefix().isEmpty()) return + for ((i, prefixMatcher) in allPrefixMatchers.withIndex()) { + AllClassesCompletion(parameters, indicesHelper, prefixMatcher.toClassifierNamePrefixMatcher(), { !it.isSingleton() }) + .collect( + { addSuggestionsForClassifier(it, userPrefixes[i], prefixMatcher) }, + { addSuggestionsForJavaClass(it, userPrefixes[i], prefixMatcher) } + ) - AllClassesCompletion(parameters, indicesHelper, modifiedPrefixMatcher, { !it.isSingleton() }) - .collect({ addSuggestionsForClassifier(it) }, { addSuggestionsForJavaClass(it) }) + collector.flushToResultSet() + } } + private fun PrefixMatcher.toClassifierNamePrefixMatcher() = cloneWithPrefix(getPrefix().capitalize()) + public fun addFromParametersInFile(position: PsiElement, resolutionFacade: ResolutionFacade, visibilityFilter: (DeclarationDescriptor) -> Boolean) { val lookupElementToCount = LinkedHashMap() position.getContainingFile().forEachDescendantOfType( canGoInside = { it !is JetExpression || it is JetDeclaration } // we analyze parameters inside bodies to not resolve too much ) { declaration -> val name = declaration.getName() - if (name != null && prefixMatcher.prefixMatches(name)) { + if (name != null && parametersInCurrentFilePrefixMatcher.prefixMatches(name)) { val parameter = resolutionFacade.analyze(declaration)[BindingContext.VALUE_PARAMETER, declaration] if (parameter != null) { val parameterType = parameter.getType() if (parameterType.isVisible(visibilityFilter)) { - val lookupElement = MyLookupElement.create(NameAndArbitraryType(name, parameterType), lookupElementFactory) + val lookupElement = MyLookupElement.create("", name, ArbitraryType(parameterType), lookupElementFactory) val count = lookupElementToCount[lookupElement] ?: 0 lookupElementToCount[lookupElement] = count + 1 } @@ -116,25 +146,29 @@ class ParameterNameAndTypeCompletion( for ((lookupElement, count) in lookupElementToCount) { lookupElement.putUserData(PRIORITY_KEY, -count) - collector.addElement(lookupElement) + collector.addElement(lookupElement, parametersInCurrentFilePrefixMatcher) } } - private fun addSuggestionsForClassifier(classifier: DeclarationDescriptor) { - addSuggestions(classifier.getName().asString()) { name -> NameAndDescriptorType(name, classifier as ClassifierDescriptor) } + private fun addSuggestionsForClassifier(classifier: DeclarationDescriptor, userPrefix: String, prefixMatcher: PrefixMatcher) { + addSuggestions(classifier.getName().asString(), userPrefix, prefixMatcher, DescriptorType(classifier as ClassifierDescriptor)) } - private fun addSuggestionsForJavaClass(psiClass: PsiClass) { - addSuggestions(psiClass.getName()) { name -> NameAndJavaType(name, psiClass) } + private fun addSuggestionsForJavaClass(psiClass: PsiClass, userPrefix: String, prefixMatcher: PrefixMatcher) { + addSuggestions(psiClass.getName(), userPrefix, prefixMatcher, JavaClassType(psiClass)) } - private inline fun addSuggestions(className: String, nameAndTypeFactory: (String) -> NameAndType) { - val parameterNames = JetNameSuggester.getCamelNames(className, EmptyValidator) - for (parameterName in parameterNames) { - if (prefixMatcher.prefixMatches(parameterName)) { - val lookupElement = MyLookupElement.create(nameAndTypeFactory(parameterName), lookupElementFactory) + private fun addSuggestions(className: String, userPrefix: String, prefixMatcher: PrefixMatcher, type: Type) { + if (suggestionsByTypesAdded.contains(type)) return // don't add suggestions for the same with longer user prefix + + val nameSuggestions = JetNameSuggester.getCamelNames(className, EmptyValidator, userPrefix.isEmpty()) + for (name in nameSuggestions) { + if (prefixMatcher.prefixMatches(name)) { + val lookupElement = MyLookupElement.create(userPrefix, name, type, lookupElementFactory) if (lookupElement != null) { - collector.addElement(lookupElement) + lookupElement.putUserData(PRIORITY_KEY, userPrefix.length()) // suggestions with longer user prefix get smaller priority + collector.addElement(lookupElement, prefixMatcher) + suggestionsByTypesAdded.add(type) } } } @@ -146,55 +180,51 @@ class ParameterNameAndTypeCompletion( return visibilityFilter(classifier) && getArguments().all { it.getType().isVisible(visibilityFilter) } } - private abstract class NameAndType(val parameterName: String) { + private abstract class Type(private val idString: String) { abstract fun createTypeLookupElement(lookupElementFactory: LookupElementFactory): LookupElement? - abstract val typeIdString: String // types with equal id-string are considered as equal (used to avoid duplicated items) + + override fun equals(other: Any?) = other is Type && other.idString == idString + override fun hashCode() = idString.hashCode() } - private class NameAndDescriptorType(parameterName: String, private val classifier: ClassifierDescriptor) : NameAndType(parameterName) { + private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(DescriptorUtils.getFqName(classifier).render()) { override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) = lookupElementFactory.createLookupElement(classifier, false) - - override val typeIdString: String - get() = DescriptorUtils.getFqName(classifier).render() } - private class NameAndJavaType(parameterName: String, private val psiClass: PsiClass) : NameAndType(parameterName) { + private class JavaClassType(private val psiClass: PsiClass) : Type(psiClass.getQualifiedName()!!) { override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) = lookupElementFactory.createLookupElementForJavaClass(psiClass) - - override val typeIdString: String - get() = psiClass.getQualifiedName()!! } - private class NameAndArbitraryType(parameterName: String, private val type: JetType) : NameAndType(parameterName) { + private class ArbitraryType(private val type: JetType) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)) { override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) = lookupElementFactory.createLookupElementForType(type) - - override val typeIdString: String - get() = IdeDescriptorRenderers.SOURCE_CODE.renderType(type) } - private class MyLookupElement private constructor( - private val parameterName: String, - private val typeIdString: String, + private data class MyLookupElement private constructor( + private val userPrefix: String, + private val nameSuggestion: String, + private val type: Type, typeLookupElement: LookupElement ) : LookupElementDecorator(typeLookupElement) { companion object { - fun create(nameAndType: NameAndType, factory: LookupElementFactory): LookupElement? { - val typeLookupElement = nameAndType.createTypeLookupElement(factory) ?: return null - val lookupElement = MyLookupElement(nameAndType.parameterName, nameAndType.typeIdString, typeLookupElement) + fun create(userPrefix: String, nameSuggestion: String, type: Type, factory: LookupElementFactory): LookupElement? { + val typeLookupElement = type.createTypeLookupElement(factory) ?: return null + val lookupElement = MyLookupElement(userPrefix, nameSuggestion, type, typeLookupElement) return lookupElement.suppressAutoInsertion() } } + private val parameterName = userPrefix + nameSuggestion + override fun equals(other: Any?) - = other is MyLookupElement && parameterName == other.parameterName && typeIdString == other.typeIdString + = other is MyLookupElement && nameSuggestion == other.nameSuggestion && userPrefix == other.userPrefix && type == other.type override fun hashCode() = parameterName.hashCode() - override fun getLookupString() = parameterName - override fun getAllLookupStrings() = setOf(parameterName) + override fun getLookupString() = nameSuggestion + override fun getAllLookupStrings() = setOf(nameSuggestion) override fun renderElement(presentation: LookupElementPresentation) { super.renderElement(presentation) @@ -205,7 +235,7 @@ class ParameterNameAndTypeCompletion( val settings = CodeStyleSettingsManager.getInstance(context.getProject()).getCurrentSettings().getCustomSettings(javaClass()) val spaceBefore = if (settings.SPACE_BEFORE_TYPE_COLON) " " else "" val spaceAfter = if (settings.SPACE_AFTER_TYPE_COLON) " " else "" - val text = parameterName + spaceBefore + ":" + spaceAfter + val text = nameSuggestion + spaceBefore + ":" + spaceAfter val startOffset = context.getStartOffset() context.getDocument().insertString(startOffset, text) @@ -223,4 +253,10 @@ class ParameterNameAndTypeCompletion( object Weigher : LookupElementWeigher("kotlin.parameterNameAndTypePriority") { override fun weigh(element: LookupElement, context: WeighingContext): Int = element.getUserData(PRIORITY_KEY) ?: 0 } + + private class MyPrefixMatcher(prefix: String) : CamelHumpMatcher(prefix, false) { + override fun prefixMatches(element: LookupElement) = isStartMatch(element) + + override fun cloneWithPrefix(prefix: String) = MyPrefixMatcher(prefix) + } } \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/ByAbbreviation.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/ByAbbreviation.kt new file mode 100644 index 00000000000..5c20882841b --- /dev/null +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/ByAbbreviation.kt @@ -0,0 +1,7 @@ +package pack + +class DeclarationDescriptor + +fun f(dd) + +// EXIST: { lookupString: "declarationDescriptor", itemText: "declarationDescriptor: DeclarationDescriptor", tailText: " (pack)" } diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/NoAutoInsertion.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/NoAutoInsertion.kt index a1e9baaa54e..c771e0cc1fe 100644 --- a/idea/idea-completion/testData/basic/common/parameterNameAndType/NoAutoInsertion.kt +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/NoAutoInsertion.kt @@ -1,7 +1,7 @@ -class FooBar +class FooBaaaaar -fun f(fooBa) +fun f(fooBaaaa) // AUTOCOMPLETE_SETTING: true -// EXIST: fooBar +// EXIST: fooBaaaaar // NUMBER: 1 diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplication.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplication.kt index 3162454ff16..9e1fef8377a 100644 --- a/idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplication.kt +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplication.kt @@ -1,6 +1,6 @@ import kotlin.properties.* -fun f(readOnlyProp) +fun f(keyMissin) -// EXIST: { lookupString: "readOnlyProperty", itemText: "readOnlyProperty: ReadOnlyProperty", tailText: " (kotlin.properties)" } +// EXIST: { lookupString: "keyMissingException", itemText: "keyMissingException: KeyMissingException", tailText: " (kotlin.properties)" } // NUMBER: 1 diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplicationJava.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplicationJava.kt index 5f6b9fe24f1..faaa1a9f30e 100644 --- a/idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplicationJava.kt +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplicationJava.kt @@ -1,6 +1,6 @@ -import java.io.* +import java.sql.* -fun f(printSt) +fun f(blob) -// EXIST_JAVA_ONLY: { lookupString: "printStream", itemText: "printStream: PrintStream", tailText: " (java.io)" } +// EXIST_JAVA_ONLY: { lookupString: "blob", itemText: "blob: Blob", tailText: " (java.sql)" } // NUMBER_JAVA: 1 diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/NoFromFileAndFromClassesDuplication.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/NoFromFileAndFromClassesDuplication.kt index bb94818861d..9e3f5901c66 100644 --- a/idea/idea-completion/testData/basic/common/parameterNameAndType/NoFromFileAndFromClassesDuplication.kt +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/NoFromFileAndFromClassesDuplication.kt @@ -1,17 +1,16 @@ package ppp -import java.io.* +import java.security.* -class MyPrintStream +class MyAlgorithmException -fun f1(printStream: PrintStream){} -fun f2(printStream: PrintStream?){} -fun f3(printStream: MyPrintStream){} +fun f1(algorithmException: NoSuchAlgorithmException){} +fun f2(algorithmException: NoSuchAlgorithmException?){} +fun f3(algorithmException: MyAlgorithmException){} -fun f(printStr) +fun f(algorith) -// EXIST_JAVA_ONLY: { lookupString: "printStream", itemText: "printStream: PrintStream", tailText: " (java.io)" } -// EXIST_JAVA_ONLY: { lookupString: "printStream", itemText: "printStream: PrintStream?", tailText: " (java.io)" } -// EXIST: { lookupString: "printStream", itemText: "printStream: MyPrintStream", tailText: " (ppp)" } -// EXIST: { lookupString: "myPrintStream", itemText: "myPrintStream: MyPrintStream", tailText: " (ppp)" } +// EXIST_JAVA_ONLY: { lookupString: "algorithmException", itemText: "algorithmException: NoSuchAlgorithmException", tailText: " (java.security)" } +// EXIST_JAVA_ONLY: { lookupString: "algorithmException", itemText: "algorithmException: NoSuchAlgorithmException?", tailText: " (java.security)" } +// EXIST: { lookupString: "algorithmException", itemText: "algorithmException: MyAlgorithmException", tailText: " (ppp)" } // NOTHING_ELSE diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/Simple.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/Simple.kt index ee9e2ceaf5f..92b8fc83616 100644 --- a/idea/idea-completion/testData/basic/common/parameterNameAndType/Simple.kt +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/Simple.kt @@ -7,5 +7,5 @@ class Boo fun f(b) // EXIST: { lookupString: "bar", itemText: "bar: FooBar", tailText: " (pack)" } -// EXIST: { lookupString: "fooBar", itemText: "fooBar: FooBar", tailText: " (pack)" } +// ABSENT: fooBar // EXIST: { lookupString: "boo", itemText: "boo: Boo", tailText: " (pack)" } diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix1.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix1.kt new file mode 100644 index 00000000000..cf271721723 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix1.kt @@ -0,0 +1,11 @@ +package pack + +class FooBar + +class Boo + +fun f(myB) + +// EXIST: { lookupString: "Bar", itemText: "myBar: FooBar", tailText: " (pack)" } +// ABSENT: FooBar +// EXIST: { lookupString: "Boo", itemText: "myBoo: Boo", tailText: " (pack)" } diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix2.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix2.kt new file mode 100644 index 00000000000..ea1d514d431 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix2.kt @@ -0,0 +1,3 @@ +fun f(myF) + +// EXIST_JAVA_ONLY: { lookupString: "File", itemText: "myFile: File", tailText: " (java.io)" } diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix3.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix3.kt new file mode 100644 index 00000000000..a7520f7da1d --- /dev/null +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix3.kt @@ -0,0 +1,11 @@ +package pack + +class FooFaa + +class Fuu + +fun f(myFooF) + +// EXIST: { lookupString: "FooFaa", itemText: "myFooFaa: FooFaa", tailText: " (pack)" } +// EXIST: { lookupString: "Fuu", itemText: "myFooFuu: Fuu", tailText: " (pack)" } +// ABSENT: { itemText: "myFooFooFaa: FooFaa" } diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/ValParameter.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/ValParameter.kt index 5667ddd7dc2..e6c580348ef 100644 --- a/idea/idea-completion/testData/basic/common/parameterNameAndType/ValParameter.kt +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/ValParameter.kt @@ -7,5 +7,5 @@ class Boo class C(val b) // EXIST: { lookupString: "bar", itemText: "bar: FooBar", tailText: " (pack)" } -// EXIST: { lookupString: "fooBar", itemText: "fooBar: FooBar", tailText: " (pack)" } +// ABSENT: fooBar // EXIST: { lookupString: "boo", itemText: "boo: Boo", tailText: " (pack)" } diff --git a/idea/idea-completion/testData/basic/common/parameterNameAndType/VarParameter.kt b/idea/idea-completion/testData/basic/common/parameterNameAndType/VarParameter.kt index 68880017a92..2fe93b0359c 100644 --- a/idea/idea-completion/testData/basic/common/parameterNameAndType/VarParameter.kt +++ b/idea/idea-completion/testData/basic/common/parameterNameAndType/VarParameter.kt @@ -7,5 +7,5 @@ class C(private var b) { } // EXIST: { lookupString: "bar", itemText: "bar: FooBar", tailText: " (pack)" } -// EXIST: { lookupString: "fooBar", itemText: "fooBar: FooBar", tailText: " (pack)" } +// ABSENT: fooBar // EXIST: { lookupString: "boo", itemText: "boo: Boo", tailText: " (pack.C)" } diff --git a/idea/idea-completion/testData/handlers/basic/parameterNameAndType/UserPrefix.kt b/idea/idea-completion/testData/handlers/basic/parameterNameAndType/UserPrefix.kt new file mode 100644 index 00000000000..872ae2cdb96 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/parameterNameAndType/UserPrefix.kt @@ -0,0 +1,5 @@ +class FooBar + +fun f(myFo) + +// ELEMENT: FooBar diff --git a/idea/idea-completion/testData/handlers/basic/parameterNameAndType/UserPrefix.kt.after b/idea/idea-completion/testData/handlers/basic/parameterNameAndType/UserPrefix.kt.after new file mode 100644 index 00000000000..ba272cb1284 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/parameterNameAndType/UserPrefix.kt.after @@ -0,0 +1,5 @@ +class FooBar + +fun f(myFooBar: FooBar) + +// ELEMENT: FooBar diff --git a/idea/idea-completion/testData/weighers/basic/parameterNameAndType/UserPrefix.kt b/idea/idea-completion/testData/weighers/basic/parameterNameAndType/UserPrefix.kt new file mode 100644 index 00000000000..277f6a107d7 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/parameterNameAndType/UserPrefix.kt @@ -0,0 +1,11 @@ +class ZzzYyyXxx + +class YyyXoo + +class Xaa + +fun foo(zzzYyyX) + +// ORDER: zzzYyyXxx +// ORDER: YyyXoo +// ORDER: Xaa diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index ed9e7571c6a..8a0e6ccef00 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -1410,6 +1410,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/parameterNameAndType"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("ByAbbreviation.kt") + public void testByAbbreviation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/ByAbbreviation.kt"); + doTest(fileName); + } + @TestMetadata("NoAutoInsertion.kt") public void testNoAutoInsertion() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NoAutoInsertion.kt"); @@ -1482,6 +1488,24 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest(fileName); } + @TestMetadata("UserPrefix1.kt") + public void testUserPrefix1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix1.kt"); + doTest(fileName); + } + + @TestMetadata("UserPrefix2.kt") + public void testUserPrefix2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix2.kt"); + doTest(fileName); + } + + @TestMetadata("UserPrefix3.kt") + public void testUserPrefix3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix3.kt"); + doTest(fileName); + } + @TestMetadata("ValParameter.kt") public void testValParameter() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/ValParameter.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index af75c7ff4d0..002d4d0f7a3 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -1410,6 +1410,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/parameterNameAndType"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("ByAbbreviation.kt") + public void testByAbbreviation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/ByAbbreviation.kt"); + doTest(fileName); + } + @TestMetadata("NoAutoInsertion.kt") public void testNoAutoInsertion() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NoAutoInsertion.kt"); @@ -1482,6 +1488,24 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("UserPrefix1.kt") + public void testUserPrefix1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix1.kt"); + doTest(fileName); + } + + @TestMetadata("UserPrefix2.kt") + public void testUserPrefix2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix2.kt"); + doTest(fileName); + } + + @TestMetadata("UserPrefix3.kt") + public void testUserPrefix3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix3.kt"); + doTest(fileName); + } + @TestMetadata("ValParameter.kt") public void testValParameter() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/ValParameter.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java index c83546ef609..7ceff4eafc6 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java @@ -294,6 +294,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/Simple.kt"); doTest(fileName); } + + @TestMetadata("UserPrefix.kt") + public void testUserPrefix() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/UserPrefix.kt"); + doTest(fileName); + } } @TestMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate") diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java index 8aedcb29b06..c378b9a6ab2 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java @@ -174,5 +174,11 @@ public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletion String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/parameterNameAndType/StartMatchFirst.kt"); doTest(fileName); } + + @TestMetadata("UserPrefix.kt") + public void testUserPrefix() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/parameterNameAndType/UserPrefix.kt"); + doTest(fileName); + } } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/refactoring/JetNameSuggester.java b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/refactoring/JetNameSuggester.java index 0f3dda8b9c9..62133977f1a 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/refactoring/JetNameSuggester.java +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/refactoring/JetNameSuggester.java @@ -199,13 +199,17 @@ public class JetNameSuggester { private static final String[] ACCESSOR_PREFIXES = { "get", "is", "set" }; - public static List getCamelNames(String name, JetNameValidator validator) { + public static List getCamelNames(String name, JetNameValidator validator, boolean startLowerCase) { ArrayList result = new ArrayList(); - addCamelNames(result, name, validator); + addCamelNames(result, name, validator, startLowerCase); return result; } private static void addCamelNames(ArrayList result, String name, JetNameValidator validator) { + addCamelNames(result, name, validator, true); + } + + private static void addCamelNames(ArrayList result, String name, JetNameValidator validator, boolean startLowerCase) { if (name == "") return; String s = deleteNonLetterFromString(name); @@ -225,11 +229,12 @@ public class JetNameSuggester { boolean upperCaseLetter = Character.isUpperCase(c); if (i == 0) { - addName(result, decapitalize(s), validator); + addName(result, startLowerCase ? decapitalize(s) : s, validator); } else { if (upperCaseLetter && !upperCaseLetterBefore) { - addName(result, decapitalize(s.substring(i)), validator); + String substring = s.substring(i); + addName(result, startLowerCase ? decapitalize(substring) : substring, validator); } } @@ -237,7 +242,7 @@ public class JetNameSuggester { } } - private static String decapitalize(String s) { + public static String decapitalize(String s) { char c = s.charAt(0); if (!Character.isUpperCase(c)) return s;