From d6e3a392f6e1f4eaf5f43cffc55fd1d8b0531931 Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Wed, 9 May 2012 01:08:51 +0400 Subject: [PATCH] Added navigation to sources of builtin entities. #KT-1069 fixed --- .../jet/lang/resolve/BindingContextUtils.java | 11 -- idea/src/META-INF/plugin.xml | 3 + .../plugin/references/JetPsiReference.java | 7 + .../StandardLibraryReferenceResolver.java | 166 ++++++++++++++++++ 4 files changed, 176 insertions(+), 11 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/references/StandardLibraryReferenceResolver.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java index 389a3fa1486..938868bd8ee 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java @@ -24,7 +24,6 @@ import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.VariableAsFunctionResolvedCall; -import org.jetbrains.jet.lang.types.lang.JetStandardClasses; import org.jetbrains.jet.util.slicedmap.ReadOnlySlice; import org.jetbrains.jet.util.slicedmap.Slices; @@ -74,11 +73,6 @@ public class BindingContextUtils { return element; } - // TODO: Need to have a valid stubs for standard classes - if (referenceExpression != null && JetStandardClasses.getAllStandardClasses().contains(declarationDescriptor)) { - return referenceExpression.getContainingFile(); - } - return null; } @@ -94,11 +88,6 @@ public class BindingContextUtils { return elements; } - // TODO: Need to have a valid stubs for standard classes - if (referenceExpression != null && JetStandardClasses.getAllStandardClasses().contains(declarationDescriptor)) { - return Lists.newArrayList(referenceExpression.getContainingFile()); - } - return Lists.newArrayList(); } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index c04ae767ba5..4b3b4ac859a 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -18,6 +18,9 @@ org.jetbrains.jet.plugin.caches.JetCacheManager + + org.jetbrains.jet.plugin.references.StandardLibraryReferenceResolver + diff --git a/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java b/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java index cb64f9cd50c..a4bfbabd7df 100644 --- a/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java +++ b/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java @@ -110,6 +110,13 @@ public abstract class JetPsiReference implements PsiPolyVariantReference { if (psiElement.size() > 1) { return null; } + if (psiElement.isEmpty()) { + PsiElement stdlibSymbol = myExpression.getProject().getComponent(StandardLibraryReferenceResolver.class) + .resolveStandardLibrarySymbol(bindingContext, myExpression); + if (stdlibSymbol != null) { + return stdlibSymbol; + } + } Collection declarationDescriptors = bindingContext.get(AMBIGUOUS_REFERENCE_TARGET, myExpression); if (declarationDescriptors != null) return null; diff --git a/idea/src/org/jetbrains/jet/plugin/references/StandardLibraryReferenceResolver.java b/idea/src/org/jetbrains/jet/plugin/references/StandardLibraryReferenceResolver.java new file mode 100644 index 00000000000..e751c898592 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/references/StandardLibraryReferenceResolver.java @@ -0,0 +1,166 @@ +/* + * 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.plugin.references; + +import com.intellij.openapi.components.AbstractProjectComponent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetReferenceExpression; +import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler; +import org.jetbrains.jet.lang.resolve.scopes.WritableScope; +import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; +import org.jetbrains.jet.resolve.DescriptorRenderer; + +import java.util.Collections; +import java.util.List; + +public class StandardLibraryReferenceResolver extends AbstractProjectComponent { + @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") + private BindingContext bindingContext = null; + + public StandardLibraryReferenceResolver(Project project) { + super(project); + } + + @Override + public void initComponent() { + StartupManager.getInstance(myProject).registerPostStartupActivity(new Runnable() { + @Override + public void run() { + ensureInitialized(); + } + }); + } + + private synchronized void ensureInitialized() { + if (bindingContext != null) { + return; + } + + BindingTraceContext context = new BindingTraceContext(); + NamespaceDescriptorImpl fakeRootNamespace = new NamespaceDescriptorImpl(new ModuleDescriptor(""), + Collections.emptyList(), ""); + NamespaceDescriptorImpl jetNamespace = new NamespaceDescriptorImpl(fakeRootNamespace, + Collections.emptyList(), "jet"); + context.record(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, new FqName("jet"), jetNamespace); + + WritableScopeImpl scope = new WritableScopeImpl(JetScope.EMPTY, jetNamespace, RedeclarationHandler.THROW_EXCEPTION) + .setDebugName("Builtin classes scope"); + scope.changeLockLevel(WritableScope.LockLevel.BOTH); + jetNamespace.initialize(scope); + + TopDownAnalyzer.processStandardLibraryNamespace(myProject, context, scope, jetNamespace, getJetFiles("std")); + + ClassDescriptor tuple0 = context.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, new FqName("jet.Tuple0")); + assert tuple0 != null; + scope = new WritableScopeImpl(scope, jetNamespace, RedeclarationHandler.THROW_EXCEPTION).setDebugName("Builtin classes scope #2"); + scope.changeLockLevel(WritableScope.LockLevel.BOTH); + scope.addClassifierAlias("Unit", tuple0); + jetNamespace.initialize(scope); + + TopDownAnalyzer.processStandardLibraryNamespace(myProject, context, scope, jetNamespace, getJetFiles("jet")); + AnalyzingUtils.throwExceptionOnErrors(context.getBindingContext()); + + bindingContext = context.getBindingContext(); + } + + private List getJetFiles(String dir) { + String url = StandardLibraryReferenceResolver.class.getResource("/" + dir + "/").toString(); + if (url.startsWith("jar:file:")) { + url = url.replace("jar:file:", "jar:/"); + } + else if (url.startsWith("file:/")) { + url = url.replace("file:/", "file://"); + } + else { + assert false; + } + + VirtualFile vf = VirtualFileManager.getInstance().findFileByUrl(url); + assert vf != null; + PsiDirectory psiDirectory = PsiManager.getInstance(myProject).findDirectory(vf); + assert psiDirectory != null; + return ContainerUtil.mapNotNull(psiDirectory.getFiles(), new Function() { + @Override + public JetFile fun(PsiFile file) { + return file instanceof JetFile ? (JetFile) file : null; + } + }); + } + + @Nullable + private DeclarationDescriptor findCurrentDescriptor(@NotNull DeclarationDescriptor originalDescriptor) { + if (originalDescriptor instanceof ClassDescriptor) { + return bindingContext.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, DescriptorUtils.getFQName(originalDescriptor).toSafe()); + } + else if (originalDescriptor instanceof NamespaceDescriptor) { + return bindingContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, DescriptorUtils.getFQName(originalDescriptor).toSafe()); + } + else { + DeclarationDescriptor parent = originalDescriptor.getContainingDeclaration(); + if (parent == null) return null; + parent = findCurrentDescriptor(parent); + JetScope memberScope; + if (parent instanceof ClassDescriptor) { + memberScope = ((ClassDescriptor) parent).getDefaultType().getMemberScope(); + } + else if (parent instanceof NamespaceDescriptor) { + memberScope = ((NamespaceDescriptor)parent).getMemberScope(); + } + else { + return null; + } + String renderedOriginal = DescriptorRenderer.TEXT.render(originalDescriptor); + for (DeclarationDescriptor member : memberScope.getAllDescriptors()) { + if (renderedOriginal.equals(DescriptorRenderer.TEXT.render(member))) { + return member; + } + } + } + return null; + } + + @Nullable + public PsiElement resolveStandardLibrarySymbol(@NotNull BindingContext originalContext, @Nullable JetReferenceExpression referenceExpression) { + ensureInitialized(); + DeclarationDescriptor declarationDescriptor = BindingContextUtils.referenceToDescriptor(originalContext, referenceExpression); + + if (declarationDescriptor != null) { + declarationDescriptor = declarationDescriptor.getOriginal(); + declarationDescriptor = findCurrentDescriptor(declarationDescriptor); + if (declarationDescriptor != null) { + return BindingContextUtils.descriptorToDeclaration(bindingContext, declarationDescriptor); + } + } + return null; + } +}