From 365bb3ad8a47634e0085c0e8efb5ecde65d0e0f3 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Mon, 13 Aug 2012 16:06:28 +0400 Subject: [PATCH] Use lazy resolve in completion --- .../jet/cli/jvm/compiler/TipsManager.java | 11 +- .../resolve/lazy/LazyBindingContextUtils.java | 201 ++++++++++++++++++ .../jet/plugin/caches/JetShortNamesCache.java | 26 ++- .../JetClassCompletionContributor.java | 71 +++---- .../completion/JetCompletionContributor.java | 68 +++--- .../JetExtensionReceiverTypeContributor.java | 8 +- .../completion/JetPackagesContributor.java | 8 +- .../project/AnalyzerFacadeWithCache.java | 109 ++++++++-- .../project/WholeProjectAnalyzerFacade.java | 22 +- 9 files changed, 409 insertions(+), 115 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyBindingContextUtils.java diff --git a/compiler/backend/src/org/jetbrains/jet/cli/jvm/compiler/TipsManager.java b/compiler/backend/src/org/jetbrains/jet/cli/jvm/compiler/TipsManager.java index 9c625a867d1..d1040cbefc1 100644 --- a/compiler/backend/src/org/jetbrains/jet/cli/jvm/compiler/TipsManager.java +++ b/compiler/backend/src/org/jetbrains/jet/cli/jvm/compiler/TipsManager.java @@ -51,12 +51,15 @@ public final class TipsManager { } @NotNull - public static Collection getReferenceVariants(JetSimpleNameExpression expression, BindingContext context) { + public static Collection getReferenceVariants( + @NotNull final JetSimpleNameExpression expression, + @NotNull final BindingContext context + ) { JetExpression receiverExpression = expression.getReceiverExpression(); if (receiverExpression != null) { // Process as call expression - final JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); - final JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression); + JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); + JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression); if (expressionType != null && resolutionScope != null) { if (!(expressionType instanceof NamespaceType)) { @@ -116,7 +119,7 @@ public final class TipsManager { } @NotNull - public static Collection getReferenceVariants(JetNamespaceHeader expression, BindingContext context) { + public static Collection getPackageReferenceVariants(JetSimpleNameExpression expression, BindingContext context) { JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); if (resolutionScope != null) { return excludeNonPackageDescriptors(resolutionScope.getAllDescriptors()); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyBindingContextUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyBindingContextUtils.java new file mode 100644 index 00000000000..654b936eb06 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyBindingContextUtils.java @@ -0,0 +1,201 @@ +/* + * Copyright 2010-2012 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.lang.resolve.lazy; + +import com.google.common.base.Predicates; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.di.InjectorForBodyResolve; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.resolve.scopes.WritableScope; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +/** + * @author Nikolay Krasko + */ +public class LazyBindingContextUtils { + private static final Logger LOG = Logger.getInstance("#" + LazyBindingContextUtils.class.getName()); + + private LazyBindingContextUtils() { + } + + private static class EmptyBodyResolveContext implements BodiesResolveContext { + @Override + public Map getClasses() { + return Collections.emptyMap(); + } + + @Override + public Map getObjects() { + return Collections.emptyMap(); + } + + @Override + public Map getProperties() { + return Collections.emptyMap(); + } + + @Override + public Map getFunctions() { + return Collections.emptyMap(); + } + + @Override + public Map getDeclaringScopes() { + return Collections.emptyMap(); + } + + @Override + public Map getScripts() { + return Collections.emptyMap(); + } + + @Override + public Map getScriptScopes() { + return Collections.emptyMap(); + } + + @Override + public void setTopDownAnalysisParameters(TopDownAnalysisParameters parameters) { + } + + @Override + public boolean completeAnalysisNeeded(@NotNull PsiElement element) { + return true; + } + } + + public static @NotNull BindingContext getExpressionBindingContext(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) { + final DelegatingBindingTrace trace = new DelegatingBindingTrace(resolveSession.getBindingContext()); + + JetFile file = (JetFile) expression.getContainingFile(); + + // Resolve enclosing function body + JetNamedFunction namedFunction = PsiTreeUtil.getTopmostParentOfType(expression, JetNamedFunction.class); + if (namedFunction != null) { + TopDownAnalysisParameters parameters = new TopDownAnalysisParameters( + Predicates.alwaysTrue(), false, true, Collections.emptyList()); + + InjectorForBodyResolve bodyResolve = new InjectorForBodyResolve(file.getProject(), parameters, trace, new EmptyBodyResolveContext()); + + final BodyResolver bodyResolver = bodyResolve.getBodyResolver(); + + if (namedFunction.getParent() instanceof JetFile || + namedFunction.getParent() instanceof JetClassBody || + namedFunction.getParent() instanceof JetClassObject) { + JetScope scope = resolveSession.getResolutionScope(namedFunction); + FunctionDescriptor functionDescriptor = (FunctionDescriptor) resolveSession.resolveToDescriptor(namedFunction); + bodyResolver.resolveFunctionBody(trace, namedFunction, functionDescriptor, scope); + } + else { + LOG.warn(String.format("Not expected parent for function: %s in %s", + namedFunction.getName(), namedFunction.getContainingFile())); + } + } + + if (trace.getBindingContext().get(BindingContext.RESOLUTION_SCOPE, expression) == null) { + JetScope scope = getExpressionMemberScope(resolveSession, expression); + if (scope != null) { + trace.record(BindingContext.RESOLUTION_SCOPE, expression, scope); + } + } + + return trace.getBindingContext(); + } + + private static JetScope getExpressionOuterScope(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) { + JetDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(expression, JetDeclaration.class); + // DeclarationDescriptor descriptor = resolveToDescriptor(parentDeclaration); + return resolveSession.getResolutionScope(parentDeclaration); + } + + public static JetScope getExpressionMemberScope(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) { + DelegatingBindingTrace trace = new DelegatingBindingTrace(resolveSession.getBindingContext()); + + if (expression instanceof JetReferenceExpression) { + // In some type declaration + if (expression.getParent() instanceof JetUserType) { + JetUserType qualifier = ((JetUserType) expression.getParent()).getQualifier(); + if (qualifier != null) { + Collection descriptors = resolveSession.getInjector().getQualifiedExpressionResolver() + .lookupDescriptorsForUserType(qualifier, getExpressionOuterScope(resolveSession, expression), trace); + + for (DeclarationDescriptor descriptor : descriptors) { + if (descriptor instanceof LazyPackageDescriptor) { + return ((LazyPackageDescriptor) descriptor).getMemberScope(); + } + + } + } + } + + // Inside import + if (PsiTreeUtil.getParentOfType(expression, JetImportDirective.class, false) != null) { + if (expression.getParent() instanceof JetDotQualifiedExpression) { + JetExpression element = ((JetDotQualifiedExpression) expression.getParent()).getReceiverExpression(); + + NamespaceDescriptor filePackage = resolveSession.getPackageDescriptorByFqName( + new FqName(((JetFile) expression.getContainingFile()).getPackageName())); + + NamespaceDescriptor rootPackage = resolveSession.getPackageDescriptorByFqName(FqName.ROOT); + + Collection descriptors; + + if (element instanceof JetDotQualifiedExpression) { + descriptors = resolveSession.getInjector().getQualifiedExpressionResolver().lookupDescriptorsForQualifiedExpression( + (JetDotQualifiedExpression) element, rootPackage.getMemberScope(), filePackage.getMemberScope(), trace, false, false); + } + else { + descriptors = resolveSession.getInjector().getQualifiedExpressionResolver().lookupDescriptorsForSimpleNameReference( + (JetSimpleNameExpression) element, rootPackage.getMemberScope(), filePackage.getMemberScope(), trace, false, false, false); + } + + for (DeclarationDescriptor descriptor : descriptors) { + if (descriptor instanceof NamespaceDescriptor) { + return ((NamespaceDescriptor) descriptor).getMemberScope(); + } + } + } + else { + return resolveSession.getPackageDescriptorByFqName(FqName.ROOT).getMemberScope(); + } + } + + // Inside package declaration + JetNamespaceHeader namespaceHeader = PsiTreeUtil.getParentOfType(expression, JetNamespaceHeader.class, false); + if (namespaceHeader != null) { + NamespaceDescriptor packageDescriptor = resolveSession.getPackageDescriptorByFqName( + namespaceHeader.getParentFqName((JetReferenceExpression)expression)); + if (packageDescriptor != null) { + return packageDescriptor.getMemberScope(); + } + } + } + + return null; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java index ccf3905c1d8..438c428bf8d 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java +++ b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.plugin.caches; import com.google.common.base.Function; import com.google.common.collect.Collections2; +import com.google.common.collect.Sets; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiClass; @@ -39,6 +40,8 @@ import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingTraceContext; import org.jetbrains.jet.lang.resolve.ImportPath; import org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver; +import org.jetbrains.jet.lang.resolve.lazy.LazyBindingContextUtils; +import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.JetType; @@ -155,22 +158,25 @@ public class JetShortNamesCache extends PsiShortNamesCache { @NotNull JetSimpleNameExpression expression, @NotNull GlobalSearchScope scope ) { - HashSet result = new HashSet(); - JetFile jetFile = (JetFile) expression.getContainingFile(); - BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile).getBindingContext(); + final ResolveSession resolveSessionForFile = WholeProjectAnalyzerFacade.getLazyResolveSessionForFile(jetFile); + LazyBindingContextUtils.getExpressionBindingContext(resolveSessionForFile, expression); + BindingContext context = WholeProjectAnalyzerFacade.getLazyResolveContext(jetFile, expression); JetScope jetScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); if (jetScope == null) { - return result; + return Collections.emptyList(); } + Set result = Sets.newHashSet(); + Collection topLevelFunctionPrototypes = JetFromJavaDescriptorHelper.getTopLevelFunctionPrototypesByName(name, project, scope); for (PsiMethod method : topLevelFunctionPrototypes) { FqName functionFQN = JetFromJavaDescriptorHelper.getJetTopLevelDeclarationFQN(method); if (functionFQN != null) { JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, new ImportPath(functionFQN, false)); - Collection declarationDescriptors = new QualifiedExpressionResolver().analyseImportReference(importDirective, jetScope, new BindingTraceContext()); + Collection declarationDescriptors = new QualifiedExpressionResolver().analyseImportReference( + importDirective, jetScope, new BindingTraceContext()); for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) { if (declarationDescriptor instanceof FunctionDescriptor) { result.add((FunctionDescriptor) declarationDescriptor); @@ -181,10 +187,8 @@ public class JetShortNamesCache extends PsiShortNamesCache { Collection jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope); for (JetNamedFunction jetNamedFunction : jetNamedFunctions) { - SimpleFunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, jetNamedFunction); - if (functionDescriptor != null) { - result.add(functionDescriptor); - } + FunctionDescriptor functionDescriptor = (FunctionDescriptor) resolveSessionForFile.resolveToDescriptor(jetNamedFunction); + result.add(functionDescriptor); } return result; @@ -224,7 +228,7 @@ public class JetShortNamesCache extends PsiShortNamesCache { JetFile jetFile = (JetFile) expression.getContainingFile(); - BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile).getBindingContext(); + BindingContext context = WholeProjectAnalyzerFacade.getLazyResolveContext(jetFile, expression); JetExpression receiverExpression = expression.getReceiverExpression(); if (receiverExpression != null) { @@ -273,7 +277,7 @@ public class JetShortNamesCache extends PsiShortNamesCache { @NotNull Condition acceptedShortNameCondition, @NotNull JetFile jetFile ) { - BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile).getBindingContext(); + BindingContext context = WholeProjectAnalyzerFacade.getLazyResolveContext(jetFile, null); Collection classDescriptors = new ArrayList(); for (String fqName : JetFullClassNameIndex.getInstance().getAllKeys(project)) { diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetClassCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetClassCompletionContributor.java index 8b7e9a18138..561aad3ebf3 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetClassCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetClassCompletionContributor.java @@ -21,18 +21,23 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiFile; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.asJava.JetLightClass; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.psi.JetClassOrObject; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; import org.jetbrains.jet.plugin.caches.JetCacheManager; import org.jetbrains.jet.plugin.caches.JetShortNamesCache; import org.jetbrains.jet.plugin.completion.handlers.JetJavaClassInsertHandler; import org.jetbrains.jet.plugin.project.JsModuleDetector; import org.jetbrains.jet.plugin.project.WholeProjectAnalyzerFacade; +import org.jetbrains.jet.plugin.stubindex.JetFullClassNameIndex; import java.util.Collection; @@ -40,34 +45,7 @@ import java.util.Collection; * @author Nikolay Krasko */ public class JetClassCompletionContributor extends CompletionContributor { - public JetClassCompletionContributor() { - // Should be removed in new idea - //extend(CompletionType.CLASS_NAME, PlatformPatterns.psiElement(), - // new CompletionProvider() { - // @Override - // protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, - // final @NotNull CompletionResultSet result) { - // final CompletionResultSet jetResult = JetCompletionSorting.addJetSorting(parameters, result); - // - // final PsiElement position = parameters.getPosition(); - // if (!(position.getContainingFile() instanceof JetFile)) { - // return; - // } - // - // final PsiReference ref = position.getContainingFile().findReferenceAt(parameters.getOffset()); - // if (ref instanceof JetSimpleNameReference) { - // addClasses(parameters, result, new Consumer() { - // @Override - // public void consume(LookupElement lookupElement) { - // jetResult.addElement(lookupElement); - // } - // }); - // } - // - // result.stopHere(); - // } - // }); - } + public JetClassCompletionContributor() {} /** * Jet classes will be added as java completions for unification @@ -75,17 +53,14 @@ public class JetClassCompletionContributor extends CompletionContributor { static void addClasses( @NotNull final CompletionParameters parameters, @NotNull final CompletionResultSet result, + @NotNull final BindingContext jetContext, @NotNull final Consumer consumer ) { CompletionResultSet tempResult = result.withPrefixMatcher(CompletionUtil.findReferenceOrAlphanumericPrefix(parameters)); - // TODO: Make icon for standard types final Collection jetOnlyClasses = JetShortNamesCache.getJetOnlyTypes(); - final BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile( - (JetFile)parameters.getPosition().getContainingFile()).getBindingContext(); - for (DeclarationDescriptor jetOnlyClass : jetOnlyClasses) { - consumer.consume(DescriptorLookupConverter.createLookupElement(bindingContext, jetOnlyClass)); + consumer.consume(DescriptorLookupConverter.createLookupElement(jetContext, jetOnlyClass)); } if (!JsModuleDetector.isJsModule((JetFile)parameters.getOriginalFile())) { @@ -99,8 +74,8 @@ public class JetClassCompletionContributor extends CompletionContributor { if (lookupElement instanceof JavaPsiClassReferenceElement) { JavaPsiClassReferenceElement javaPsiReferenceElement = (JavaPsiClassReferenceElement) lookupElement; - PsiClass object = javaPsiReferenceElement.getObject(); - if (addAsJetLookupElement(object, bindingContext, consumer)) { + PsiClass psiClass = javaPsiReferenceElement.getObject(); + if (addAsJetLookupElement(parameters, psiClass, consumer)) { return; } @@ -124,20 +99,30 @@ public class JetClassCompletionContributor extends CompletionContributor { (JetFile) parameters.getOriginalFile()); for (ClassDescriptor descriptor : descriptors) { - consumer.consume(DescriptorLookupConverter.createLookupElement(bindingContext, descriptor)); + consumer.consume(DescriptorLookupConverter.createLookupElement(jetContext, descriptor)); } } } - private static boolean addAsJetLookupElement(PsiClass aClass, BindingContext bindingContext, Consumer consumer) { + private static boolean addAsJetLookupElement(CompletionParameters parameters, PsiClass aClass, Consumer consumer) { if (aClass instanceof JetLightClass) { - ClassDescriptor descriptor = bindingContext.get( - BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, ((JetLightClass)aClass).getFqName()); + Project project = parameters.getPosition().getProject(); + PsiFile completionFile = parameters.getPosition().getContainingFile(); - if (descriptor != null) { - LookupElement element = DescriptorLookupConverter.createLookupElement(bindingContext, descriptor); - consumer.consume(element); - return true; + Collection classOrObjects = + JetFullClassNameIndex.getInstance().get(aClass.getQualifiedName(), project, GlobalSearchScope.allScope(project)); + + for (JetClassOrObject classOrObject : classOrObjects) { + if (classOrObject.getContainingFile().getOriginalFile().equals(aClass.getContainingFile())) { + PsiFile fileOfClass = completionFile.getOriginalFile().equals(aClass.getContainingFile()) ? completionFile : aClass.getContainingFile(); + + ResolveSession resolveSessionForFile = WholeProjectAnalyzerFacade.getLazyResolveSessionForFile((JetFile) fileOfClass); + ClassDescriptor descriptor = resolveSessionForFile.getClassDescriptor(classOrObject); + + LookupElement element = DescriptorLookupConverter.createLookupElement(resolveSessionForFile.getBindingContext(), descriptor); + consumer.consume(element); + return true; + } } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java index c70ca9b9fa2..e1f0cf5d72c 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java @@ -77,20 +77,18 @@ public class JetCompletionContributor extends CompletionContributor { JetSimpleNameReference jetReference = getJetReference(parameters); if (jetReference != null) { - - BindingContext jetContext = - WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile)position.getContainingFile()) - .getBindingContext(); + BindingContext jetContext = WholeProjectAnalyzerFacade.getLazyResolveContext( + (JetFile) position.getContainingFile(), jetReference.getExpression()); JetScope scope = jetContext.get(BindingContext.RESOLUTION_SCOPE, jetReference.getExpression()); session.inDescriptor = scope != null ? scope.getContainingDeclaration() : null; - completeForReference(parameters, result, position, jetReference, session); + completeForReference(parameters, result, position, jetReference, session, jetContext); if (!session.isSomethingAdded && session.customInvocationCount == 0) { // Rerun completion if nothing was found session.customInvocationCount = 1; - completeForReference(parameters, result, position, jetReference, session); + completeForReference(parameters, result, position, jetReference, session, jetContext); } } @@ -105,18 +103,18 @@ public class JetCompletionContributor extends CompletionContributor { @NotNull CompletionResultSet result, @NotNull PsiElement position, @NotNull JetSimpleNameReference jetReference, - @NotNull CompletionSession session - ) { + @NotNull CompletionSession session, + BindingContext jetContext) { if (isOnlyKeywordCompletion(position)) { return; } if (shouldRunTypeCompletionOnly(position, jetReference)) { if (session.customInvocationCount > 0) { - addClasses(parameters, result, session); + addClasses(parameters, result, session, jetContext); } else { - for (LookupElement variant : getReferenceVariants(jetReference, result, session)) { + for (LookupElement variant : getReferenceVariants(jetReference, result, session, jetContext)) { if (isTypeDeclaration(variant)) { addCheckedCompletionToResult(result, variant, session); } @@ -126,7 +124,7 @@ public class JetCompletionContributor extends CompletionContributor { return; } - for (LookupElement variant : getReferenceVariants(jetReference, result, session)) { + for (LookupElement variant : getReferenceVariants(jetReference, result, session, jetContext)) { addCheckedCompletionToResult(result, variant, session); } @@ -144,7 +142,7 @@ public class JetCompletionContributor extends CompletionContributor { } if (shouldRunTopLevelCompletion(parameters, session)) { - addClasses(parameters, result, session); + addClasses(parameters, result, session, jetContext); addJetTopLevelFunctions(jetReference.getExpression(), result, position, session); } @@ -157,12 +155,12 @@ public class JetCompletionContributor extends CompletionContributor { return PsiTreeUtil.getParentOfType(position, JetModifierList.class) != null; } - private static void addJetExtensions(JetSimpleNameExpression expression, CompletionResultSet result, PsiElement position) { + private static void addJetExtensions(@NotNull JetSimpleNameExpression expression, @NotNull CompletionResultSet result, @NotNull PsiElement position) { final PrefixMatcher prefixMatcher = result.getPrefixMatcher(); JetShortNamesCache namesCache = JetCacheManager.getInstance(position.getProject()).getNamesCache(); Condition matchPrefixCondition = new Condition() { @Override - public boolean value(String callableName) { + public boolean value(@NotNull String callableName) { return prefixMatcher.prefixMatches(callableName); } }; @@ -170,8 +168,8 @@ public class JetCompletionContributor extends CompletionContributor { Collection jetCallableExtensions = namesCache.getJetCallableExtensions( matchPrefixCondition, expression, GlobalSearchScope.allScope(position.getProject())); - BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile)position.getContainingFile()) - .getBindingContext(); + BindingContext context = WholeProjectAnalyzerFacade.getLazyResolveContext( + (JetFile) position.getContainingFile(), expression); for (DeclarationDescriptor jetCallableExtension : jetCallableExtensions) { result.addElement(DescriptorLookupConverter.createLookupElement(context, jetCallableExtension)); @@ -192,11 +190,10 @@ public class JetCompletionContributor extends CompletionContributor { return false; } - private static void addJetTopLevelFunctions(JetSimpleNameExpression expression, + private static void addJetTopLevelFunctions(@NotNull JetSimpleNameExpression expression, @NotNull CompletionResultSet result, @NotNull PsiElement position, @NotNull CompletionSession session) { - String actualPrefix = result.getPrefixMatcher().getPrefix(); Project project = position.getProject(); @@ -205,8 +202,8 @@ public class JetCompletionContributor extends CompletionContributor { GlobalSearchScope scope = GlobalSearchScope.allScope(project); Collection functionNames = namesCache.getAllTopLevelFunctionNames(); - BindingContext resolutionContext = - WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile)position.getContainingFile()).getBindingContext(); + BindingContext resolutionContext = WholeProjectAnalyzerFacade.getLazyResolveContext( + (JetFile) position.getContainingFile(), expression); for (String name : functionNames) { if (name.contains(actualPrefix)) { @@ -223,9 +220,9 @@ public class JetCompletionContributor extends CompletionContributor { private static void addClasses( @NotNull CompletionParameters parameters, @NotNull final CompletionResultSet result, - @NotNull final CompletionSession session - ) { - JetClassCompletionContributor.addClasses(parameters, result, new Consumer() { + @NotNull final CompletionSession session, + BindingContext jetContext) { + JetClassCompletionContributor.addClasses(parameters, result, jetContext, new Consumer() { @Override public void consume(@NotNull LookupElement element) { addCompletionToResult(result, element, session); @@ -233,7 +230,7 @@ public class JetCompletionContributor extends CompletionContributor { }); } - private static boolean shouldRunTypeCompletionOnly(PsiElement position, JetSimpleNameReference jetReference) { + private static boolean shouldRunTypeCompletionOnly(PsiElement position, @NotNull JetSimpleNameReference jetReference) { // Check that completion in the type annotation context and if there's a qualified // expression we are at first of it JetTypeReference typeReference = PsiTreeUtil.getParentOfType(position, JetTypeReference.class); @@ -245,7 +242,7 @@ public class JetCompletionContributor extends CompletionContributor { return false; } - private static boolean shouldRunTopLevelCompletion(@NotNull CompletionParameters parameters, CompletionSession session) { + private static boolean shouldRunTopLevelCompletion(@NotNull CompletionParameters parameters, @NotNull CompletionSession session) { if (session.customInvocationCount == 0) { return false; } @@ -263,7 +260,7 @@ public class JetCompletionContributor extends CompletionContributor { return false; } - private static boolean shouldRunExtensionsCompletion(CompletionParameters parameters, String prefix, CompletionSession session) { + private static boolean shouldRunExtensionsCompletion(@NotNull CompletionParameters parameters, @NotNull String prefix, @NotNull CompletionSession session) { if (session.customInvocationCount == 0 && prefix.length() < 3) { return false; } @@ -312,7 +309,7 @@ public class JetCompletionContributor extends CompletionContributor { session.isSomethingAdded = true; } - private static boolean isVisibleElement(LookupElement element, CompletionSession session) { + private static boolean isVisibleElement(@NotNull LookupElement element, @NotNull CompletionSession session) { if (session.inDescriptor != null) { if (element.getObject() instanceof JetLookupObject) { JetLookupObject jetObject = (JetLookupObject)element.getObject(); @@ -324,14 +321,16 @@ public class JetCompletionContributor extends CompletionContributor { return true; } - private static boolean isVisibleDescriptor(DeclarationDescriptor descriptor, CompletionSession session) { + private static boolean isVisibleDescriptor(DeclarationDescriptor descriptor, @NotNull CompletionSession session) { if (session.customInvocationCount >= 2) { // Show everything if user insist on showing completion list return true; } if (descriptor instanceof DeclarationDescriptorWithVisibility) { - return Visibilities.isVisible((DeclarationDescriptorWithVisibility)descriptor, session.inDescriptor); + if (session.inDescriptor != null) { + return Visibilities.isVisible((DeclarationDescriptorWithVisibility)descriptor, session.inDescriptor); + } } return true; @@ -341,13 +340,10 @@ public class JetCompletionContributor extends CompletionContributor { private static LookupElement[] getReferenceVariants( @NotNull JetSimpleNameReference reference, @NotNull final CompletionResultSet result, - @NotNull final CompletionSession session + @NotNull final CompletionSession session, + BindingContext jetContext ) { - BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile( - (JetFile)reference.getExpression().getContainingFile()) - .getBindingContext(); - - Collection descriptors = TipsManager.getReferenceVariants(reference.getExpression(), bindingContext); + Collection descriptors = TipsManager.getReferenceVariants(reference.getExpression(), jetContext); Collection checkedDescriptors = Collections2.filter(descriptors, new Predicate() { @Override @@ -360,6 +356,6 @@ public class JetCompletionContributor extends CompletionContributor { } }); - return DescriptorLookupConverter.collectLookupElements(bindingContext, checkedDescriptors); + return DescriptorLookupConverter.collectLookupElements(jetContext, checkedDescriptors); } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetExtensionReceiverTypeContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetExtensionReceiverTypeContributor.java index 664584407c5..7d7b92482b5 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetExtensionReceiverTypeContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetExtensionReceiverTypeContributor.java @@ -24,7 +24,10 @@ import com.intellij.psi.PsiElement; import com.intellij.util.Consumer; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.project.WholeProjectAnalyzerFacade; /** * Special contributor for getting completion of type for extensions receiver. @@ -38,8 +41,11 @@ public class JetExtensionReceiverTypeContributor extends CompletionContributor { ProcessingContext context, final @NotNull CompletionResultSet result ) { + BindingContext jetContext = WholeProjectAnalyzerFacade + .getLazyResolveContext((JetFile) parameters.getPosition().getContainingFile(), null); + if (parameters.getInvocationCount() > 0 || parameters.getCompletionType() == CompletionType.CLASS_NAME) { - JetClassCompletionContributor.addClasses(parameters, result, new Consumer() { + JetClassCompletionContributor.addClasses(parameters, result, jetContext, new Consumer() { @Override public void consume(@NotNull LookupElement element) { result.addElement(element); diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java index 955a72f1568..144c89ec9a0 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java @@ -71,12 +71,11 @@ public class JetPackagesContributor extends CompletionContributor { int prefixLength = parameters.getOffset() - simpleNameReference.getExpression().getTextOffset(); result = result.withPrefixMatcher(new PlainPrefixMatcher(name.substring(0, prefixLength))); - BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile( - (JetFile)namespaceHeader.getContainingFile()) - .getBindingContext(); + BindingContext bindingContext = WholeProjectAnalyzerFacade.getLazyResolveContext( + (JetFile) namespaceHeader.getContainingFile(), simpleNameReference.getExpression()); for (LookupElement variant : DescriptorLookupConverter.collectLookupElements( - bindingContext, TipsManager.getReferenceVariants(namespaceHeader, bindingContext))) { + bindingContext, TipsManager.getPackageReferenceVariants(simpleNameReference.getExpression(), bindingContext))) { if (!variant.getLookupString().contains(DUMMY_IDENTIFIER)) { result.addElement(variant); } @@ -92,5 +91,6 @@ public class JetPackagesContributor extends CompletionContributor { public void beforeCompletion(@NotNull CompletionInitializationContext context) { // Will need to filter this dummy identifier to avoid showing it in completion context.setDummyIdentifier(DUMMY_IDENTIFIER); + // context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER); } } diff --git a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java index 4bc6ffc70d7..6f394b83cad 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java +++ b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java @@ -18,33 +18,43 @@ package org.jetbrains.jet.plugin.project; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.collect.Lists; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.impl.PsiModificationTrackerImpl; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.analyzer.AnalyzeExhaust; +import org.jetbrains.jet.di.InjectorForJavaDescriptorResolver; +import org.jetbrains.jet.lang.BuiltinsScopeExtensionMode; +import org.jetbrains.jet.lang.DefaultModuleConfiguration; +import org.jetbrains.jet.lang.ModuleConfiguration; +import org.jetbrains.jet.lang.descriptors.ModuleDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.lang.psi.JetPsiUtil; -import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter; -import org.jetbrains.jet.lang.resolve.BindingTraceContext; -import org.jetbrains.jet.lang.resolve.BodiesResolveContext; -import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace; -import org.jetbrains.jet.lang.resolve.java.JetFilesProvider; +import org.jetbrains.jet.lang.psi.JetImportDirective; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.java.*; +import org.jetbrains.jet.lang.resolve.lazy.FileBasedDeclarationProviderFactory; +import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.resolve.scopes.WritableScope; +import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; -import java.util.Collection; -import java.util.Collections; +import java.util.*; /** * @author Pavel Talanov @@ -55,6 +65,7 @@ public final class AnalyzerFacadeWithCache { private final static Key> ANALYZE_EXHAUST_HEADERS = Key.create("ANALYZE_EXHAUST_HEADERS"); private final static Key> ANALYZE_EXHAUST_FULL = Key.create("ANALYZE_EXHAUST_FULL"); + private final static Key> ANALYZE_EXHAUST_LAZY_FULL = Key.create("ANALYZE_EXHAUST_FULL"); private static final Object lock = new Object(); public static final Function> SINGLE_DECLARATION_PROVIDER = new Function>() { @@ -67,7 +78,6 @@ public final class AnalyzerFacadeWithCache { private AnalyzerFacadeWithCache() { } - /** * Analyze project with string cache for given file. Given file will be fully analyzed. * @@ -88,8 +98,6 @@ public final class AnalyzerFacadeWithCache { @Override public Result compute() { try { - // System.out.println("===============ReCache - In-Block=============="); - // Collect context for headers first AnalyzeExhaust analyzeExhaustHeaders = analyzeHeadersWithCacheOnFile(file, declarationProvider); @@ -149,7 +157,6 @@ public final class AnalyzerFacadeWithCache { CachedValuesManager.getManager(fileToCache.getProject()).createCachedValue(new CachedValueProvider() { @Override public Result compute() { - // System.out.println("===============ReCache - OUT-OF-BLOCK=============="); AnalyzeExhaust exhaust = AnalyzerFacadeProvider.getAnalyzerFacadeForFile(fileToCache) .analyzeFiles(fileToCache.getProject(), declarationProvider.fun(fileToCache), @@ -169,4 +176,80 @@ public final class AnalyzerFacadeWithCache { DiagnosticUtils.throwIfRunningOnServer(e); LOG.error(e); } + + private static final Object lazyLock = new Object(); + + @NotNull + public static ResolveSession getLazyResolveSession(@NotNull final JetFile file) { + synchronized (lazyLock) { + final Project fileProject = file.getProject(); + CachedValue bindingContextCachedValue = file.getUserData(ANALYZE_EXHAUST_LAZY_FULL); + if (bindingContextCachedValue == null) { + bindingContextCachedValue = + CachedValuesManager.getManager(file.getProject()).createCachedValue(new CachedValueProvider() { + @Override + public Result compute() { + ModuleDescriptor javaModule = new ModuleDescriptor(Name.special("")); + + InjectorForJavaDescriptorResolver injector = new InjectorForJavaDescriptorResolver( + fileProject, new BindingTraceContext(), javaModule, BuiltinsScopeExtensionMode.ALL); + + List files = JetFilesProvider.getInstance(fileProject).allInScope(GlobalSearchScope.allScope(fileProject)); + + // Given file can differ from the original because it can be a virtual copy with some modifications + JetFile originalFile = (JetFile) file.getOriginalFile(); + files.remove(originalFile); + files.add(file); + + final PsiClassFinder psiClassFinder = injector.getPsiClassFinder(); + + // TODO: Replace with stub declaration provider + final FileBasedDeclarationProviderFactory declarationProviderFactory = new FileBasedDeclarationProviderFactory(files, new Predicate() { + @Override + public boolean apply(FqName fqName) { + return psiClassFinder.findPsiPackage(fqName) != null || new FqName("jet").equals(fqName); + } + }); + + final JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver(); + + ModuleConfiguration moduleConfiguration = new ModuleConfiguration() { + @Override + public void addDefaultImports(@NotNull Collection directives) { + final Collection defaultImports = Lists.newArrayList(JavaBridgeConfiguration.DEFAULT_JAVA_IMPORTS); + defaultImports.addAll(Arrays.asList(DefaultModuleConfiguration.DEFAULT_JET_IMPORTS)); + + for (ImportPath defaultJetImport : defaultImports) { + directives.add(JetPsiFactory.createImportDirective(fileProject, defaultJetImport)); + } + } + + @Override + public void extendNamespaceScope( + @NotNull BindingTrace trace, + @NotNull NamespaceDescriptor namespaceDescriptor, + @NotNull WritableScope namespaceMemberScope + ) { + FqName fqName = DescriptorUtils.getFQName(namespaceDescriptor).toSafe(); + if (new FqName("jet").equals(fqName)) { + namespaceMemberScope.importScope(JetStandardLibrary.getInstance().getLibraryScope()); + } + if (psiClassFinder.findPsiPackage(fqName) != null) { + JavaPackageScope javaPackageScope = javaDescriptorResolver.getJavaPackageScope(fqName, namespaceDescriptor); + namespaceMemberScope.importScope(javaPackageScope); + } + } + }; + + ModuleDescriptor lazyModule = new ModuleDescriptor(Name.special("")); + ResolveSession resolveSession = new ResolveSession(fileProject, lazyModule, moduleConfiguration, declarationProviderFactory); + + return new Result(resolveSession, PsiModificationTracker.MODIFICATION_COUNT); + } + }, false); + file.putUserData(ANALYZE_EXHAUST_LAZY_FULL, bindingContextCachedValue); + } + return bindingContextCachedValue.getValue(); + } + } } diff --git a/idea/src/org/jetbrains/jet/plugin/project/WholeProjectAnalyzerFacade.java b/idea/src/org/jetbrains/jet/plugin/project/WholeProjectAnalyzerFacade.java index 9b399be0311..51b1c6035bc 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/WholeProjectAnalyzerFacade.java +++ b/idea/src/org/jetbrains/jet/plugin/project/WholeProjectAnalyzerFacade.java @@ -17,20 +17,23 @@ package org.jetbrains.jet.plugin.project; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.analyzer.AnalyzeExhaust; +import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.java.JetFilesProvider; +import org.jetbrains.jet.lang.resolve.lazy.LazyBindingContextUtils; +import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; import static org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache.analyzeFileWithCache; +import static org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache.getLazyResolveSession; /** * @author abreslav */ public final class WholeProjectAnalyzerFacade { - /** - * Forbid creating - */ private WholeProjectAnalyzerFacade() { } @@ -38,4 +41,17 @@ public final class WholeProjectAnalyzerFacade { public static AnalyzeExhaust analyzeProjectWithCacheOnAFile(@NotNull JetFile file) { return analyzeFileWithCache(file, JetFilesProvider.getInstance(file.getProject()).sampleToAllFilesInModule()); } + + @NotNull + public static ResolveSession getLazyResolveSessionForFile(@NotNull JetFile file) { + return getLazyResolveSession(file); + } + + @NotNull + public static BindingContext getLazyResolveContext(@NotNull JetFile file, @Nullable JetExpression expression) { + ResolveSession resolveSession = getLazyResolveSessionForFile(file); + return expression != null ? + LazyBindingContextUtils.getExpressionBindingContext(resolveSession, expression) : + resolveSession.getBindingContext(); + } }