Code completion of "this@..."
#KT-2035 Fixed
This commit is contained in:
@@ -41,9 +41,11 @@ import kotlin.properties.Delegates
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||
import org.jetbrains.jet.plugin.util.CallType
|
||||
import org.jetbrains.jet.plugin.completion.isVisible
|
||||
import org.jetbrains.jet.lang.resolve.scopes.DescriptorKindExclude
|
||||
import org.jetbrains.jet.lang.resolve.lazy.BodyResolveMode
|
||||
import com.intellij.patterns.StandardPatterns
|
||||
import com.intellij.util.ProcessingContext
|
||||
import com.intellij.patterns.PatternCondition
|
||||
|
||||
class CompletionSessionConfiguration(
|
||||
val completeNonImportedDeclarations: Boolean,
|
||||
@@ -57,17 +59,49 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
protected val parameters: CompletionParameters,
|
||||
resultSet: CompletionResultSet) {
|
||||
protected val position: PsiElement = parameters.getPosition()
|
||||
protected val jetReference: JetSimpleNameReference? = position.getParent()?.getReferences()?.firstIsInstanceOrNull<JetSimpleNameReference>()
|
||||
private val file = position.getContainingFile() as JetFile
|
||||
protected val resolutionFacade: ResolutionFacade = file.getResolutionFacade()
|
||||
protected val moduleDescriptor: ModuleDescriptor = resolutionFacade.findModuleDescriptor(file)
|
||||
protected val bindingContext: BindingContext? = jetReference?.let { resolutionFacade.analyze(it.expression, BodyResolveMode.PARTIAL_FOR_COMPLETION) }
|
||||
protected val inDescriptor: DeclarationDescriptor? = jetReference?.let { bindingContext!!.get(BindingContext.RESOLUTION_SCOPE, it.expression)?.getContainingDeclaration() }
|
||||
|
||||
// set prefix matcher here to override default one which relies on CompletionUtil.findReferencePrefix()
|
||||
// which sometimes works incorrectly for Kotlin
|
||||
protected val reference: JetSimpleNameReference?
|
||||
protected val expression: JetExpression?
|
||||
|
||||
;{
|
||||
val reference = position.getParent()?.getReferences()?.firstIsInstanceOrNull<JetSimpleNameReference>()
|
||||
if (reference != null) {
|
||||
if (reference.expression is JetLabelReferenceExpression) {
|
||||
this.expression = reference.expression.getParent().getParent() as? JetThisExpression
|
||||
this.reference = null
|
||||
}
|
||||
else {
|
||||
this.expression = reference.expression
|
||||
this.reference = reference
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.reference = null
|
||||
this.expression = null
|
||||
}
|
||||
}
|
||||
|
||||
protected val bindingContext: BindingContext? = expression?.let { resolutionFacade.analyze(it, BodyResolveMode.PARTIAL_FOR_COMPLETION) }
|
||||
protected val inDescriptor: DeclarationDescriptor? = expression?.let { bindingContext!!.get(BindingContext.RESOLUTION_SCOPE, it)?.getContainingDeclaration() }
|
||||
|
||||
protected val prefix: String = CompletionUtil.findIdentifierPrefix(
|
||||
parameters.getPosition().getContainingFile(),
|
||||
parameters.getOffset(),
|
||||
StandardPatterns.or(StandardPatterns.character().javaIdentifierPart(),
|
||||
StandardPatterns.character().with(
|
||||
object : PatternCondition<Char>("@") {
|
||||
override fun accepts(c: Char?, context: ProcessingContext?): Boolean {
|
||||
return c == '@'
|
||||
}
|
||||
})
|
||||
),
|
||||
StandardPatterns.character().javaIdentifierStart())
|
||||
|
||||
protected val resultSet: CompletionResultSet = resultSet
|
||||
.withPrefixMatcher(CompletionUtil.findJavaIdentifierPrefix(parameters))
|
||||
.withPrefixMatcher(prefix)
|
||||
.addKotlinSorting(parameters)
|
||||
|
||||
protected val prefixMatcher: PrefixMatcher = this.resultSet.getPrefixMatcher()
|
||||
@@ -76,8 +110,8 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
= if (bindingContext != null) ReferenceVariantsHelper(bindingContext) { isVisibleDescriptor(it) } else null
|
||||
|
||||
protected val lookupElementFactory: LookupElementFactory = run {
|
||||
val receiverTypes = if (jetReference != null) {
|
||||
val expression = jetReference.expression
|
||||
val receiverTypes = if (reference != null) {
|
||||
val expression = reference.expression
|
||||
val (receivers, callType) = referenceVariantsHelper!!.getReferenceVariantsReceivers(expression)
|
||||
val dataFlowInfo = bindingContext!!.getDataFlowInfo(expression)
|
||||
var receiverTypes = receivers.flatMap {
|
||||
@@ -113,7 +147,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
if (configuration.completeNonAccessibleDeclarations) return true
|
||||
|
||||
if (descriptor is DeclarationDescriptorWithVisibility && inDescriptor != null) {
|
||||
return descriptor.isVisible(inDescriptor, bindingContext, jetReference?.expression)
|
||||
return descriptor.isVisible(inDescriptor, bindingContext, reference?.expression)
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -135,7 +169,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
private var alreadyAddedDescriptors: Collection<DeclarationDescriptor> by Delegates.notNull()
|
||||
|
||||
fun getReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean): Collection<DeclarationDescriptor> {
|
||||
val descriptors = referenceVariantsHelper!!.getReferenceVariants(jetReference!!.expression, kindFilter, shouldCastToRuntimeType, prefixMatcher.asNameFilter())
|
||||
val descriptors = referenceVariantsHelper!!.getReferenceVariants(reference!!.expression, kindFilter, shouldCastToRuntimeType, prefixMatcher.asNameFilter())
|
||||
if (!shouldCastToRuntimeType) {
|
||||
if (position.getContainingFile() is JetCodeFragment) {
|
||||
alreadyAddedDescriptors = descriptors
|
||||
@@ -160,13 +194,13 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
}
|
||||
|
||||
protected fun shouldRunExtensionsCompletion(): Boolean
|
||||
= configuration.completeNonImportedDeclarations || prefixMatcher.getPrefix().length >= 3
|
||||
= configuration.completeNonImportedDeclarations || prefixMatcher.getPrefix().length() >= 3
|
||||
|
||||
protected fun getKotlinTopLevelCallables(): Collection<DeclarationDescriptor>
|
||||
= indicesHelper.getTopLevelCallables({ prefixMatcher.prefixMatches(it) }, jetReference!!.expression)
|
||||
= indicesHelper.getTopLevelCallables({ prefixMatcher.prefixMatches(it) }, reference!!.expression)
|
||||
|
||||
protected fun getKotlinExtensions(): Collection<CallableDescriptor>
|
||||
= indicesHelper.getCallableExtensions({ prefixMatcher.prefixMatches(it) }, jetReference!!.expression)
|
||||
= indicesHelper.getCallableExtensions({ prefixMatcher.prefixMatches(it) }, reference!!.expression)
|
||||
|
||||
protected fun addAllClasses(kindFilter: (ClassKind) -> Boolean) {
|
||||
AllClassesCompletion(
|
||||
@@ -185,7 +219,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
assert(parameters.getCompletionType() == CompletionType.BASIC)
|
||||
|
||||
if (!NamedParametersCompletion.isOnlyNamedParameterExpected(position)) {
|
||||
val completeReference = jetReference != null && !isOnlyKeywordCompletion()
|
||||
val completeReference = reference != null && !isOnlyKeywordCompletion()
|
||||
val onlyTypes = completeReference && shouldRunOnlyTypeCompletion()
|
||||
|
||||
val kindFilter = if (onlyTypes)
|
||||
@@ -197,7 +231,20 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
addReferenceVariants(kindFilter, shouldCastToRuntimeType = false)
|
||||
}
|
||||
|
||||
KeywordCompletion.complete(parameters, prefixMatcher.getPrefix(), collector)
|
||||
val ampIndex = prefix.indexOf("@")
|
||||
val keywordsPrefix = if (ampIndex < 0) prefix else prefix.substring(0, ampIndex) // if there is '@' in the prefix - use shorter prefix to not loose 'this' etc
|
||||
KeywordCompletion.complete(expression ?: parameters.getPosition(), keywordsPrefix) { lookupElement ->
|
||||
when (lookupElement.getLookupString()) {
|
||||
// if "this" is parsed correctly in the current context - insert it and all this@xxx items
|
||||
"this" -> {
|
||||
if (expression != null) {
|
||||
collector.addElements(thisExpressionItems(bindingContext!!, expression).map { it.factory() })
|
||||
}
|
||||
}
|
||||
|
||||
else -> collector.addElement(lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
if (completeReference) {
|
||||
if (!configuration.completeNonImportedDeclarations && isNoQualifierContext()) {
|
||||
@@ -240,7 +287,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
val typeReference = position.getStrictParentOfType<JetTypeReference>()
|
||||
if (typeReference != null) {
|
||||
val firstPartReference = PsiTreeUtil.findChildOfType(typeReference, javaClass<JetSimpleNameExpression>())
|
||||
return firstPartReference == jetReference!!.expression
|
||||
return firstPartReference == reference!!.expression
|
||||
}
|
||||
|
||||
return false
|
||||
@@ -261,33 +308,36 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
|
||||
private val DESCRIPTOR_KIND_MASK = DescriptorKindFilter.VALUES exclude SamConstructorDescriptorKindExclude
|
||||
|
||||
override fun doComplete() {
|
||||
if (jetReference != null) {
|
||||
if (expression != null) {
|
||||
val mapper = ToFromOriginalFileMapper(parameters.getOriginalFile() as JetFile, position.getContainingFile() as JetFile, parameters.getOffset())
|
||||
val completion = SmartCompletion(jetReference.expression, resolutionFacade, moduleDescriptor,
|
||||
bindingContext!!, { isVisibleDescriptor(it) }, originalSearchScope,
|
||||
val completion = SmartCompletion(expression, resolutionFacade, moduleDescriptor,
|
||||
bindingContext!!, { isVisibleDescriptor(it) }, prefixMatcher, originalSearchScope,
|
||||
mapper, lookupElementFactory)
|
||||
val result = completion.execute()
|
||||
if (result != null) {
|
||||
collector.addElements(result.additionalItems)
|
||||
|
||||
val filter = result.declarationFilter
|
||||
if (filter != null) {
|
||||
getReferenceVariants(DESCRIPTOR_KIND_MASK, false).forEach { collector.addElements(filter(it)) }
|
||||
flushToResultSet()
|
||||
if (reference != null) {
|
||||
val filter = result.declarationFilter
|
||||
if (filter != null) {
|
||||
getReferenceVariants(DESCRIPTOR_KIND_MASK, false).forEach { collector.addElements(filter(it)) }
|
||||
flushToResultSet()
|
||||
|
||||
processNonImported { collector.addElements(filter(it)) }
|
||||
flushToResultSet()
|
||||
processNonImported { collector.addElements(filter(it)) }
|
||||
flushToResultSet()
|
||||
|
||||
if (position.getContainingFile() is JetCodeFragment) {
|
||||
getReferenceVariants(DESCRIPTOR_KIND_MASK, true).forEach { collector.addElementsWithReceiverCast(filter(it)) }
|
||||
if (position.getContainingFile() is JetCodeFragment) {
|
||||
getReferenceVariants(DESCRIPTOR_KIND_MASK, true).forEach { collector.addElementsWithReceiverCast(filter(it)) }
|
||||
flushToResultSet()
|
||||
}
|
||||
}
|
||||
|
||||
// it makes no sense to search inheritors if there is no reference because it means that we have prefix like "this@"
|
||||
result.inheritanceSearcher?.search({ prefixMatcher.prefixMatches(it) }) {
|
||||
collector.addElement(it)
|
||||
flushToResultSet()
|
||||
}
|
||||
}
|
||||
|
||||
result.inheritanceSearcher?.search({ prefixMatcher.prefixMatches(it) }) {
|
||||
collector.addElement(it)
|
||||
flushToResultSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,19 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils
|
||||
import com.intellij.openapi.editor.Document
|
||||
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
|
||||
import org.jetbrains.jet.lang.psi.JetValueArgument
|
||||
import org.jetbrains.jet.lang.psi.JetValueArgumentList
|
||||
import org.jetbrains.jet.lang.psi.JetCallExpression
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.jet.plugin.util.getImplicitReceiversWithInstance
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.plugin.util.FuzzyType
|
||||
|
||||
enum class ItemPriority {
|
||||
MULTIPLE_ARGUMENTS_ITEM
|
||||
@@ -185,3 +197,55 @@ fun InsertionContext.isAfterDot(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
// do not complete this items by prefix like "is"
|
||||
fun shouldCompleteThisItems(prefixMatcher: PrefixMatcher): Boolean {
|
||||
val prefix = prefixMatcher.getPrefix()
|
||||
val s = "this@"
|
||||
return if (prefix.length() > s.length())
|
||||
prefix.startsWith(s)
|
||||
else
|
||||
s.startsWith(prefix)
|
||||
}
|
||||
|
||||
data class ThisItemInfo(val factory: () -> LookupElement, val type: FuzzyType)
|
||||
|
||||
fun thisExpressionItems(bindingContext: BindingContext, context: JetExpression): Collection<ThisItemInfo> {
|
||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] ?: return listOf()
|
||||
|
||||
val result = ArrayList<ThisItemInfo>()
|
||||
for ((i, receiver) in scope.getImplicitReceiversWithInstance().withIndex()) {
|
||||
val thisType = receiver.getType()
|
||||
val fuzzyType = FuzzyType(thisType, listOf())
|
||||
val qualifier = if (i == 0) null else (thisQualifierName(receiver) ?: continue)
|
||||
|
||||
fun createLookupElement(): LookupElement {
|
||||
var element = LookupElementBuilder.create(KeywordLookupObject, if (qualifier == null) "this" else "this@" + qualifier)
|
||||
element = element.withPresentableText("this")
|
||||
element = element.withBoldness(true)
|
||||
if (qualifier != null) {
|
||||
element = element.withTailText("@" + qualifier, false)
|
||||
}
|
||||
element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType))
|
||||
return element
|
||||
}
|
||||
|
||||
result.add(ThisItemInfo({ createLookupElement() }, fuzzyType))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun thisQualifierName(receiver: ReceiverParameterDescriptor): String? {
|
||||
val descriptor = receiver.getContainingDeclaration()
|
||||
val name = descriptor.getName()
|
||||
if (!name.isSpecial()) {
|
||||
return name.asString()
|
||||
}
|
||||
|
||||
val functionLiteral = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? JetFunctionLiteral
|
||||
val valueArgument = (functionLiteral?.getParent() as? JetFunctionLiteralExpression)
|
||||
?.getParent() as? JetValueArgument ?: return null
|
||||
val parent = valueArgument.getParent()
|
||||
val callExpression = (if (parent is JetValueArgumentList) parent else valueArgument)
|
||||
.getParent() as? JetCallExpression
|
||||
return (callExpression?.getCalleeExpression() as? JetSimpleNameExpression)?.getReferencedName()
|
||||
}
|
||||
|
||||
@@ -34,8 +34,9 @@ import org.jetbrains.jet.lexer.JetTokens.*
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.prevLeafSkipWhitespacesAndComments
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getNonStrictParentOfType
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
|
||||
class KeywordLookupObject(val keyword: String)
|
||||
object KeywordLookupObject
|
||||
|
||||
object KeywordCompletion {
|
||||
private val NON_ACTUAL_KEYWORDS = setOf(REIFIED_KEYWORD,
|
||||
@@ -47,22 +48,20 @@ object KeywordCompletion {
|
||||
|
||||
private val KEYWORD_TO_DUMMY_POSTFIX = mapOf(OUT_KEYWORD to " X")
|
||||
|
||||
public fun complete(parameters: CompletionParameters, prefix: String, collector: LookupElementsCollector) {
|
||||
val position = parameters.getPosition()
|
||||
|
||||
public fun complete(position: PsiElement, prefix: String, consumer: (LookupElement) -> Unit) {
|
||||
if (!GENERAL_FILTER.isAcceptable(position, position)) return
|
||||
|
||||
val parserFilter = buildFilter(position)
|
||||
for (keywordToken in ALL_KEYWORDS) {
|
||||
val keyword = keywordToken.getValue()
|
||||
if (keyword.startsWith(prefix)/* use simple matching by prefix, not prefix matcher from completion*/ && parserFilter(keywordToken)) {
|
||||
val element = LookupElementBuilder.create(KeywordLookupObject(keyword), keyword)
|
||||
val element = LookupElementBuilder.create(KeywordLookupObject, keyword)
|
||||
.bold()
|
||||
.withInsertHandler(if (keywordToken !in FUNCTION_KEYWORDS)
|
||||
KotlinKeywordInsertHandler
|
||||
else
|
||||
KotlinFunctionInsertHandler.NO_PARAMETERS_HANDLER)
|
||||
collector.addElement(element)
|
||||
consumer(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,10 +222,7 @@ object KeywordCompletion {
|
||||
}
|
||||
|
||||
private fun PsiElement.getStartOffsetInAncestor(ancestor: PsiElement): Int {
|
||||
val parent = getParent()!!
|
||||
return if (parent == ancestor)
|
||||
getStartOffsetInParent()
|
||||
else
|
||||
parent.getStartOffsetInAncestor(ancestor) + getStartOffsetInParent()
|
||||
if (ancestor == this) return 0
|
||||
return getParent()!!.getStartOffsetInAncestor(ancestor) + getStartOffsetInParent()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class KotlinCompletionCharFilter() : CharFilter() {
|
||||
if (lookup.getPsiFile() !is JetFile) return null
|
||||
if (!lookup.isCompletion()) return null
|
||||
|
||||
if (Character.isJavaIdentifierPart(c) || c == ':' /* used in '::xxx'*/) {
|
||||
if (Character.isJavaIdentifierPart(c) || c == ':' /* used in '::xxx'*/ || c == '@') {
|
||||
return CharFilter.Result.ADD_TO_PREFIX
|
||||
}
|
||||
|
||||
|
||||
@@ -29,24 +29,25 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||
import org.jetbrains.jet.plugin.util.makeNullable
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
||||
import org.jetbrains.jet.plugin.caches.resolve.resolveToDescriptor
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.jet.lang.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.jet.plugin.util.FuzzyType
|
||||
import org.jetbrains.jet.plugin.util.fuzzyReturnType
|
||||
import org.jetbrains.jet.plugin.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
|
||||
|
||||
trait InheritanceItemsSearcher {
|
||||
fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit)
|
||||
}
|
||||
|
||||
class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
class SmartCompletion(val expression: JetExpression,
|
||||
val resolutionFacade: ResolutionFacade,
|
||||
val moduleDescriptor: ModuleDescriptor,
|
||||
val bindingContext: BindingContext,
|
||||
val visibilityFilter: (DeclarationDescriptor) -> Boolean,
|
||||
val prefixMatcher: PrefixMatcher,
|
||||
val inheritorSearchScope: GlobalSearchScope,
|
||||
val toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
||||
val lookupElementFactory: LookupElementFactory) {
|
||||
@@ -99,7 +100,7 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
val asTypePositionResult = buildForAsTypePosition()
|
||||
if (asTypePositionResult != null) return asTypePositionResult
|
||||
|
||||
val receiver = expression.getReceiverExpression()
|
||||
val receiver = if (expression is JetSimpleNameExpression) expression.getReceiverExpression() else null
|
||||
val expressionWithType = if (receiver != null) {
|
||||
expression.getParent() as? JetExpression ?: return null
|
||||
}
|
||||
@@ -146,13 +147,15 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
TypeInstantiationItems(resolutionFacade, moduleDescriptor, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory)
|
||||
.addTo(additionalItems, inheritanceSearchers, expectedInfos)
|
||||
|
||||
StaticMembers(bindingContext, resolutionFacade, lookupElementFactory).addToCollection(additionalItems, expectedInfos, expression, itemsToSkip)
|
||||
if (expression is JetSimpleNameExpression) {
|
||||
StaticMembers(bindingContext, resolutionFacade, lookupElementFactory).addToCollection(additionalItems, expectedInfos, expression, itemsToSkip)
|
||||
}
|
||||
|
||||
ThisItems(bindingContext).addToCollection(additionalItems, expressionWithType, expectedInfos)
|
||||
additionalItems.addThisItems(expression, expectedInfos)
|
||||
|
||||
LambdaItems.addToCollection(additionalItems, functionExpectedInfos)
|
||||
|
||||
KeywordValues.addToCollection(additionalItems, filteredExpectedInfos/* use filteredExpectedInfos to not include null after == */, expressionWithType)
|
||||
KeywordValues.addToCollection(additionalItems, filteredExpectedInfos/* use filteredExpectedInfos to not include null after == */, expression)
|
||||
|
||||
MultipleArgumentsItemProvider(bindingContext, smartCastTypes).addToCollection(additionalItems, expectedInfos, expression)
|
||||
}
|
||||
@@ -168,6 +171,18 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
return Result(::filterDeclaration, additionalItems, inheritanceSearcher)
|
||||
}
|
||||
|
||||
private fun MutableCollection<LookupElement>.addThisItems(place: JetExpression, expectedInfos: Collection<ExpectedInfo>) {
|
||||
if (shouldCompleteThisItems(prefixMatcher)) {
|
||||
val items = thisExpressionItems(bindingContext, place)
|
||||
for ((factory, type) in items) {
|
||||
val classifier = { (expectedInfo: ExpectedInfo) -> type.classifyExpectedInfo(expectedInfo) }
|
||||
addLookupElements(null, expectedInfos, classifier) {
|
||||
factory().assignSmartCompletionPriority(SmartCompletionItemPriority.THIS)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.fuzzyTypes(smartCastTypes: (VariableDescriptor) -> Collection<JetType>): Collection<FuzzyType> {
|
||||
if (this is CallableDescriptor) {
|
||||
var returnType = fuzzyReturnType() ?: return listOf()
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.jet.plugin.completion.smart
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
|
||||
import org.jetbrains.jet.lang.psi.JetValueArgument
|
||||
import org.jetbrains.jet.lang.psi.JetValueArgumentList
|
||||
import org.jetbrains.jet.lang.psi.JetCallExpression
|
||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
|
||||
import org.jetbrains.jet.plugin.util.FuzzyType
|
||||
import org.jetbrains.jet.plugin.util.getImplicitReceiversWithInstance
|
||||
|
||||
class ThisItems(val bindingContext: BindingContext) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, context: JetExpression, expectedInfos: Collection<ExpectedInfo>) {
|
||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] ?: return
|
||||
|
||||
for ((i, receiver) in scope.getImplicitReceiversWithInstance().withIndex()) {
|
||||
val thisType = receiver.getType()
|
||||
val fuzzyType = FuzzyType(thisType, listOf())
|
||||
val classifier = { (expectedInfo: ExpectedInfo) -> fuzzyType.classifyExpectedInfo(expectedInfo) }
|
||||
fun createLookupElement(): LookupElement? {
|
||||
//TODO: use this code when KT-4258 fixed
|
||||
//val expressionText = if (i == 0) "this" else "this@" + (thisQualifierName(receiver, bindingContext) ?: return null)
|
||||
val qualifier = if (i == 0) null else (thisQualifierName(receiver) ?: return null)
|
||||
val expressionText = if (qualifier == null) "this" else "this@" + qualifier
|
||||
return LookupElementBuilder.create(expressionText)
|
||||
.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType))
|
||||
.assignSmartCompletionPriority(SmartCompletionItemPriority.THIS)
|
||||
}
|
||||
collection.addLookupElements(null, expectedInfos, classifier) { createLookupElement() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun thisQualifierName(receiver: ReceiverParameterDescriptor): String? {
|
||||
val descriptor = receiver.getContainingDeclaration()
|
||||
val name = descriptor.getName()
|
||||
if (!name.isSpecial()) {
|
||||
return name.asString()
|
||||
}
|
||||
|
||||
val functionLiteral = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? JetFunctionLiteral
|
||||
return (((((functionLiteral?.getParent() as? JetFunctionLiteralExpression)
|
||||
?.getParent() as? JetValueArgument)
|
||||
?.getParent() as? JetValueArgumentList)
|
||||
?.getParent() as? JetCallExpression)
|
||||
?.getCalleeExpression() as? JetSimpleNameExpression)
|
||||
?.getReferencedName()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun String.foo() {
|
||||
t<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// COMPLETION_TYPE: BASIC
|
||||
// ELEMENT: *
|
||||
// CHAR: @
|
||||
@@ -0,0 +1,11 @@
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun String.foo() {
|
||||
t@<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// COMPLETION_TYPE: BASIC
|
||||
// ELEMENT: *
|
||||
// CHAR: @
|
||||
@@ -16,10 +16,9 @@ fun foo(p: Int) {
|
||||
// EXIST: package
|
||||
// EXIST: return
|
||||
// EXIST: super
|
||||
// EXIST: this
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NUMBER: 17
|
||||
// NUMBER: 16
|
||||
|
||||
@@ -15,7 +15,6 @@ fun foo() {
|
||||
// EXIST: package
|
||||
// EXIST: return
|
||||
// EXIST: super
|
||||
// EXIST: this
|
||||
// EXIST: throw
|
||||
// EXIST: trait
|
||||
// EXIST: true
|
||||
@@ -24,4 +23,4 @@ fun foo() {
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NUMBER: 22
|
||||
// NUMBER: 21
|
||||
|
||||
@@ -11,10 +11,9 @@ fun foo() = <caret>
|
||||
// EXIST: package
|
||||
// EXIST: return
|
||||
// EXIST: super
|
||||
// EXIST: this
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NUMBER: 17
|
||||
// NUMBER: 16
|
||||
|
||||
@@ -12,10 +12,9 @@ val prop: Int
|
||||
// EXIST: package
|
||||
// EXIST: return
|
||||
// EXIST: super
|
||||
// EXIST: this
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NUMBER: 17
|
||||
// NUMBER: 16
|
||||
|
||||
@@ -11,10 +11,9 @@ fun foo(p: Int = <caret>)
|
||||
// EXIST: package
|
||||
// EXIST: return
|
||||
// EXIST: super
|
||||
// EXIST: this
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NUMBER: 17
|
||||
// NUMBER: 16
|
||||
|
||||
@@ -11,10 +11,9 @@ var a : Int = <caret>
|
||||
// EXIST: package
|
||||
// EXIST: return
|
||||
// EXIST: super
|
||||
// EXIST: this
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NUMBER: 17
|
||||
// NUMBER: 16
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun String.foo() {
|
||||
this@<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ABSENT: this
|
||||
// ABSENT: "this@foo"
|
||||
// EXIST: { lookupString: "this@Inner", itemText: "this", tailText: "@Inner", typeText: "Outer.Inner", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@Outer", itemText: "this", tailText: "@Outer", typeText: "Outer", attributes: "bold" }
|
||||
// NUMBER: 2
|
||||
@@ -0,0 +1,28 @@
|
||||
class Outer {
|
||||
class Nested {
|
||||
inner class Inner {
|
||||
fun String.foo() {
|
||||
takeHandler1 {
|
||||
takeHandler2({
|
||||
takeHandler3 { this<caret> }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun takeHandler1(handler: Int.() -> Unit){}
|
||||
fun takeHandler2(handler: Char.() -> Unit){}
|
||||
fun takeHandler3(handler: Char.() -> Unit){}
|
||||
|
||||
// INVOCATION_COUNT: 1
|
||||
// EXIST: { lookupString: "this", itemText: "this", tailText: null, typeText: "Char", attributes: "bold" }
|
||||
// ABSENT: "this@takeHandler3"
|
||||
// EXIST: { lookupString: "this@takeHandler2", itemText: "this", tailText: "@takeHandler2", typeText: "Char", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@takeHandler1", itemText: "this", tailText: "@takeHandler1", typeText: "Int", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@foo", itemText: "this", tailText: "@foo", typeText: "String", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@Inner", itemText: "this", tailText: "@Inner", typeText: "Outer.Nested.Inner", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@Nested", itemText: "this", tailText: "@Nested", typeText: "Outer.Nested", attributes: "bold" }
|
||||
// ABSENT: "this@Outer"
|
||||
// NUMBER: 6
|
||||
@@ -0,0 +1,12 @@
|
||||
fun is1(): Boolean{}
|
||||
fun is2(): Boolean{}
|
||||
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun String.foo() {
|
||||
is<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NUMBER: 0
|
||||
@@ -1,7 +1,13 @@
|
||||
class Foo{
|
||||
fun String.foo(){
|
||||
val foo : Foo = <caret>
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun String.foo() {
|
||||
val v: Any = this@<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: { lookupString:"this@Foo", typeText:"Foo" }
|
||||
// ABSENT: this
|
||||
// ABSENT: "this@foo"
|
||||
// EXIST: { lookupString: "this@Inner", itemText: "this", tailText: "@Inner", typeText: "Outer.Inner", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@Outer", itemText: "this", tailText: "@Outer", typeText: "Outer", attributes: "bold" }
|
||||
// NUMBER: 2
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
fun String.foo(){
|
||||
val s : String = <caret>
|
||||
class Outer {
|
||||
class Nested {
|
||||
inner class Inner {
|
||||
fun String.foo() {
|
||||
takeHandler1 {
|
||||
takeHandler2({
|
||||
takeHandler3 {
|
||||
takeHandler4 { val v: Any = <caret> }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: { lookupString:"this", typeText:"String" }
|
||||
fun takeHandler1(handler: Int.() -> Unit){}
|
||||
fun takeHandler2(handler: Char.() -> Unit){}
|
||||
fun takeHandler3(handler: Any?.() -> Unit){}
|
||||
fun takeHandler4(handler: Any.() -> Unit){}
|
||||
|
||||
// EXIST: { lookupString: "this", itemText: "this", tailText: null, typeText: "Any", attributes: "bold" }
|
||||
// ABSENT: "this@takeHandler4"
|
||||
// ABSENT: "this@takeHandler3"
|
||||
// EXIST: { lookupString: "this@takeHandler2", itemText: "this", tailText: "@takeHandler2", typeText: "Char", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@takeHandler1", itemText: "this", tailText: "@takeHandler1", typeText: "Int", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@foo", itemText: "this", tailText: "@foo", typeText: "String", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@Inner", itemText: "this", tailText: "@Inner", typeText: "Outer.Nested.Inner", attributes: "bold" }
|
||||
// EXIST: { lookupString: "this@Nested", itemText: "this", tailText: "@Nested", typeText: "Outer.Nested", attributes: "bold" }
|
||||
// ABSENT: "this@Outer"
|
||||
|
||||
@@ -276,6 +276,24 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("QualifiedThis.kt")
|
||||
public void testQualifiedThis() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/keywords/QualifiedThis.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("This.kt")
|
||||
public void testThis() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/keywords/This.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ThisPrefixMatching.kt")
|
||||
public void testThisPrefixMatching() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/keywords/ThisPrefixMatching.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("TopScope.kt")
|
||||
public void testTopScope() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/keywords/TopScope.kt");
|
||||
|
||||
+6
-1
@@ -19,7 +19,6 @@ package org.jetbrains.jet.completion.handlers;
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.jet.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.jet.JetTestUtils;
|
||||
import org.jetbrains.jet.test.InnerTestClasses;
|
||||
import org.jetbrains.jet.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -168,6 +167,12 @@ public class CompletionCharFilterTestGenerated extends AbstractCompletionCharFil
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("QualifiedThis.kt")
|
||||
public void testQualifiedThis() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/charFilter/QualifiedThis.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("RangeTyping.kt")
|
||||
public void testRangeTyping() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/charFilter/RangeTyping.kt");
|
||||
|
||||
Reference in New Issue
Block a user