diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java index f6bd788b9a0..63b503fe53f 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java @@ -33,15 +33,22 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.ModuleConfiguration; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ClassOrNamespaceDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.VariableDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.AnalyzingUtils; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.FqName; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.plugin.JetFileType; +import org.jetbrains.jet.resolve.DescriptorRenderer; +import org.jetbrains.jet.util.slicedmap.WritableSlice; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; /** * @author Evgeny Gerashchenko @@ -52,17 +59,33 @@ public class JetSourceNavigationHelper { } @Nullable - private static Tuple2 getBindingContextAndClassDescriptor(@NotNull JetClass decompiledClass) { - for (VirtualFile sourceDir : getAllSourceDirs(decompiledClass)) { - BindingContext bindingContext = analyzeLibrary(sourceDir, decompiledClass.getProject()); - ClassDescriptor cd = bindingContext.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, JetPsiUtil.getFQName(decompiledClass)); - if (cd != null) { - return new Tuple2(bindingContext, cd); + private static Tuple2 + getBindingContextAndClassOrNamespaceDescriptor(@NotNull WritableSlice slice, + @NotNull PsiElement psiElement, + @Nullable FqName fqName) { + if (fqName == null) { + return null; + } + for (VirtualFile sourceDir : getAllSourceDirs(psiElement)) { + BindingContext bindingContext = analyzeLibrary(sourceDir, psiElement.getProject()); + D descriptor = bindingContext.get(slice, fqName); + if (descriptor != null) { + return new Tuple2(bindingContext, descriptor); } } return null; } + @Nullable + private static Tuple2 getBindingContextAndClassDescriptor(@NotNull JetClass decompiledClass) { + return getBindingContextAndClassOrNamespaceDescriptor(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, decompiledClass, JetPsiUtil.getFQName(decompiledClass)); + } + + @Nullable + private static Tuple2 getBindingContextAndNamespaceDescriptor(@NotNull JetFile decompiledNamespaceFile) { + return getBindingContextAndClassOrNamespaceDescriptor(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, decompiledNamespaceFile, JetPsiUtil.getFQName(decompiledNamespaceFile)); + } + @Nullable public static JetClass getSourceClass(@NotNull JetClass decompiledClass) { Tuple2 bindingContextAndClassDescriptor = getBindingContextAndClassDescriptor(decompiledClass); @@ -91,12 +114,12 @@ public class JetSourceNavigationHelper { } @NotNull - private static List getAllSourceDirs(@NotNull JetClass decompiledClass) { + private static List getAllSourceDirs(@NotNull PsiElement psiElement) { List allSourceDirs = new ArrayList(); - VirtualFile decompiledFile = decompiledClass.getContainingFile().getVirtualFile(); + VirtualFile decompiledFile = psiElement.getContainingFile().getVirtualFile(); if (decompiledFile != null) { - ProjectFileIndex projectFileIndex = ProjectFileIndex.SERVICE.getInstance(decompiledClass.getProject()); + ProjectFileIndex projectFileIndex = ProjectFileIndex.SERVICE.getInstance(psiElement.getProject()); List orderEntries = projectFileIndex.getOrderEntriesForFile(decompiledFile); for (OrderEntry orderEntry : orderEntries) { Collections.addAll(allSourceDirs, orderEntry.getFiles(OrderRootType.SOURCES)); @@ -115,8 +138,30 @@ public class JetSourceNavigationHelper { PsiElement propertyContainer = decompiledProperty.getParent(); if (propertyContainer instanceof JetFile) { - // TODO global property, may be extension - return null; + Tuple2 bindingContextAndNamespaceDescriptor = getBindingContextAndNamespaceDescriptor((JetFile) propertyContainer); + if (bindingContextAndNamespaceDescriptor == null) return null; + BindingContext bindingContext = bindingContextAndNamespaceDescriptor._1; + NamespaceDescriptor namespaceDescriptor = bindingContextAndNamespaceDescriptor._2; + JetTypeReference receiverType = decompiledProperty.getReceiverTypeRef(); + if (receiverType == null) { + // non-extension property + Set properties = namespaceDescriptor.getMemberScope().getProperties(propertyName); + assert properties.size() <= 1; + if (properties.size() > 0) { + return (JetDeclaration) bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, properties.iterator().next()); + } + } else { + // extension property + String expectedTypeString = receiverType.getText(); + for (VariableDescriptor vd : namespaceDescriptor.getMemberScope().getProperties(propertyName)) { + if (vd.getReceiverParameter() != ReceiverDescriptor.NO_RECEIVER) { + String thisReceiverType = DescriptorRenderer.TEXT.renderType(vd.getReceiverParameter().getType()); + if (expectedTypeString.equals(thisReceiverType)) { + return (JetDeclaration) bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, vd); + } + } + } + } } else if (propertyContainer instanceof JetClassBody) { Tuple2 bindingContextAndClassDescriptor = getBindingContextAndClassDescriptor((JetClass) propertyContainer.getParent());