Parameter name completion allows user prefix part in the name + more strict prefix matcher used

This commit is contained in:
Valentin Kipyatkov
2015-06-19 19:20:42 +03:00
parent d95c44f838
commit ecbc7ba5b6
23 changed files with 262 additions and 96 deletions
@@ -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) {
@@ -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<LookupElement>(element) {
collector.addElement(object: LookupElementDecorator<LookupElement>(element) {
override fun handleInsert(context: InsertionContext?) {
// insert only plain name here, no qualifier/parentheses/etc.
}
@@ -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<LookupElement>()
private val elements = LinkedHashMap<PrefixMatcher, ArrayList<LookupElement>>()
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<LookupElement>(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<LookupElement>) {
elements.forEach { addElement(it) }
}
public fun advertiseSecondCompletion() {
JavaCompletionContributor.advertiseSecondCompletion(completionParameters.getOriginalFile().getProject(), defaultResultSet)
}
}
@@ -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<String>
private val allPrefixes: List<String> // 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<Type>()
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<LookupElement, Int>()
position.getContainingFile().forEachDescendantOfType<JetParameter>(
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<LookupElement>(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<JetCodeStyleSettings>())
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)
}
}
@@ -0,0 +1,7 @@
package pack
class DeclarationDescriptor
fun f(dd<caret>)
// EXIST: { lookupString: "declarationDescriptor", itemText: "declarationDescriptor: DeclarationDescriptor", tailText: " (pack)" }
@@ -1,7 +1,7 @@
class FooBar
class FooBaaaaar
fun f(fooBa<caret>)
fun f(fooBaaaa<caret>)
// AUTOCOMPLETE_SETTING: true
// EXIST: fooBar
// EXIST: fooBaaaaar
// NUMBER: 1
@@ -1,6 +1,6 @@
import kotlin.properties.*
fun f(readOnlyProp<caret>)
fun f(keyMissin<caret>)
// EXIST: { lookupString: "readOnlyProperty", itemText: "readOnlyProperty: ReadOnlyProperty", tailText: "<R, T> (kotlin.properties)" }
// EXIST: { lookupString: "keyMissingException", itemText: "keyMissingException: KeyMissingException", tailText: " (kotlin.properties)" }
// NUMBER: 1
@@ -1,6 +1,6 @@
import java.io.*
import java.sql.*
fun f(printSt<caret>)
fun f(blob<caret>)
// 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
@@ -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<caret>)
fun f(algorith<caret>)
// 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
@@ -7,5 +7,5 @@ class Boo
fun f(b<caret>)
// 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)" }
@@ -0,0 +1,11 @@
package pack
class FooBar
class Boo
fun f(myB<caret>)
// EXIST: { lookupString: "Bar", itemText: "myBar: FooBar", tailText: " (pack)" }
// ABSENT: FooBar
// EXIST: { lookupString: "Boo", itemText: "myBoo: Boo", tailText: " (pack)" }
@@ -0,0 +1,3 @@
fun f(myF<caret>)
// EXIST_JAVA_ONLY: { lookupString: "File", itemText: "myFile: File", tailText: " (java.io)" }
@@ -0,0 +1,11 @@
package pack
class FooFaa
class Fuu
fun f(myFooF<caret>)
// EXIST: { lookupString: "FooFaa", itemText: "myFooFaa: FooFaa", tailText: " (pack)" }
// EXIST: { lookupString: "Fuu", itemText: "myFooFuu: Fuu", tailText: " (pack)" }
// ABSENT: { itemText: "myFooFooFaa: FooFaa" }
@@ -7,5 +7,5 @@ class Boo
class C(val b<caret>)
// 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)" }
@@ -7,5 +7,5 @@ class C(private var b<caret>) {
}
// 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)" }
@@ -0,0 +1,5 @@
class FooBar
fun f(myFo<caret>)
// ELEMENT: FooBar
@@ -0,0 +1,5 @@
class FooBar
fun f(myFooBar: FooBar<caret>)
// ELEMENT: FooBar
@@ -0,0 +1,11 @@
class ZzzYyyXxx
class YyyXoo
class Xaa
fun foo(zzzYyyX<caret>)
// ORDER: zzzYyyXxx
// ORDER: YyyXoo
// ORDER: Xaa
@@ -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");
@@ -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");
@@ -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")
@@ -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);
}
}
}
@@ -199,13 +199,17 @@ public class JetNameSuggester {
private static final String[] ACCESSOR_PREFIXES = { "get", "is", "set" };
public static List<String> getCamelNames(String name, JetNameValidator validator) {
public static List<String> getCamelNames(String name, JetNameValidator validator, boolean startLowerCase) {
ArrayList<String> result = new ArrayList<String>();
addCamelNames(result, name, validator);
addCamelNames(result, name, validator, startLowerCase);
return result;
}
private static void addCamelNames(ArrayList<String> result, String name, JetNameValidator validator) {
addCamelNames(result, name, validator, true);
}
private static void addCamelNames(ArrayList<String> 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;