Faster completion of non-imported extensions
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.jet.lang.psi;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.stubs.IStubElementType;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -38,4 +39,13 @@ abstract class JetDeclarationStub<T extends StubElement> extends JetModifierList
|
||||
public KDoc getDocComment() {
|
||||
return FindDocCommentPackage.findDocComment(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement getParent() {
|
||||
T stub = getStub();
|
||||
if (stub != null) {
|
||||
return stub.getParentStub().getPsi();
|
||||
}
|
||||
return super.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,7 @@ import org.jetbrains.jet.lang.psi.*
|
||||
import org.jetbrains.jet.lang.resolve.*
|
||||
import org.jetbrains.jet.lang.resolve.name.Name
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils
|
||||
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||
import com.intellij.openapi.project.Project
|
||||
import java.util.HashSet
|
||||
@@ -37,6 +35,8 @@ import org.jetbrains.jet.lang.resolve.bindingContextUtil.getDataFlowInfo
|
||||
import org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo
|
||||
import com.intellij.psi.stubs.StringStubIndexExtension
|
||||
import org.jetbrains.jet.plugin.caches.resolve.getLazyResolveSession
|
||||
|
||||
public class KotlinIndicesHelper(private val project: Project,
|
||||
private val resolveSession: ResolveSessionForBodies,
|
||||
@@ -138,58 +138,81 @@ public class KotlinIndicesHelper(private val project: Project,
|
||||
val bindingContext = resolveSession.resolveToElement(expression)
|
||||
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
||||
|
||||
val sourceNames = JetTopLevelFunctionsFqnNameIndex.getInstance().getAllKeys(project).stream() +
|
||||
JetTopLevelPropertiesFqnNameIndex.getInstance().getAllKeys(project).stream()
|
||||
val allFqNames = sourceNames.map { FqName(it) } + JetFromJavaDescriptorHelper.getTopLevelCallableFqNames(project, scope, true).stream()
|
||||
val matchingFqNames = allFqNames.filter { nameFilter(it.shortName().asString()) }.toSet()
|
||||
val functionsIndex = JetTopLevelFunctionsFqnNameIndex.getInstance()
|
||||
val propertiesIndex = JetTopLevelPropertiesFqnNameIndex.getInstance()
|
||||
|
||||
val sourceFunctionNames = functionsIndex.getAllKeys(project).stream().map { FqName(it) }
|
||||
val sourcePropertyNames = propertiesIndex.getAllKeys(project).stream().map { FqName(it) }
|
||||
val compiledFqNames = JetFromJavaDescriptorHelper.getTopLevelCallableFqNames(project, scope, true).stream()
|
||||
|
||||
val result = HashSet<CallableDescriptor>()
|
||||
result.fqNamesToSuitableExtensions(sourceFunctionNames, nameFilter, functionsIndex, expression, bindingContext, dataFlowInfo)
|
||||
result.fqNamesToSuitableExtensions(sourcePropertyNames, nameFilter, propertiesIndex, expression, bindingContext, dataFlowInfo)
|
||||
result.fqNamesToSuitableExtensions(compiledFqNames, nameFilter, null, expression, bindingContext, dataFlowInfo)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun MutableCollection<CallableDescriptor>.fqNamesToSuitableExtensions(
|
||||
fqNames: Stream<FqName>,
|
||||
nameFilter: (String) -> Boolean,
|
||||
index: StringStubIndexExtension<out JetCallableDeclaration>?,
|
||||
expression: JetSimpleNameExpression,
|
||||
bindingContext: BindingContext,
|
||||
dataFlowInfo: DataFlowInfo) {
|
||||
val matchingNames = fqNames.filter { nameFilter(it.shortName().asString()) }
|
||||
|
||||
val receiverExpression = expression.getReceiverExpression()
|
||||
if (receiverExpression != null) {
|
||||
val isInfixCall = expression.getParent() is JetBinaryExpression
|
||||
|
||||
val expressionType = bindingContext.get<JetExpression, JetType>(BindingContext.EXPRESSION_TYPE, receiverExpression)
|
||||
if (expressionType == null || expressionType.isError()) return listOf()
|
||||
val expressionType = bindingContext[BindingContext.EXPRESSION_TYPE, receiverExpression]
|
||||
if (expressionType == null || expressionType.isError()) return
|
||||
|
||||
val resolutionScope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, receiverExpression) ?: return listOf()
|
||||
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, receiverExpression] ?: return
|
||||
|
||||
val receiverValue = ExpressionReceiver(receiverExpression, expressionType)
|
||||
|
||||
return matchingFqNames.flatMap {
|
||||
findSuitableExtensions(it, receiverValue, dataFlowInfo, isInfixCall, resolutionScope, moduleDescriptor, bindingContext)
|
||||
matchingNames.flatMapTo(this) {
|
||||
findSuitableExtensions(it, index, receiverValue, dataFlowInfo, isInfixCall, resolutionScope, moduleDescriptor, bindingContext).stream()
|
||||
}
|
||||
}
|
||||
else {
|
||||
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, expression] ?: return listOf()
|
||||
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, expression] ?: return
|
||||
|
||||
val result = HashSet<CallableDescriptor>()
|
||||
for (receiver in resolutionScope.getImplicitReceiversHierarchy()) {
|
||||
matchingFqNames.flatMapTo(result) {
|
||||
findSuitableExtensions(it, receiver.getValue(), dataFlowInfo, false, resolutionScope, moduleDescriptor, bindingContext)
|
||||
matchingNames.flatMapTo(this) {
|
||||
findSuitableExtensions(it, index, receiver.getValue(), dataFlowInfo, false, resolutionScope, moduleDescriptor, bindingContext).stream()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
|
||||
*/
|
||||
private fun findSuitableExtensions(callableFQN: FqName,
|
||||
index: StringStubIndexExtension<out JetCallableDeclaration>?,
|
||||
receiverValue: ReceiverValue,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
isInfixCall: Boolean,
|
||||
scope: JetScope,
|
||||
resolutionScope: JetScope,
|
||||
module: ModuleDescriptor,
|
||||
bindingContext: BindingContext): List<CallableDescriptor> {
|
||||
val importDirective = JetPsiFactory(project).createImportDirective(callableFQN.asString())
|
||||
val declarationDescriptors = analyzeImportReference(importDirective, scope, BindingTraceContext(), module)
|
||||
bindingContext: BindingContext): Collection<CallableDescriptor> {
|
||||
val fqnString = callableFQN.asString()
|
||||
val descriptors = if (index != null) {
|
||||
index.get(fqnString, project, scope)
|
||||
.filter { it.getReceiverTypeReference() != null }
|
||||
.map { it.getLazyResolveSession().resolveToDescriptor(it) as CallableDescriptor }
|
||||
}
|
||||
else {
|
||||
val importDirective = JetPsiFactory(project).createImportDirective(fqnString)
|
||||
analyzeImportReference(importDirective, resolutionScope, BindingTraceContext(), module)
|
||||
.filterIsInstance(javaClass<CallableDescriptor>())
|
||||
.filter { it.getExtensionReceiverParameter() != null }
|
||||
}
|
||||
|
||||
return declarationDescriptors
|
||||
.filterIsInstance(javaClass<CallableDescriptor>())
|
||||
.filter { it.getExtensionReceiverParameter() != null &&
|
||||
visibilityFilter(it) &&
|
||||
ExpressionTypingUtils.checkIsExtensionCallable(receiverValue, it, isInfixCall, bindingContext, dataFlowInfo) }
|
||||
return descriptors.filter { visibilityFilter(it) &&
|
||||
ExpressionTypingUtils.checkIsExtensionCallable(receiverValue, it, isInfixCall, bindingContext, dataFlowInfo) }
|
||||
}
|
||||
|
||||
public fun getClassDescriptors(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
|
||||
|
||||
@@ -30,9 +30,11 @@ import org.jetbrains.jet.plugin.project.ProjectStructureUtil
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
|
||||
import org.jetbrains.jet.plugin.caches.KotlinIndicesHelper
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
|
||||
class AllClassesCompletion(val parameters: CompletionParameters,
|
||||
val resolveSession: ResolveSessionForBodies,
|
||||
val scope: GlobalSearchScope,
|
||||
val prefixMatcher: PrefixMatcher,
|
||||
val kindFilter: (ClassKind) -> Boolean,
|
||||
val visibilityFilter: (DeclarationDescriptor) -> Boolean) {
|
||||
@@ -40,14 +42,11 @@ class AllClassesCompletion(val parameters: CompletionParameters,
|
||||
val builtIns = KotlinBuiltIns.getInstance().getNonPhysicalClasses().filter { kindFilter(it.getKind()) && prefixMatcher.prefixMatches(it.getName().asString()) }
|
||||
result.addDescriptorElements(builtIns, suppressAutoInsertion = true)
|
||||
|
||||
val file = parameters.getOriginalFile()
|
||||
val project = file.getProject()
|
||||
val searchScope = file.getResolveScope()
|
||||
val helper = KotlinIndicesHelper(project, resolveSession, searchScope, visibilityFilter)
|
||||
val helper = KotlinIndicesHelper(scope.getProject(), resolveSession, scope, visibilityFilter)
|
||||
result.addDescriptorElements(helper.getClassDescriptors({ prefixMatcher.prefixMatches(it) }, kindFilter),
|
||||
suppressAutoInsertion = true)
|
||||
|
||||
if (!ProjectStructureUtil.isJsKotlinModule(file as JetFile)) {
|
||||
if (!ProjectStructureUtil.isJsKotlinModule(parameters.getOriginalFile() as JetFile)) {
|
||||
addAdaptedJavaCompletion(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ import org.jetbrains.jet.plugin.references.JetSimpleNameReference
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
|
||||
import org.jetbrains.jet.plugin.caches.KotlinIndicesHelper
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
import java.util.HashSet
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||
|
||||
class CompletionSessionConfiguration(
|
||||
@@ -62,7 +65,12 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
protected val collector: LookupElementsCollector = LookupElementsCollector(prefixMatcher, resolveSession)
|
||||
|
||||
protected val project: Project = position.getProject()
|
||||
protected val searchScope: GlobalSearchScope = parameters.getOriginalFile().getResolveScope()
|
||||
|
||||
// we need to exclude the original file from scope because our resolve session is built with this file replaced by synthetic one
|
||||
protected val searchScope: GlobalSearchScope = object : DelegatingGlobalSearchScope(parameters.getOriginalFile().getResolveScope()) {
|
||||
override fun contains(file: VirtualFile) = super.contains(file) && file != parameters.getOriginalFile().getVirtualFile()
|
||||
}
|
||||
|
||||
protected val indicesHelper: KotlinIndicesHelper = KotlinIndicesHelper(project, resolveSession, searchScope) { isVisibleDescriptor(it) }
|
||||
|
||||
protected fun isVisibleDescriptor(descriptor: DeclarationDescriptor): Boolean {
|
||||
@@ -123,7 +131,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
||||
}
|
||||
|
||||
protected fun addAllClasses(kindFilter: (ClassKind) -> Boolean) {
|
||||
AllClassesCompletion(parameters, resolveSession, prefixMatcher, kindFilter, { isVisibleDescriptor(it) }).collect(collector)
|
||||
AllClassesCompletion(parameters, resolveSession, searchScope, prefixMatcher, kindFilter, { isVisibleDescriptor(it) }).collect(collector)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user