diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/JetIconProvider.java b/idea/idea-analysis/src/org/jetbrains/jet/plugin/JetIconProvider.java index 9177804b072..fa315488daf 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/JetIconProvider.java +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/JetIconProvider.java @@ -31,6 +31,7 @@ import org.jetbrains.jet.asJava.KotlinLightClassForExplicitDeclaration; import org.jetbrains.jet.asJava.KotlinLightClassForPackage; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.caches.resolve.KotlinLightClassForDecompiledDeclaration; import javax.swing.*; import java.util.List; @@ -102,6 +103,17 @@ public class JetIconProvider extends IconProvider { return JetIcons.FILE; } + if (psiElement instanceof KotlinLightClassForDecompiledDeclaration) { + JetClassOrObject origin = ((KotlinLightClassForDecompiledDeclaration) psiElement).getOrigin(); + if (origin != null) { + psiElement = origin; + } + else { + //TODO (light classes for decompiled files): correct presentation + return JetIcons.CLASS; + } + } + if (psiElement instanceof KotlinLightClassForExplicitDeclaration) { psiElement = psiElement.getNavigationElement(); } diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/IDELightClassGenerationSupport.java b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/IDELightClassGenerationSupport.java index 91d7da07549..8d9745f9f93 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/IDELightClassGenerationSupport.java +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/IDELightClassGenerationSupport.java @@ -20,9 +20,19 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.LibraryUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.ClassFileViewProvider; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.PsiManagerImpl; +import com.intellij.psi.impl.compiled.ClsClassImpl; +import com.intellij.psi.impl.compiled.ClsFileImpl; +import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; +import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.stubs.PsiClassHolderFileStub; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.cls.ClsFormatException; import kotlin.Function1; import kotlin.KotlinPackage; import org.jetbrains.annotations.NotNull; @@ -34,6 +44,7 @@ import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor; import org.jetbrains.jet.lang.descriptors.VariableDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils; import org.jetbrains.jet.lang.resolve.lazy.BodyResolveMode; import org.jetbrains.jet.lang.resolve.lazy.ForceResolveUtil; @@ -45,10 +56,13 @@ import org.jetbrains.jet.plugin.project.ResolveSessionForBodies; import org.jetbrains.jet.plugin.stubindex.JetFullClassNameIndex; import org.jetbrains.jet.plugin.stubindex.JetTopLevelClassByPackageIndex; import org.jetbrains.jet.plugin.stubindex.PackageIndexUtil; +import org.jetbrains.jet.plugin.util.ProjectRootsUtil; +import java.io.IOException; import java.util.*; -import static org.jetbrains.jet.plugin.stubindex.JetSourceFilterScope.kotlinSources; +import static kotlin.KotlinPackage.*; +import static org.jetbrains.jet.plugin.stubindex.JetSourceFilterScope.kotlinSourceAndClassFiles; public class IDELightClassGenerationSupport extends LightClassGenerationSupport { @@ -149,13 +163,13 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport @NotNull @Override public Collection findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) { - return JetFullClassNameIndex.getInstance().get(fqName.asString(), project, kotlinSources(searchScope, project)); + return JetFullClassNameIndex.getInstance().get(fqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project)); } @NotNull @Override public Collection findFilesForPackage(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) { - return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSources(searchScope, project), project); + return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSourceAndClassFiles(searchScope, project), project); } @NotNull @@ -176,18 +190,20 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport public Collection findClassOrObjectDeclarationsInPackage( @NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope ) { - return JetTopLevelClassByPackageIndex.getInstance().get(packageFqName.asString(), project, kotlinSources(searchScope, project)); + return JetTopLevelClassByPackageIndex.getInstance().get( + packageFqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project) + ); } @Override public boolean packageExists(@NotNull FqName fqName, @NotNull GlobalSearchScope scope) { - return PackageIndexUtil.packageExists(fqName, kotlinSources(scope, project), project); + return PackageIndexUtil.packageExists(fqName, kotlinSourceAndClassFiles(scope, project), project); } @NotNull @Override public Collection getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) { - return PackageIndexUtil.getSubPackageFqNames(fqn, kotlinSources(scope, project), project); + return PackageIndexUtil.getSubPackageFqNames(fqn, kotlinSourceAndClassFiles(scope, project), project); } @Nullable @@ -195,12 +211,63 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) { VirtualFile virtualFile = classOrObject.getContainingFile().getVirtualFile(); if (virtualFile != null && LibraryUtil.findLibraryEntry(virtualFile, classOrObject.getProject()) != null) { + if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile)) { + return getLightClassForDecompiledClassOrObject(classOrObject); + } return JetSourceNavigationHelper.getOriginalClass(classOrObject); } - return KotlinLightClassForExplicitDeclaration.create(psiManager, classOrObject); } + @Nullable + private static PsiClass getLightClassForDecompiledClassOrObject(@NotNull JetClassOrObject decompiledClassOrObject) { + JetFile containingJetFile = decompiledClassOrObject.getContainingJetFile(); + if (!containingJetFile.isCompiled()) { + return null; + } + PsiClass rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingJetFile); + if (rootLightClassForDecompiledFile == null) return null; + + return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile); + } + + @NotNull + private static PsiClass findCorrespondingLightClass( + @NotNull JetClassOrObject decompiledClassOrObject, + @NotNull PsiClass rootLightClassForDecompiledFile + ) { + List relativeClassNameSegments = getClassRelativeName(decompiledClassOrObject).pathSegments(); + Iterator iterator = relativeClassNameSegments.iterator(); + Name base = iterator.next(); + assert rootLightClassForDecompiledFile.getName().equals(base.asString()) + : "Light class for file:\n" + decompiledClassOrObject.getContainingJetFile().getVirtualFile().getCanonicalPath() + + "\nwas expected to have name: " + base.asString() + "\n Actual: " + rootLightClassForDecompiledFile.getName(); + PsiClass current = rootLightClassForDecompiledFile; + while (iterator.hasNext()) { + Name name = iterator.next(); + PsiClass innerClass = current.findInnerClassByName(name.asString(), false); + assert innerClass != null : "Inner class should be found"; + current = innerClass; + } + return current; + } + + @NotNull + private static FqName getClassRelativeName(@NotNull JetClassOrObject decompiledClassOrObject) { + Name name = decompiledClassOrObject.getNameAsName(); + if (name == null) { + assert decompiledClassOrObject instanceof JetObjectDeclaration && + ((JetObjectDeclaration) decompiledClassOrObject).isClassObject(); + name = Name.identifier(JvmAbi.CLASS_OBJECT_CLASS_NAME); + } + JetClassOrObject parent = PsiTreeUtil.getParentOfType(decompiledClassOrObject, JetClassOrObject.class, true); + if (parent == null) { + assert decompiledClassOrObject.isTopLevel(); + return FqName.topLevel(name); + } + return getClassRelativeName(parent).child(name); + } + @NotNull @Override public Collection getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) { @@ -208,21 +275,45 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport List packageClassesInfos = findPackageClassesInfos(packageFqName, scope); for (KotlinLightPackageClassInfo info : packageClassesInfos) { Collection files = info.getFiles(); - if (PackagePartClassUtils.getPackageFilesWithCallables(files).isEmpty()) continue; - KotlinLightClassForPackage lightClass = KotlinLightClassForPackage.create(psiManager, packageFqName, info.getScope(), files); - if (lightClass == null) continue; + List filesWithCallables = PackagePartClassUtils.getPackageFilesWithCallables(files); + if (filesWithCallables.isEmpty()) continue; - result.add(lightClass); + IdeaModuleInfo moduleInfo = info.getModuleInfo(); + if (moduleInfo instanceof ModuleSourceInfo) { + KotlinLightClassForPackage lightClass = + KotlinLightClassForPackage.create(psiManager, packageFqName, moduleInfo.contentScope(), files); + if (lightClass == null) continue; - if (files.size() > 1) { - for (JetFile file : files) { - result.add(new FakeLightClassForFileOfPackage(psiManager, lightClass, file)); + result.add(lightClass); + + if (files.size() > 1) { + for (JetFile file : files) { + result.add(new FakeLightClassForFileOfPackage(psiManager, lightClass, file)); + } + } + } + else { + PsiClass clsClass = getLightClassForDecompiledPackage(packageFqName, filesWithCallables); + if (clsClass != null) { + result.add(clsClass); } } } return result; } + @Nullable + private static PsiClass getLightClassForDecompiledPackage(@NotNull FqName packageFqName, @NotNull List filesWithCallables) { + JetFile firstFile = filesWithCallables.iterator().next(); + if (firstFile.isCompiled()) { + if (filesWithCallables.size() > 1) { + LOG.error("Several files with callables for package: " + packageFqName); + } + return createLightClassForDecompiledKotlinFile(firstFile); + } + return null; + } + @NotNull private List findPackageClassesInfos( @NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope @@ -231,7 +322,7 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport Map> filesByInfo = groupByModuleInfo(allFiles); List result = new ArrayList(); for (Map.Entry> entry : filesByInfo.entrySet()) { - result.add(new KotlinLightPackageClassInfo(entry.getValue(), entry.getKey().contentScope())); + result.add(new KotlinLightPackageClassInfo(entry.getValue(), entry.getKey())); } sortByClasspath(wholeScope, result); return result; @@ -267,11 +358,11 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport private static final class KotlinLightPackageClassInfo { private final Collection files; - private final GlobalSearchScope scope; + private final IdeaModuleInfo moduleInfo; - public KotlinLightPackageClassInfo(@NotNull Collection files, @NotNull GlobalSearchScope scope) { + public KotlinLightPackageClassInfo(@NotNull Collection files, @NotNull IdeaModuleInfo moduleInfo) { this.files = files; - this.scope = scope; + this.moduleInfo = moduleInfo; } @NotNull @@ -280,8 +371,65 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport } @NotNull - public GlobalSearchScope getScope() { - return scope; + public IdeaModuleInfo getModuleInfo() { + return moduleInfo; } } + + @Nullable + private static KotlinLightClassForDecompiledDeclaration createLightClassForDecompiledKotlinFile(@NotNull JetFile file) { + VirtualFile virtualFile = file.getVirtualFile(); + if (virtualFile == null) { + return null; + } + + ClsClassImpl javaClsClass = createClsJavaClassFromVirtualFile(file, virtualFile); + if (javaClsClass == null) { + return null; + } + JetClassOrObject declaration = singleOrNull(filterIsInstance(file.getDeclarations(), JetClassOrObject.class)); + return new KotlinLightClassForDecompiledDeclaration(javaClsClass, declaration); + } + + @Nullable + private static ClsClassImpl createClsJavaClassFromVirtualFile( + @NotNull final JetFile decompiledKotlinFile, + @NotNull VirtualFile virtualFile + ) { + final PsiJavaFileStubImpl javaFileStub = (PsiJavaFileStubImpl) createStub(virtualFile); + if (javaFileStub == null) { + return null; + } + PsiManager manager = PsiManager.getInstance(decompiledKotlinFile.getProject()); + ClsFileImpl fakeFile = new ClsFileImpl((PsiManagerImpl) manager, new ClassFileViewProvider(manager, virtualFile)) { + @NotNull + @Override + public PsiClassHolderFileStub getStub() { + return javaFileStub; + } + + @Override + public PsiElement getMirror() { + return decompiledKotlinFile; + } + }; + fakeFile.setPhysical(false); + javaFileStub.setPsi(fakeFile); + return (ClsClassImpl) single(fakeFile.getClasses()); + } + + @Nullable + private static PsiJavaFileStub createStub(@NotNull VirtualFile file) { + try { + return ClsFileImpl.buildFileStub(file, file.contentsToByteArray()); + } + catch (ClsFormatException e) { + LOG.debug(e); + } + catch (IOException e) { + LOG.debug(e); + } + LOG.error("Failed to build java cls class for " + file.getCanonicalPath()); + return null; + } } diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinLightClassForDecompiledDeclaration.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinLightClassForDecompiledDeclaration.kt new file mode 100644 index 00000000000..c9951e44068 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinLightClassForDecompiledDeclaration.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2010-2014 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.caches.resolve + +import org.jetbrains.jet.asJava.KotlinWrappingLightClass +import com.intellij.psi.PsiClass +import com.intellij.psi.impl.compiled.ClsClassImpl +import org.jetbrains.jet.plugin.JetLanguage +import org.jetbrains.jet.lang.psi.JetDeclaration +import org.jetbrains.jet.lang.psi.JetClassOrObject +import com.intellij.navigation.ItemPresentation +import com.intellij.psi.PsiElement + +class KotlinLightClassForDecompiledDeclaration( + private val clsClass: ClsClassImpl, + override val origin: JetClassOrObject? +) : KotlinWrappingLightClass(clsClass.getManager()) { + override fun copy() = this + + override fun getOwnInnerClasses(): List { + //TODO (light classes for decompiled files): correct origin + return clsClass.getOwnInnerClasses().map { KotlinLightClassForDecompiledDeclaration(it as ClsClassImpl, null) } + } + + //TODO (light classes for decompiled files): correct navigation element + override fun getNavigationElement() = origin?.getNavigationElement() ?: super.getNavigationElement() + + override fun getDelegate() = clsClass +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/util/ProjectRootsUtil.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/util/ProjectRootsUtil.kt index 187720f2adb..392d4cca937 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/util/ProjectRootsUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/util/ProjectRootsUtil.kt @@ -24,6 +24,8 @@ import org.jetbrains.jet.plugin.configuration.JetModuleTypeManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiDirectory +import com.intellij.openapi.roots.FileIndex +import com.intellij.ide.highlighter.JavaClassFileType public object ProjectRootsUtil { platformStatic @@ -66,4 +68,14 @@ public object ProjectRootsUtil { public fun isInProjectOrLibSource(element: PsiElement): Boolean { return isInSource(element, true) } + + platformStatic + public fun isLibraryClassFile(fileIndex: ProjectFileIndex, virtualFile: VirtualFile): Boolean { + return fileIndex.isInLibraryClasses(virtualFile) && virtualFile.getFileType() == JavaClassFileType.INSTANCE + } + + platformStatic + public fun isLibraryFile(fileIndex: ProjectFileIndex, virtualFile: VirtualFile): Boolean { + return fileIndex.isInLibraryClasses(virtualFile) || fileIndex.isInLibrarySource(virtualFile) + } }