Completion: insert cast to runtime type for receiver in code fragments

This commit is contained in:
Natalia Ukhorskaya
2014-11-28 14:18:06 +03:00
parent 73e71543c6
commit 732700e959
6 changed files with 108 additions and 17 deletions
@@ -33,6 +33,9 @@ import org.jetbrains.jet.lang.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.jet.lang.resolve.scopes.DescriptorKindFilter
import org.jetbrains.jet.lang.resolve.scopes.DescriptorKindExclude
import org.jetbrains.jet.plugin.util.extensionsUtils.isExtensionCallable
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
public class ReferenceVariantsHelper(
private val context: BindingContext,
@@ -42,14 +45,16 @@ public class ReferenceVariantsHelper(
public fun getReferenceVariants(
expression: JetSimpleNameExpression,
kindFilter: DescriptorKindFilter,
shouldCastToRuntimeType: Boolean,
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> {
return getReferenceVariantsNoVisibilityFilter(expression, kindFilter, nameFilter).filter(visibilityFilter)
return getReferenceVariantsNoVisibilityFilter(expression, kindFilter, shouldCastToRuntimeType, nameFilter).filter(visibilityFilter)
}
private fun getReferenceVariantsNoVisibilityFilter(
expression: JetSimpleNameExpression,
kindFilter: DescriptorKindFilter,
shouldCastToRuntimeType: Boolean,
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> {
val parent = expression.getParent()
@@ -75,7 +80,10 @@ public class ReferenceVariantsHelper(
qualifier.scope.getDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.Extensions, nameFilter).filterTo(descriptors, ::filterIfInfix)
}
val expressionType = context[BindingContext.EXPRESSION_TYPE, receiverExpression]
val expressionType = if (shouldCastToRuntimeType)
getQualifierRuntimeType(receiverExpression)
else
context[BindingContext.EXPRESSION_TYPE, receiverExpression]
if (expressionType != null && !expressionType.isError()) {
val receiverValue = ExpressionReceiver(receiverExpression, expressionType)
val dataFlowInfo = context.getDataFlowInfo(expression)
@@ -124,6 +132,15 @@ public class ReferenceVariantsHelper(
}
}
private fun getQualifierRuntimeType(receiver: JetExpression): JetType? {
val type = context[BindingContext.EXPRESSION_TYPE, receiver]
if (TypeUtils.canHaveSubtypes(JetTypeChecker.DEFAULT, type)) {
val evaluator = receiver.getContainingFile().getCopyableUserData(JetCodeFragment.RUNTIME_TYPE_EVALUATOR)
return evaluator?.invoke(receiver)
}
return type
}
private fun getReferenceVariantsReceiver(expression: JetSimpleNameExpression): JetExpression? {
val parent = expression.getParent()
val inPositionForCompletionWithReceiver = parent is JetCallExpression
@@ -121,8 +121,8 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
protected abstract fun doComplete()
protected fun getReferenceVariants(kindFilter: DescriptorKindFilter): Collection<DeclarationDescriptor>
= referenceVariantsHelper!!.getReferenceVariants(jetReference!!.expression, kindFilter, prefixMatcher.asNameFilter())
protected fun getReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean): Collection<DeclarationDescriptor>
= referenceVariantsHelper!!.getReferenceVariants(jetReference!!.expression, kindFilter, shouldCastToRuntimeType, prefixMatcher.asNameFilter())
protected fun shouldRunTopLevelCompletion(): Boolean
= configuration.completeNonImportedDeclarations && isNoQualifierContext()
@@ -161,11 +161,12 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
val completeReference = jetReference != null && !isOnlyKeywordCompletion()
val onlyTypes = completeReference && shouldRunOnlyTypeCompletion()
val kindMask = if (onlyTypes)
DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK
else
DescriptorKindFilter.ALL_KINDS_MASK
if (completeReference) {
val kindMask = if (onlyTypes)
DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK
else
DescriptorKindFilter.ALL_KINDS_MASK
addReferenceVariants(DescriptorKindFilter(kindMask))
if (onlyTypes) {
@@ -183,6 +184,11 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
flushToResultSet()
addNonImported(onlyTypes)
}
if (completeReference && position.getContainingFile() is JetCodeFragment) {
flushToResultSet()
addReferenceVariants(DescriptorKindFilter(kindMask), true)
}
}
NamedParametersCompletion.complete(position, collector)
@@ -217,8 +223,11 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
return false
}
private fun addReferenceVariants(kindFilter: DescriptorKindFilter) {
collector.addDescriptorElements(getReferenceVariants(kindFilter), suppressAutoInsertion = false)
private fun addReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean = false) {
collector.addDescriptorElements(
getReferenceVariants(kindFilter, shouldCastToRuntimeType),
suppressAutoInsertion = false,
shouldCastToRuntimeType = shouldCastToRuntimeType)
}
}
@@ -240,11 +249,16 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
val filter = result.declarationFilter
if (filter != null) {
getReferenceVariants(DESCRIPTOR_KIND_MASK).forEach { collector.addElements(filter(it)) }
getReferenceVariants(DESCRIPTOR_KIND_MASK, false).forEach { 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)) }
flushToResultSet()
}
}
result.inheritanceSearcher?.search({ prefixMatcher.prefixMatches(it) }) {
@@ -31,6 +31,10 @@ import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
import com.intellij.codeInsight.completion.PrefixMatcher
import org.jetbrains.jet.lang.resolve.name.Name
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.completion.InsertionContext
import org.jetbrains.jet.plugin.completion.handlers.CastReceiverInsertHandler
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor
enum class ItemPriority {
MULTIPLE_ARGUMENTS_ITEM
@@ -47,6 +51,15 @@ fun LookupElement.assignPriority(priority: ItemPriority): LookupElement {
fun LookupElement.suppressAutoInsertion() = AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(this)
fun LookupElement.shouldCastReceiver(): LookupElement {
return object: LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
super.handleInsert(context)
CastReceiverInsertHandler.handleInsert(context, getDelegate())
}
}
}
val KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY = Key<Unit>("KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY")
fun LookupElement.keepOldArgumentListOnTab(): LookupElement {
@@ -65,7 +78,7 @@ fun rethrowWithCancelIndicator(exception: ProcessCanceledException): ProcessCanc
return exception
}
fun qualifiedNameForSourceCode(descriptor: ClassDescriptor): String? {
fun qualifiedNameForSourceCode(descriptor: ClassifierDescriptor): String? {
val name = descriptor.getName()
if (name.isSpecial()) return null
val nameString = IdeDescriptorRenderers.SOURCE_CODE.renderName(name)
@@ -51,16 +51,21 @@ class LookupElementsCollector(
private set
public fun addDescriptorElements(descriptors: Iterable<DeclarationDescriptor>,
suppressAutoInsertion: Boolean // auto-insertion suppression is used for elements that require adding an import
suppressAutoInsertion: Boolean, // auto-insertion suppression is used for elements that require adding an import
shouldCastToRuntimeType: Boolean = false
) {
for (descriptor in descriptors) {
addDescriptorElements(descriptor, suppressAutoInsertion)
addDescriptorElements(descriptor, suppressAutoInsertion, shouldCastToRuntimeType)
}
}
public fun addDescriptorElements(descriptor: DeclarationDescriptor, suppressAutoInsertion: Boolean) {
public fun addDescriptorElements(descriptor: DeclarationDescriptor, suppressAutoInsertion: Boolean, shouldCastToRuntimeType: Boolean) {
run {
val lookupElement = boldImmediateLookupElementFactory.createLookupElement(resolutionFacade, descriptor)
var lookupElement = boldImmediateLookupElementFactory.createLookupElement(resolutionFacade, descriptor)
if (shouldCastToRuntimeType) {
lookupElement = lookupElement.shouldCastReceiver()
}
if (suppressAutoInsertion) {
addElementWithAutoInsertionSuppressed(lookupElement)
}
@@ -136,4 +141,8 @@ class LookupElementsCollector(
public fun addElements(elements: Iterable<LookupElement>) {
elements.forEach { addElement(it) }
}
public fun addElementsWithReceiverCast(elements: Iterable<LookupElement>) {
elements.forEach { addElement(it.shouldCastReceiver()) }
}
}
@@ -37,7 +37,15 @@ import org.jetbrains.jet.plugin.completion.DeclarationDescriptorLookupObject
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
import org.jetbrains.jet.lang.psi.JetBinaryExpression
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.psi.JetParenthesizedExpression
import org.jetbrains.jet.lang.psi.JetBinaryExpressionWithTypeRHS
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
import org.jetbrains.jet.lang.psi.JetCodeFragment
import org.jetbrains.jet.lang.psi.psiUtil.getStrictParentOfType
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.plugin.completion.qualifiedNameForSourceCode
public abstract class KotlinCallableInsertHandler : BaseDeclarationInsertHandler() {
public override fun handleInsert(context: InsertionContext, item: LookupElement) {
@@ -211,4 +219,34 @@ public class KotlinFunctionInsertHandler(val caretPosition : CaretPosition, val
= CodeStyleSettingsManager.getSettings(project)
.getCustomSettings(javaClass<JetCodeStyleSettings>())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
}
}
object CastReceiverInsertHandler : KotlinCallableInsertHandler() {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
super.handleInsert(context, item)
val expression = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), javaClass<JetSimpleNameExpression>(), false)
val qualifiedExpression = PsiTreeUtil.getParentOfType(expression, javaClass<JetQualifiedExpression>(), true)
if (qualifiedExpression != null) {
val receiver = qualifiedExpression.getReceiverExpression()
val descriptor = (item.getObject() as? DeclarationDescriptorLookupObject)?.descriptor as CallableDescriptor
val project = context.getProject()
val thisObj = if (descriptor.getExtensionReceiverParameter() != null) descriptor.getExtensionReceiverParameter() else descriptor.getDispatchReceiverParameter()
val fqName = qualifiedNameForSourceCode(thisObj.getType().getConstructor().getDeclarationDescriptor())
val parentCast = JetPsiFactory(project).createExpression("(expr as $fqName)") as JetParenthesizedExpression
val cast = parentCast.getExpression() as JetBinaryExpressionWithTypeRHS
cast.getLeft().replace(receiver)
val psiDocumentManager = PsiDocumentManager.getInstance(project)
psiDocumentManager.commitAllDocuments()
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.getDocument())
val expr = receiver.replace(parentCast) as JetParenthesizedExpression
ShortenReferences.process((expr.getExpression() as JetBinaryExpressionWithTypeRHS).getRight())
}
}
}
@@ -410,7 +410,7 @@ public class JetFunctionParameterInfoHandler implements ParameterInfoHandlerWith
};
Collection<DeclarationDescriptor> variants = new ReferenceVariantsHelper(bindingContext, visibilityFilter).getReferenceVariants(
callNameExpression, new DescriptorKindFilter(DescriptorKindFilter.FUNCTIONS_MASK | DescriptorKindFilter.CLASSIFIERS_MASK,
Collections.<DescriptorKindExclude>emptyList()), nameFilter);
Collections.<DescriptorKindExclude>emptyList()), false, nameFilter);
Collection<Pair<? extends DeclarationDescriptor, ResolutionFacade>> itemsToShow = new ArrayList<Pair<? extends DeclarationDescriptor, ResolutionFacade>>();
for (DeclarationDescriptor variant : variants) {