KT-9418 Suggest name for new declaration basing on unresolved names in code

#KT-9418 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-01-15 13:27:53 +03:00
parent 83ca4b84f9
commit 28331bca9f
23 changed files with 614 additions and 116 deletions
@@ -28,6 +28,7 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.colors.CodeInsightColors
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.TextRange
import com.intellij.psi.MultiRangeReference
import com.intellij.psi.PsiElement
@@ -44,10 +45,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
import org.jetbrains.kotlin.idea.quickfix.QuickFixes
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.utils.singletonOrEmptyList
@@ -81,6 +79,12 @@ open class KotlinPsiChecker : Annotator, HighlightRangeExtension {
fun annotateElement(element: PsiElement, holder: AnnotationHolder, diagnostics: Diagnostics) {
val diagnosticsForElement = diagnostics.forElement(element)
if (element is KtNameReferenceExpression) {
val unresolved = diagnostics.any { it.factory == Errors.UNRESOLVED_REFERENCE }
element.putUserData(UNRESOLVED_KEY, if (unresolved) Unit else null)
}
if (diagnosticsForElement.isEmpty()) return
if (ProjectRootsUtil.isInProjectSource(element) || element.containingFile is KtCodeFragment) {
@@ -98,6 +102,10 @@ open class KotlinPsiChecker : Annotator, HighlightRangeExtension {
fun createQuickFixes(diagnostic: Diagnostic): Collection<IntentionAction> =
createQuickFixes(diagnostic.singletonOrEmptyList())[diagnostic]
private val UNRESOLVED_KEY = Key<Unit>("KotlinPsiChecker.UNRESOLVED_KEY")
fun wasUnresolved(element: KtNameReferenceExpression) = element.getUserData(UNRESOLVED_KEY) != null
}
}
@@ -46,16 +46,18 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.util.supertypesWithAny
import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.*
class BasicCompletionSession(
configuration: CompletionSessionConfiguration,
parameters: CompletionParameters,
toFromOriginalFileMapper: ToFromOriginalFileMapper,
private val toFromOriginalFileMapper: ToFromOriginalFileMapper,
resultSet: CompletionResultSet
) : CompletionSession(configuration, parameters, resultSet) {
@@ -88,14 +90,9 @@ class BasicCompletionSession(
private fun detectCompletionKind(): CompletionKind {
if (nameExpression == null) {
return when {
(position.parent as? KtParameter)?.nameIdentifier == position ->
PARAMETER_NAME
(position.parent as? KtNamedDeclaration)?.nameIdentifier == position -> DECLARATION_NAME
(position.parent as? KtClassOrObject)?.nameIdentifier == position && position.parent.parent is KtFile ->
TOP_LEVEL_CLASS_NAME
else ->
KEYWORDS_ONLY
else -> KEYWORDS_ONLY
}
}
@@ -158,6 +155,20 @@ class BasicCompletionSession(
}
override fun doComplete() {
val declaration = isStartOfExtensionReceiverFor()
if (declaration != null) {
completeDeclarationNameFromUnresolved(declaration)
// no auto-popup on typing after "val", "var" and "fun" because it's likely the name of the declaration which is being typed by user
if (parameters.invocationCount == 0) {
val suppressOtherCompletion = when (declaration) {
is KtNamedFunction -> prefixMatcher.prefix.let { it.isEmpty() || it[0].isLowerCase() /* function name usually starts with lower case letter */ }
else -> true
}
if (suppressOtherCompletion) return
}
}
fun completeWithSmartCompletion(lookupElementFactory: LookupElementFactory) {
if (smartCompletion != null) {
val (additionalItems, @Suppress("UNUSED_VARIABLE") inheritanceSearcher) = smartCompletion.additionalItems(lookupElementFactory)
@@ -276,6 +287,19 @@ class BasicCompletionSession(
}
}
}
private fun isStartOfExtensionReceiverFor(): KtCallableDeclaration? {
val userType = nameExpression!!.parent as? KtUserType ?: return null
if (userType.qualifier != null) return null
val typeRef = userType.parent as? KtTypeReference ?: return null
if (userType != typeRef.typeElement) return null
val parent = typeRef.parent
return when (parent) {
is KtNamedFunction -> parent.check { typeRef == it.receiverTypeReference }
is KtProperty -> parent.check { typeRef == it.receiverTypeReference }
else -> null
}
}
}
private val KEYWORDS_ONLY = object : CompletionKind {
@@ -414,45 +438,42 @@ class BasicCompletionSession(
}
}
private val PARAMETER_NAME = object : CompletionKind {
private val DECLARATION_NAME = object : CompletionKind {
override val descriptorKindFilter: DescriptorKindFilter?
get() = null
override fun doComplete() {
val declaration = declaration()
if (declaration is KtParameter && !shouldCompleteParameterNameAndType()) return // do not complete also keywords and from unresolved references in such case
collector.addLookupElementPostProcessor { lookupElement ->
lookupElement.putUserData(KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING, Unit)
lookupElement
}
KEYWORDS_ONLY.doComplete()
if (shouldCompleteParameterNameAndType()) {
val parameterNameAndTypeCompletion = ParameterNameAndTypeCompletion(collector, basicLookupElementFactory, prefixMatcher, resolutionFacade)
completeDeclarationNameFromUnresolved(declaration)
// if we are typing parameter name, restart completion each time we type an upper case letter because new suggestions will appear (previous words can be used as user prefix)
val prefixPattern = StandardPatterns.string().with(object : PatternCondition<String>("Prefix ends with uppercase letter") {
override fun accepts(prefix: String, context: ProcessingContext?) = prefix.isNotEmpty() && prefix.last().isUpperCase()
})
collector.restartCompletionOnPrefixChange(prefixPattern)
when (declaration) {
is KtParameter ->
completeParameterNameAndType()
collector.addLookupElementPostProcessor { lookupElement ->
lookupElement.putUserData(KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING, Unit)
lookupElement.putUserData(KotlinCompletionCharFilter.HIDE_LOOKUP_ON_COLON, Unit)
lookupElement
is KtClassOrObject -> {
if (declaration.isTopLevel()) {
completeTopLevelClassName()
}
}
parameterNameAndTypeCompletion.addFromParametersInFile(position, resolutionFacade, isVisibleFilterCheckAlways)
flushToResultSet()
parameterNameAndTypeCompletion.addFromImportedClasses(position, bindingContext, isVisibleFilterCheckAlways)
flushToResultSet()
parameterNameAndTypeCompletion.addFromAllClasses(parameters, indicesHelper(false))
}
}
override fun shouldDisableAutoPopup(): Boolean {
if (!shouldCompleteParameterNameAndType() || TemplateManager.getInstance(project).getActiveTemplate(parameters.editor) != null) {
return true
}
if (TemplateManager.getInstance(project).getActiveTemplate(parameters.editor) != null) return true
if (LookupCancelWatcher.getInstance(project).wasAutoPopupRecentlyCancelled(parameters.editor, position.startOffset)) {
return true
if (declaration() is KtParameter) {
if (LookupCancelWatcher.getInstance(project).wasAutoPopupRecentlyCancelled(parameters.editor, position.startOffset)) {
return true
}
}
return false
@@ -465,8 +486,41 @@ class BasicCompletionSession(
return sorter
}
private fun completeParameterNameAndType() {
val parameterNameAndTypeCompletion = ParameterNameAndTypeCompletion(collector, basicLookupElementFactory, prefixMatcher, resolutionFacade)
// if we are typing parameter name, restart completion each time we type an upper case letter because new suggestions will appear (previous words can be used as user prefix)
val prefixPattern = StandardPatterns.string().with(object : PatternCondition<String>("Prefix ends with uppercase letter") {
override fun accepts(prefix: String, context: ProcessingContext?) = prefix.isNotEmpty() && prefix.last().isUpperCase()
})
collector.restartCompletionOnPrefixChange(prefixPattern)
collector.addLookupElementPostProcessor { lookupElement ->
lookupElement.putUserData(KotlinCompletionCharFilter.HIDE_LOOKUP_ON_COLON, Unit)
lookupElement
}
parameterNameAndTypeCompletion.addFromParametersInFile(position, resolutionFacade, isVisibleFilterCheckAlways)
flushToResultSet()
parameterNameAndTypeCompletion.addFromImportedClasses(position, bindingContext, isVisibleFilterCheckAlways)
flushToResultSet()
parameterNameAndTypeCompletion.addFromAllClasses(parameters, indicesHelper(false))
}
private fun completeTopLevelClassName() {
val name = parameters.originalFile.virtualFile.nameWithoutExtension
if (!(Name.isValidIdentifier(name) && Name.identifier(name).render() == name && name[0].isUpperCase())) return
if ((parameters.originalFile as KtFile).declarations.any { it is KtClassOrObject && it.name == name }) return
collector.addElement(LookupElementBuilder.create(name))
}
private fun declaration() = position.parent as KtNamedDeclaration
private fun shouldCompleteParameterNameAndType(): Boolean {
val parameter = position.getNonStrictParentOfType<KtParameter>()!!
val parameter = declaration() as? KtParameter ?: return false
val list = parameter.parent as? KtParameterList ?: return false
val owner = list.parent
return when (owner) {
@@ -478,21 +532,6 @@ class BasicCompletionSession(
}
}
private val TOP_LEVEL_CLASS_NAME = object : CompletionKind {
override val descriptorKindFilter: DescriptorKindFilter?
get() = null
override fun doComplete() {
val name = parameters.originalFile.virtualFile.nameWithoutExtension
if (!(Name.isValidIdentifier(name) && Name.identifier(name).render() == name && name[0].isUpperCase())) return
if ((parameters.originalFile as KtFile).declarations.any { it is KtClassOrObject && it.name == name }) return
val lookupElement = LookupElementBuilder.create(name)
lookupElement.putUserData(KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING, Unit)
collector.addElement(lookupElement)
}
}
private val SUPER_QUALIFIER = object : CompletionKind {
override val descriptorKindFilter: DescriptorKindFilter?
get() = DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS
@@ -513,4 +552,35 @@ class BasicCompletionSession(
.forEach { collector.addElement(it) }
}
}
private fun completeDeclarationNameFromUnresolved(declaration: KtNamedDeclaration) {
val referenceScope = referenceScope(declaration) ?: return
val originalScope = toFromOriginalFileMapper.toOriginalFile(referenceScope) ?: return
val afterOffset = if (referenceScope is KtBlockExpression) parameters.offset else null
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration]
FromUnresolvedNamesCompletion(collector, prefixMatcher).addNameSuggestions(originalScope, afterOffset, descriptor)
}
private fun referenceScope(declaration: KtNamedDeclaration): KtElement? {
val parent = declaration.parent
when (parent) {
is KtParameterList -> return parent.parent as KtElement
is KtClassBody -> {
val classOrObject = parent.parent as KtClassOrObject
if (classOrObject is KtObjectDeclaration && classOrObject.isCompanion()) {
return classOrObject.containingClassOrObject
}
else {
return classOrObject
}
}
is KtFile -> return parent
is KtBlockExpression -> return parent
else -> return null
}
}
}
@@ -64,6 +64,7 @@ tailrec fun <T : Any> LookupElement.getUserDataDeep(key: Key<T>): T? {
enum class ItemPriority {
SUPER_METHOD_WITH_ARGUMENTS,
FROM_UNRESOLVED_NAME_SUGGESTION,
DEFAULT,
IMPLEMENT,
OVERRIDE,
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2016 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.completion
import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.progress.ProgressManager
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.highlighter.KotlinPsiChecker
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import java.util.*
class FromUnresolvedNamesCompletion(
private val collector: LookupElementsCollector,
private val prefixMatcher: PrefixMatcher
) {
fun addNameSuggestions(scope: KtElement, afterOffset: Int?, sampleDescriptor: DeclarationDescriptor?) {
val names = HashSet<String>()
scope.forEachDescendantOfType<KtNameReferenceExpression> { refExpr ->
ProgressManager.checkCanceled()
if (KotlinPsiChecker.wasUnresolved(refExpr)) {
val callTypeAndReceiver = CallTypeAndReceiver.detect(refExpr)
if (callTypeAndReceiver.receiver != null) return@forEachDescendantOfType
if (sampleDescriptor != null) {
if (!callTypeAndReceiver.callType.descriptorKindFilter.accepts(sampleDescriptor)) return@forEachDescendantOfType
if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
val isCall = refExpr.parent is KtCallExpression
val canBeUsage = when (sampleDescriptor) {
is FunctionDescriptor -> isCall // cannot use simply function name without arguments
is VariableDescriptor -> true // variable can as well be used with arguments when it has invoke()
is ClassDescriptor -> if (isCall) sampleDescriptor.kind == ClassKind.CLASS else sampleDescriptor.kind.isSingleton
else -> false // what else it can be?
}
if (!canBeUsage) return@forEachDescendantOfType
}
}
val name = refExpr.getReferencedName()
if (!prefixMatcher.prefixMatches(name)) return@forEachDescendantOfType
if (afterOffset != null && refExpr.startOffset < afterOffset) return@forEachDescendantOfType
if (refExpr.mainReference.resolveToDescriptors(refExpr.analyze(BodyResolveMode.PARTIAL)).isEmpty()) {
names.add(name)
}
}
}
for (name in names.sorted()) {
val lookupElement = LookupElementBuilder.create(name)
.suppressAutoInsertion()
.assignPriority(ItemPriority.FROM_UNRESOLVED_NAME_SUGGESTION)
lookupElement.putUserData(KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING, Unit)
collector.addElement(lookupElement)
}
}
}
@@ -42,7 +42,6 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
var KtFile.doNotComplete: Boolean? by UserDataProperty(Key.create("DO_NOT_COMPLETE"))
@@ -365,33 +364,9 @@ class KotlinCompletionContributor : CompletionContributor() {
// no completion auto-popup after integer and dot
if (invocationCount == 0 && prefixMatcher.prefix.isEmpty() && AFTER_INTEGER_LITERAL_AND_DOT.accepts(position)) return true
// no auto-popup on typing after "val", "var" and "fun" because it's likely the name of the declaration which is being typed by user
if (invocationCount == 0) {
val callable = isInExtensionReceiverOf(position)
if (callable != null) {
return when (callable) {
is KtNamedFunction -> prefixMatcher.prefix.let { it.isEmpty() || it[0].isLowerCase() /* function name usually starts with lower case letter */ }
else -> true
}
}
}
return false
}
private fun isInExtensionReceiverOf(position: PsiElement): KtCallableDeclaration? {
val nameRef = position.parent as? KtNameReferenceExpression ?: return null
val userType = nameRef.parent as? KtUserType ?: return null
val typeRef = userType.parent as? KtTypeReference ?: return null
if (userType != typeRef.typeElement) return null
val parent = typeRef.parent
return when (parent) {
is KtNamedFunction -> parent.check { typeRef == it.receiverTypeReference }
is KtProperty -> parent.check { typeRef == it.receiverTypeReference }
else -> null
}
}
private fun isAtEndOfLine(offset: Int, document: Document): Boolean {
var i = offset
val chars = document.charsSequence
@@ -16,9 +16,9 @@
package org.jetbrains.kotlin.idea.completion
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtDeclaration
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.startOffset
class ToFromOriginalFileMapper(
@@ -63,15 +63,15 @@ class ToFromOriginalFileMapper(
}
}
fun toOriginalFile(declaration: KtDeclaration): KtDeclaration? {
if (declaration.containingFile != syntheticFile) return declaration
val offset = toOriginalFile(declaration.startOffset) ?: return null
return PsiTreeUtil.findElementOfClassAtOffset(originalFile, offset, KtDeclaration::class.java, true)
fun <TElement : PsiElement> toOriginalFile(element: TElement): TElement? {
if (element.containingFile != syntheticFile) return element
val offset = toOriginalFile(element.startOffset) ?: return null
return PsiTreeUtil.findElementOfClassAtOffset(originalFile, offset, element.javaClass, true)
}
fun toSyntheticFile(declaration: KtDeclaration): KtDeclaration? {
if (declaration.containingFile != originalFile) return declaration
val offset = toSyntheticFile(declaration.startOffset) ?: return null
return PsiTreeUtil.findElementOfClassAtOffset(syntheticFile, offset, KtDeclaration::class.java, true)
fun <TElement : PsiElement> toSyntheticFile(element: TElement): TElement? {
if (element.containingFile != originalFile) return element
val offset = toSyntheticFile(element.startOffset) ?: return null
return PsiTreeUtil.findElementOfClassAtOffset(syntheticFile, offset, element.javaClass, true)
}
}
@@ -264,6 +264,8 @@ class TypeInstantiationItems(
other.renderElement(presentation2)
return presentation1.itemText == presentation2.itemText && presentation1.tailText == presentation2.tailText
}
override fun hashCode() = lookupString.hashCode()
}
return InstantiationLookupElement(lookupElement).addTail(tail)
@@ -307,7 +309,7 @@ class TypeInstantiationItems(
for (inheritor in ClassInheritorsSearch.search(parameters)) {
val descriptor = inheritor.resolveToDescriptor(
resolutionFacade,
{ toFromOriginalFileMapper.toSyntheticFile(it) as KtClassOrObject? }
{ toFromOriginalFileMapper.toSyntheticFile(it) }
) ?: continue
if (!visibilityFilter(descriptor)) continue
@@ -5,4 +5,5 @@ class Outer {
fun Outer.<caret>
// INVOCATION_COUNT: 0
// NUMBER: 0
// EXIST: Nested
// NOTHING_ELSE
@@ -5,4 +5,5 @@ class Outer {
fun <T> Outer.<caret>
// INVOCATION_COUNT: 0
// NUMBER: 0
// EXIST: Nested
// NOTHING_ELSE
@@ -0,0 +1,32 @@
// RUN_HIGHLIGHTING_BEFORE
class C {
fun foo(p: Int) {
unresolvedInFoo1()
if (p > 0) {
unresolvedInFoo2()
}
}
companion object {
fun <caret>
fun bar() {
unresolvedInBar()
}
}
fun zoo() {
unresolvedInZoo()
}
}
fun f() {
unresolvedOutside()
}
// EXIST: unresolvedInFoo1
// EXIST: unresolvedInFoo2
// EXIST: unresolvedInBar
// EXIST: unresolvedInZoo
// ABSENT: unresolvedOutside
@@ -0,0 +1,32 @@
// RUN_HIGHLIGHTING_BEFORE
fun foo(p: Int) {
print(unresolved0)
if (p > 0) {
print(unresolved1)
val u<caret>
print(unresolved2)
if (p > 0) {
print(unresolved3)
}
unresolvedCall()
}
print(unresolved4)
}
fun bar() {
print(unresolvedInBar)
}
// ABSENT: unresolved0
// ABSENT: unresolved1
// EXIST: unresolved2
// EXIST: unresolved3
// ABSENT: unresolved4
// ABSENT: unresolvedInBar
// EXIST: unresolvedCall
@@ -0,0 +1,27 @@
// RUN_HIGHLIGHTING_BEFORE
class C {
fun foo(p: Int) {
unresolvedInFoo1()
if (p > 0) {
unresolvedInFoo2()
}
}
fun <caret>
fun bar(s: String) {
unresolvedInBar()
s.unresolvedWithReceiver()
}
}
fun f() {
unresolvedOutside()
}
// EXIST: unresolvedInFoo1
// EXIST: unresolvedInFoo2
// EXIST: unresolvedInBar
// ABSENT: unresolvedWithReceiver
// ABSENT: unresolvedOutside
@@ -0,0 +1,24 @@
// RUN_HIGHLIGHTING_BEFORE
class C {
fun foo(p: Int) {
print(unresolvedInFoo)
}
val <caret>
fun bar(s: String, x: UnresolvedType) {
print(unresolvedInBar)
print(s.unresolvedWithReceiver)
}
}
fun f() {
print(unresolvedOutside)
}
// EXIST: unresolvedInFoo
// EXIST: unresolvedInBar
// ABSENT: unresolvedWithReceiver
// ABSENT: unresolvedOutside
// ABSENT: UnresolvedType
@@ -0,0 +1,9 @@
// RUN_HIGHLIGHTING_BEFORE
fun foo() {
unresolvedInFoo()
}
fun String.<caret>
// ABSENT: unresolvedInFoo
@@ -0,0 +1,16 @@
// RUN_HIGHLIGHTING_BEFORE
fun foo(p: Int, <caret>) {
print(unresolvedInFoo1)
if (p > 0) {
print(unresolvedInFoo2)
}
}
fun bar() {
print(unresolvedInBar)
}
// EXIST: { lookupString: "unresolvedInFoo1", itemText: "unresolvedInFoo1" }
// EXIST: { lookupString: "unresolvedInFoo2", itemText: "unresolvedInFoo2" }
// ABSENT: unresolvedInBar
@@ -0,0 +1,13 @@
// RUN_HIGHLIGHTING_BEFORE
fun foo(p: UnresolvedClass1) {
val foo = UnresolvedClass2()
val bar = unresolvedValue
}
class <caret>
// EXIST: TopLevelClass
// EXIST: UnresolvedClass1
// EXIST: UnresolvedClass2
// ABSENT: unresolvedValue
@@ -0,0 +1,31 @@
// RUN_HIGHLIGHTING_BEFORE
fun foo(p: Int, x: UnresolvedClassName) {
unresolvedInFoo1()
if (p > 0) {
unresolvedInFoo2()
}
}
fun <caret>
fun bar() {
unresolvedInBar()
val v = unresolvedValue
listOf(1).filter(::unresolvedFunctionRef)
}
class C {
fun f() {
unresolvedInClass()
}
}
// EXIST: unresolvedInFoo1
// EXIST: unresolvedInFoo2
// EXIST: unresolvedInBar
// EXIST: unresolvedInClass
// ABSENT: UnresolvedClassName
// ABSENT: unresolvedValue
// EXIST: unresolvedFunctionRef
@@ -0,0 +1,13 @@
// RUN_HIGHLIGHTING_BEFORE
fun foo(p: UnresolvedClass1) {
val foo = UnresolvedClass2()
val bar = unresolvedValue
}
interface <caret>
// EXIST: TopLevelInterface
// EXIST: UnresolvedClass1
// ABSENT: UnresolvedClass2
// ABSENT: unresolvedValue
@@ -0,0 +1,13 @@
// RUN_HIGHLIGHTING_BEFORE
fun foo(p: UnresolvedClass1) {
val foo = UnresolvedClass2()
val bar = unresolvedValue
}
object <caret>
// EXIST: TopLevelObject
// EXIST: UnresolvedClass1
// ABSENT: UnresolvedClass2
// EXIST: unresolvedValue
@@ -108,6 +108,7 @@ object ExpectedCompletionUtils {
private val NUMBER_JAVA_LINE_PREFIX = "NUMBER_JAVA:"
private val NOTHING_ELSE_PREFIX = "NOTHING_ELSE"
private val RUN_HIGHLIGHTING_BEFORE_PREFIX = "RUN_HIGHLIGHTING_BEFORE"
private val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:"
private val WITH_ORDER_PREFIX = "WITH_ORDER"
@@ -131,6 +132,7 @@ object ExpectedCompletionUtils {
WITH_ORDER_PREFIX,
AUTOCOMPLETE_SETTING_PREFIX,
NOTHING_ELSE_PREFIX,
RUN_HIGHLIGHTING_BEFORE_PREFIX,
RUNTIME_TYPE,
COMPLETION_TYPE_PREFIX,
LightClassComputationControl.LIGHT_CLASS_DIRECTIVE,
@@ -190,7 +192,11 @@ object ExpectedCompletionUtils {
}
fun isNothingElseExpected(fileText: String): Boolean {
return !InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, NOTHING_ELSE_PREFIX).isEmpty()
return InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, NOTHING_ELSE_PREFIX).isNotEmpty()
}
fun shouldRunHighlightingBeforeCompletion(fileText: String): Boolean {
return InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, RUN_HIGHLIGHTING_BEFORE_PREFIX).isNotEmpty()
}
fun getInvocationCount(fileText: String): Int? {
@@ -990,6 +990,18 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("InExtFunName.kt")
public void testInExtFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/InExtFunName.kt");
doTest(fileName);
}
@TestMetadata("InGenericExtFunName.kt")
public void testInGenericExtFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/InGenericExtFunName.kt");
doTest(fileName);
}
@TestMetadata("NoAutoPopupAfterNumberLiteral.kt")
public void testNoAutoPopupAfterNumberLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutoPopupAfterNumberLiteral.kt");
@@ -1002,24 +1014,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("NoAutopopupInExtFunName.kt")
public void testNoAutopopupInExtFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutopopupInExtFunName.kt");
doTest(fileName);
}
@TestMetadata("NoAutopopupInFunName.kt")
public void testNoAutopopupInFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutopopupInFunName.kt");
doTest(fileName);
}
@TestMetadata("NoAutopopupInGenericExtFunName.kt")
public void testNoAutopopupInGenericExtFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutopopupInGenericExtFunName.kt");
doTest(fileName);
}
@TestMetadata("NoAutopopupInGenericFunName.kt")
public void testNoAutopopupInGenericFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutopopupInGenericFunName.kt");
@@ -1438,6 +1438,75 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FromUnresolvedNames extends AbstractJSBasicCompletionTest {
public void testAllFilesPresentInFromUnresolvedNames() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromUnresolvedNames"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("FunctionInCompanionObject.kt")
public void testFunctionInCompanionObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/FunctionInCompanionObject.kt");
doTest(fileName);
}
@TestMetadata("LocalVal.kt")
public void testLocalVal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/LocalVal.kt");
doTest(fileName);
}
@TestMetadata("MemberFunction.kt")
public void testMemberFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/MemberFunction.kt");
doTest(fileName);
}
@TestMetadata("MemberProperty.kt")
public void testMemberProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/MemberProperty.kt");
doTest(fileName);
}
@TestMetadata("NotForExtensionFunction.kt")
public void testNotForExtensionFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/NotForExtensionFunction.kt");
doTest(fileName);
}
@TestMetadata("Parameter.kt")
public void testParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/Parameter.kt");
doTest(fileName);
}
@TestMetadata("TopLevelClass.kt")
public void testTopLevelClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/TopLevelClass.kt");
doTest(fileName);
}
@TestMetadata("TopLevelFunction.kt")
public void testTopLevelFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/TopLevelFunction.kt");
doTest(fileName);
}
@TestMetadata("TopLevelInterface.kt")
public void testTopLevelInterface() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/TopLevelInterface.kt");
doTest(fileName);
}
@TestMetadata("TopLevelObject.kt")
public void testTopLevelObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/TopLevelObject.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -990,6 +990,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("InExtFunName.kt")
public void testInExtFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/InExtFunName.kt");
doTest(fileName);
}
@TestMetadata("InGenericExtFunName.kt")
public void testInGenericExtFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/InGenericExtFunName.kt");
doTest(fileName);
}
@TestMetadata("NoAutoPopupAfterNumberLiteral.kt")
public void testNoAutoPopupAfterNumberLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutoPopupAfterNumberLiteral.kt");
@@ -1002,24 +1014,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("NoAutopopupInExtFunName.kt")
public void testNoAutopopupInExtFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutopopupInExtFunName.kt");
doTest(fileName);
}
@TestMetadata("NoAutopopupInFunName.kt")
public void testNoAutopopupInFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutopopupInFunName.kt");
doTest(fileName);
}
@TestMetadata("NoAutopopupInGenericExtFunName.kt")
public void testNoAutopopupInGenericExtFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutopopupInGenericExtFunName.kt");
doTest(fileName);
}
@TestMetadata("NoAutopopupInGenericFunName.kt")
public void testNoAutopopupInGenericFunName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/autoPopup/NoAutopopupInGenericFunName.kt");
@@ -1438,6 +1438,75 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FromUnresolvedNames extends AbstractJvmBasicCompletionTest {
public void testAllFilesPresentInFromUnresolvedNames() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromUnresolvedNames"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("FunctionInCompanionObject.kt")
public void testFunctionInCompanionObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/FunctionInCompanionObject.kt");
doTest(fileName);
}
@TestMetadata("LocalVal.kt")
public void testLocalVal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/LocalVal.kt");
doTest(fileName);
}
@TestMetadata("MemberFunction.kt")
public void testMemberFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/MemberFunction.kt");
doTest(fileName);
}
@TestMetadata("MemberProperty.kt")
public void testMemberProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/MemberProperty.kt");
doTest(fileName);
}
@TestMetadata("NotForExtensionFunction.kt")
public void testNotForExtensionFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/NotForExtensionFunction.kt");
doTest(fileName);
}
@TestMetadata("Parameter.kt")
public void testParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/Parameter.kt");
doTest(fileName);
}
@TestMetadata("TopLevelClass.kt")
public void testTopLevelClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/TopLevelClass.kt");
doTest(fileName);
}
@TestMetadata("TopLevelFunction.kt")
public void testTopLevelFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/TopLevelFunction.kt");
doTest(fileName);
}
@TestMetadata("TopLevelInterface.kt")
public void testTopLevelInterface() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/TopLevelInterface.kt");
doTest(fileName);
}
@TestMetadata("TopLevelObject.kt")
public void testTopLevelObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromUnresolvedNames/TopLevelObject.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -37,6 +37,11 @@ abstract class KotlinFixtureCompletionBaseTestCase : KotlinLightCodeInsightFixtu
setUpFixture(testPath)
val fileText = FileUtil.loadFile(File(testPath), true)
if (ExpectedCompletionUtils.shouldRunHighlightingBeforeCompletion(fileText)) {
myFixture.doHighlighting()
}
testCompletion(fileText, getPlatform(), { completionType, count -> complete(completionType, count) }, defaultCompletionType(), defaultInvocationCount())
}