Code completion of "this@..."

#KT-2035 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-12-26 02:09:08 +03:00
parent 2adcaef39c
commit 3b0fe8831b
21 changed files with 321 additions and 146 deletions
@@ -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
+28
View File
@@ -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
+28 -3
View File
@@ -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");
@@ -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");