diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/ResolveSession.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/ResolveSession.java index 261646b4a19..bff81905d3f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/ResolveSession.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/ResolveSession.java @@ -71,7 +71,6 @@ public class ResolveSession implements KotlinCodeAnalyzer { private final InjectorForLazyResolve injector; private final Function classifierAliases; - private final ResolveElementCache resolveElementCache; public ResolveSession( @NotNull Project project, @@ -122,7 +121,6 @@ public class ResolveSession implements KotlinCodeAnalyzer { rootDescriptor.setRootNamespace(rootPackage); this.declarationProviderFactory = declarationProviderFactory; - this.resolveElementCache = new ResolveElementCache(this); } @NotNull @@ -352,11 +350,6 @@ public class ResolveSession implements KotlinCodeAnalyzer { return result; } - @NotNull - public BindingContext resolveElement(@NotNull JetElement jetElement) { - return resolveElementCache.resolveElement(jetElement); - } - @NotNull public Name resolveClassifierAlias(@NotNull FqName packageName, @NotNull Name alias) { // TODO: creating a new FqName object every time... diff --git a/idea/src/org/jetbrains/jet/plugin/completion/CompletionProgressIndicatorUtil.java b/idea/src/org/jetbrains/jet/plugin/completion/CompletionProgressIndicatorUtil.java new file mode 100644 index 00000000000..d5a297bfaf8 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/completion/CompletionProgressIndicatorUtil.java @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2013 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; + +import com.intellij.codeInsight.completion.CompletionProgressIndicator; +import com.intellij.codeInsight.completion.CompletionService; +import com.intellij.openapi.progress.ProcessCanceledException; + +public class CompletionProgressIndicatorUtil { + private CompletionProgressIndicatorUtil() { + } + + public static ProcessCanceledException rethrowWithCancelIndicator(ProcessCanceledException exception) { + CompletionProgressIndicator indicator = (CompletionProgressIndicator) CompletionService.getCompletionService().getCurrentCompletion(); + assert indicator != null; + + // Force cancel to avoid deadlock in CompletionThreading.delegateWeighing() + if (!indicator.isCanceled()) { + indicator.cancel(); + } + + return exception; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java index 178a8bfaed6..fa8ca6c567e 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.plugin.completion; import com.intellij.codeInsight.completion.*; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; @@ -50,19 +51,24 @@ public class JetCompletionContributor extends CompletionContributor { JetSimpleNameReference jetReference = getJetReference(parameters); if (jetReference != null) { - result.restartCompletionWhenNothingMatches(); + try { + result.restartCompletionWhenNothingMatches(); - CompletionSession session = new CompletionSession(parameters, result, jetReference, position); - session.completeForReference(); - - if (!session.getJetResult().isSomethingAdded() && session.getParameters().getInvocationCount() < 2) { - // Rerun completion if nothing was found - session = new CompletionSession(parameters.withInvocationCount(2), result, jetReference, position); + CompletionSession session = new CompletionSession(parameters, result, jetReference, position); session.completeForReference(); - } - // Prevent from adding reference variants from standard reference contributor - result.stopHere(); + if (!session.getJetResult().isSomethingAdded() && session.getParameters().getInvocationCount() < 2) { + // Rerun completion if nothing was found + session = new CompletionSession(parameters.withInvocationCount(2), result, jetReference, position); + session.completeForReference(); + } + + // Prevent from adding reference variants from standard reference contributor + result.stopHere(); + } + catch (ProcessCanceledException e) { + throw CompletionProgressIndicatorUtil.rethrowWithCancelIndicator(e); + } } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java index bd18e485758..fc930b1be43 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.plugin.completion; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.patterns.ElementPattern; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; @@ -66,21 +67,26 @@ public class JetPackagesContributor extends CompletionContributor { return; } - int prefixLength = parameters.getOffset() - simpleNameReference.getExpression().getTextOffset(); - result = result.withPrefixMatcher(new PlainPrefixMatcher(name.substring(0, prefixLength))); + try { + int prefixLength = parameters.getOffset() - simpleNameReference.getExpression().getTextOffset(); + result = result.withPrefixMatcher(new PlainPrefixMatcher(name.substring(0, prefixLength))); - CancelableResolveSession resolveSession = WholeProjectAnalyzerFacade.getLazyResolveResultForFile( - (JetFile) simpleNameReference.getExpression().getContainingFile()); - BindingContext bindingContext = resolveSession.resolveToElement(simpleNameReference.getExpression()); + CancelableResolveSession resolveSession = WholeProjectAnalyzerFacade.getLazyResolveResultForFile( + (JetFile) simpleNameReference.getExpression().getContainingFile()); + BindingContext bindingContext = resolveSession.resolveToElement(simpleNameReference.getExpression()); - for (LookupElement variant : DescriptorLookupConverter.collectLookupElements( - resolveSession, bindingContext, TipsManager.getPackageReferenceVariants(simpleNameReference.getExpression(), bindingContext))) { - if (!variant.getLookupString().contains(DUMMY_IDENTIFIER)) { - result.addElement(variant); + for (LookupElement variant : DescriptorLookupConverter.collectLookupElements( + resolveSession, bindingContext, TipsManager.getPackageReferenceVariants(simpleNameReference.getExpression(), bindingContext))) { + if (!variant.getLookupString().contains(DUMMY_IDENTIFIER)) { + result.addElement(variant); + } } - } - result.stopHere(); + result.stopHere(); + } + catch (ProcessCanceledException e) { + throw CompletionProgressIndicatorUtil.rethrowWithCancelIndicator(e); + } } } }); diff --git a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java index bbcad6bf737..611f86cf214 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java +++ b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeWithCache.java @@ -188,7 +188,7 @@ public final class AnalyzerFacadeWithCache { ResolveSession resolveSession = AnalyzerFacadeProvider.getAnalyzerFacadeForFile(file).getLazyResolveSession(fileProject, files); - return Result.create(new CancelableResolveSession(file, resolveSession), PsiModificationTracker.MODIFICATION_COUNT); + return Result.create(new CancelableResolveSession(file, resolveSession), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } }, true); diff --git a/idea/src/org/jetbrains/jet/plugin/project/CancelableResolveSession.java b/idea/src/org/jetbrains/jet/plugin/project/CancelableResolveSession.java index 7403fdc1877..34f0c8be903 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/CancelableResolveSession.java +++ b/idea/src/org/jetbrains/jet/plugin/project/CancelableResolveSession.java @@ -40,18 +40,20 @@ import java.util.concurrent.atomic.AtomicLong; public class CancelableResolveSession implements KotlinCodeAnalyzer, ModificationTracker { private final JetFile file; private final ResolveSession resolveSession; + private final ResolveElementCache resolveElementCache; private final AtomicLong canceledTracker = new AtomicLong(); - public CancelableResolveSession(JetFile file, ResolveSession resolveSession) { + public CancelableResolveSession(@NotNull JetFile file, @NotNull ResolveSession resolveSession) { this.file = file; this.resolveSession = resolveSession; + this.resolveElementCache = new ResolveElementCache(resolveSession, file.getProject()); } public BindingContext resolveToElement(final JetElement element) { return computableWithProcessingCancel(new Computable() { @Override public BindingContext compute() { - return resolveSession.resolveElement(element); + return resolveElementCache.resolveElement(element); } }); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/ResolveElementCache.java b/idea/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java similarity index 73% rename from compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/ResolveElementCache.java rename to idea/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java index 5e098eb71d0..ceb51aa6b22 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/ResolveElementCache.java +++ b/idea/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java @@ -14,21 +14,27 @@ * limitations under the License. */ -package org.jetbrains.jet.lang.resolve.lazy; +package org.jetbrains.jet.plugin.project; +import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Predicates; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.Function; +import com.intellij.psi.util.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.di.InjectorForBodyResolve; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.annotations.Annotated; import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer; +import org.jetbrains.jet.lang.resolve.lazy.LazyDescriptor; +import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; +import org.jetbrains.jet.lang.resolve.lazy.ScopeProvider; import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor; import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyPackageDescriptor; import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNotNull; @@ -44,32 +50,39 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; -class ResolveElementCache { - @SuppressWarnings("unchecked") - private static final BodyResolveContextForLazy EMPTY_CONTEXT = new BodyResolveContextForLazy((com.google.common.base.Function) Functions.constant(null)); +public class ResolveElementCache { + private static final BodyResolveContextForLazy EMPTY_CONTEXT = new BodyResolveContextForLazy(Functions.constant(null)); + private final CachedValue> additionalResolveCache; private final ResolveSession resolveSession; - private final MemoizedFunctionToNotNull resolveElementCache; - ResolveElementCache(ResolveSession resolveSession) { + public ResolveElementCache(ResolveSession resolveSession, Project project) { this.resolveSession = resolveSession; - this.resolveElementCache = resolveSession.getStorageManager().createMemoizedFunction(new Function() { - @Override - public BindingContext fun(JetElement namedFunction) { - return computeResolveElement(namedFunction); - } - }, StorageManager.ReferenceKind.WEAK); + // Recreate internal cache after change of modification count + this.additionalResolveCache = + CachedValuesManager.getManager(project).createCachedValue(new CachedValueProvider>() { + @Nullable + @Override + public Result> compute() { + StorageManager manager = ResolveElementCache.this.resolveSession.getStorageManager(); + MemoizedFunctionToNotNull elementsCacheFunction = + manager.createMemoizedFunction(new com.intellij.util.Function() { + @Override + public BindingContext fun(JetElement jetElement) { + return elementAdditionalResolve(jetElement); + } + }, StorageManager.ReferenceKind.WEAK); + + return Result.create(elementsCacheFunction, PsiModificationTracker.MODIFICATION_COUNT); + } + }, + false); } @NotNull - BindingContext resolveElement(@NotNull JetElement jetElement) { - return resolveElementCache.fun(jetElement); - } - - @NotNull - private BindingContext computeResolveElement(@NotNull JetElement jetElement) { - @SuppressWarnings("unchecked") PsiElement resolveElement = JetPsiUtil.getTopmostParentOfTypes( + public BindingContext resolveElement(@NotNull JetElement jetElement) { + @SuppressWarnings("unchecked") JetElement elementOfAdditionalResolve = (JetElement) JetPsiUtil.getTopmostParentOfTypes( jetElement, JetNamedFunction.class, JetClassInitializer.class, @@ -81,49 +94,12 @@ class ResolveElementCache { JetTypeConstraint.class, JetNamespaceHeader.class); - if (resolveElement != null) { - // All additional resolve should be done to separate trace - BindingTrace trace = resolveSession.getStorageManager().createSafeTrace( - new DelegatingBindingTrace(resolveSession.getBindingContext(), "trace to resolve element", jetElement)); - - JetFile file = (JetFile) jetElement.getContainingFile(); - - if (resolveElement instanceof JetNamedFunction) { - functionAdditionalResolve(resolveSession, (JetNamedFunction) resolveElement, trace, file); - } - else if (resolveElement instanceof JetClassInitializer) { - initializerAdditionalResolve(resolveSession, (JetClassInitializer) resolveElement, trace, file); - } - else if (resolveElement instanceof JetProperty) { - propertyAdditionalResolve(resolveSession, (JetProperty) resolveElement, trace, file); - } - else if (resolveElement instanceof JetDelegationSpecifierList) { - delegationSpecifierAdditionalResolve(resolveSession, (JetDelegationSpecifierList) resolveElement, trace, file); - } - else if (resolveElement instanceof JetImportDirective) { - JetImportDirective importDirective = (JetImportDirective) resolveElement; - JetScope scope = resolveSession.getInjector().getScopeProvider().getFileScope((JetFile) importDirective.getContainingFile()); - - // Get all descriptors to force resolving all imports - scope.getAllDescriptors(); - } - else if (resolveElement instanceof JetAnnotationEntry) { - annotationAdditionalResolve(resolveSession, (JetAnnotationEntry) resolveElement); - } - else if (resolveElement instanceof JetTypeParameter) { - typeParameterAdditionalResolve(resolveSession, (JetTypeParameter) resolveElement); - } - else if (resolveElement instanceof JetTypeConstraint) { - typeConstraintAdditionalResolve(resolveSession, jetElement); - } - else if (resolveElement instanceof JetNamespaceHeader) { - namespaceRefAdditionalResolve(resolveSession, (JetNamespaceHeader) resolveElement, trace, jetElement); - } - else { - assert false : "Invalid type of the topmost parent"; + if (elementOfAdditionalResolve != null) { + if (elementOfAdditionalResolve instanceof JetNamespaceHeader) { + elementOfAdditionalResolve = jetElement; } - return trace.getBindingContext(); + return additionalResolveCache.getValue().fun(elementOfAdditionalResolve); } JetDeclaration declaration = PsiTreeUtil.getParentOfType(jetElement, JetDeclaration.class, false); @@ -135,11 +111,57 @@ class ResolveElementCache { return resolveSession.getBindingContext(); } - private static void namespaceRefAdditionalResolve( - ResolveSession resolveSession, - JetNamespaceHeader header, BindingTrace trace, JetElement jetElement - ) { + @NotNull + private BindingContext elementAdditionalResolve(@NotNull JetElement resolveElement) { + // All additional resolve should be done to separate trace + BindingTrace trace = resolveSession.getStorageManager().createSafeTrace( + new DelegatingBindingTrace(resolveSession.getBindingContext(), "trace to resolve element", resolveElement)); + + JetFile file = (JetFile) resolveElement.getContainingFile(); + + if (resolveElement instanceof JetNamedFunction) { + functionAdditionalResolve(resolveSession, (JetNamedFunction) resolveElement, trace, file); + } + else if (resolveElement instanceof JetClassInitializer) { + initializerAdditionalResolve(resolveSession, (JetClassInitializer) resolveElement, trace, file); + } + else if (resolveElement instanceof JetProperty) { + propertyAdditionalResolve(resolveSession, (JetProperty) resolveElement, trace, file); + } + else if (resolveElement instanceof JetDelegationSpecifierList) { + delegationSpecifierAdditionalResolve(resolveSession, (JetDelegationSpecifierList) resolveElement, trace, file); + } + else if (resolveElement instanceof JetImportDirective) { + JetImportDirective importDirective = (JetImportDirective) resolveElement; + JetScope scope = resolveSession.getInjector().getScopeProvider().getFileScope((JetFile) importDirective.getContainingFile()); + + // Get all descriptors to force resolving all imports + scope.getAllDescriptors(); + } + else if (resolveElement instanceof JetAnnotationEntry) { + annotationAdditionalResolve(resolveSession, (JetAnnotationEntry) resolveElement); + } + else if (resolveElement instanceof JetTypeParameter) { + typeParameterAdditionalResolve(resolveSession, (JetTypeParameter) resolveElement); + } + else if (resolveElement instanceof JetTypeConstraint) { + typeConstraintAdditionalResolve(resolveSession, (JetTypeConstraint) resolveElement); + } + else if (PsiTreeUtil.getParentOfType(resolveElement, JetNamespaceHeader.class) != null) { + namespaceRefAdditionalResolve(resolveSession, trace, resolveElement); + } + else { + assert false : "Invalid type of the topmost parent"; + } + + return trace.getBindingContext(); + } + + private static void namespaceRefAdditionalResolve(ResolveSession resolveSession, BindingTrace trace, JetElement jetElement) { if (jetElement instanceof JetSimpleNameExpression) { + JetNamespaceHeader header = PsiTreeUtil.getParentOfType(jetElement, JetNamespaceHeader.class); + assert header != null; + JetSimpleNameExpression packageNameExpression = (JetSimpleNameExpression) jetElement; if (trace.getBindingContext().get(BindingContext.RESOLUTION_SCOPE, packageNameExpression) == null) { JetScope scope = getExpressionMemberScope(resolveSession, packageNameExpression); @@ -160,8 +182,8 @@ class ResolveElementCache { } } - private static void typeConstraintAdditionalResolve(KotlinCodeAnalyzer analyzer, JetElement jetElement) { - JetDeclaration declaration = PsiTreeUtil.getParentOfType(jetElement, JetDeclaration.class); + private static void typeConstraintAdditionalResolve(KotlinCodeAnalyzer analyzer, JetTypeConstraint jetTypeConstraint) { + JetDeclaration declaration = PsiTreeUtil.getParentOfType(jetTypeConstraint, JetDeclaration.class); DeclarationDescriptor descriptor = analyzer.resolveToDescriptor(declaration); assert (descriptor instanceof ClassDescriptor); @@ -212,7 +234,7 @@ class ResolveElementCache { final JetScope propertyResolutionScope = resolveSession.getInjector().getScopeProvider().getResolutionScopeForDeclaration( jetProperty); - BodyResolveContextForLazy bodyResolveContext = new BodyResolveContextForLazy(new com.google.common.base.Function() { + BodyResolveContextForLazy bodyResolveContext = new BodyResolveContextForLazy(new Function() { @Override public JetScope apply(JetDeclaration declaration) { assert declaration.getParent() == jetProperty : "Must be called only for property accessors, but called for " + declaration; @@ -287,7 +309,7 @@ class ResolveElementCache { return provider.getResolutionScopeForDeclaration(parentDeclaration); } - public static JetScope getExpressionMemberScope(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) { + private static JetScope getExpressionMemberScope(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) { BindingTrace trace = resolveSession.getStorageManager().createSafeTrace(new DelegatingBindingTrace( resolveSession.getBindingContext(), "trace to resolve a member scope of expression", expression)); @@ -363,9 +385,9 @@ class ResolveElementCache { private static class BodyResolveContextForLazy implements BodiesResolveContext { - private final com.google.common.base.Function declaringScopes; + private final Function declaringScopes; - private BodyResolveContextForLazy(@NotNull com.google.common.base.Function declaringScopes) { + private BodyResolveContextForLazy(@NotNull Function declaringScopes) { this.declaringScopes = declaringScopes; } @@ -395,8 +417,9 @@ class ResolveElementCache { } @Override - public com.google.common.base.Function getDeclaringScopes() { - return declaringScopes; + public Function getDeclaringScopes() { + //noinspection unchecked + return (Function) declaringScopes; } @Override