From 732c3fac2fe0891fb3ae47e69bb73d8cc4dd07ae Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 24 Dec 2014 15:32:26 +0100 Subject: [PATCH] Implement APIs added in IDEA 14.1 for Packages view and Coverage view to work. --- .../jet/asJava/JavaElementFinder.java | 35 +++++-- .../coverage/KotlinCoverageExtension.kt | 93 ++++++++++++++++++- 2 files changed, 120 insertions(+), 8 deletions(-) diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/JavaElementFinder.java b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/JavaElementFinder.java index 735ff5584f6..9681c97b960 100644 --- a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/JavaElementFinder.java +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/JavaElementFinder.java @@ -21,29 +21,26 @@ import com.google.common.collect.Collections2; import com.google.common.collect.Sets; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElementFinder; -import com.intellij.psi.PsiManager; -import com.intellij.psi.PsiPackage; +import com.intellij.psi.*; 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.SmartList; +import com.intellij.util.containers.Predicate; import com.intellij.util.containers.SLRUCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.JetClassOrObject; import org.jetbrains.jet.lang.psi.JetEnumEntry; +import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.java.JavaPsiFacadeKotlinHacks; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.NamePackage; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; public class JavaElementFinder extends PsiElementFinder implements JavaPsiFacadeKotlinHacks.KotlinFinderMarker { @@ -213,6 +210,30 @@ public class JavaElementFinder extends PsiElementFinder implements JavaPsiFacade return answer.toArray(new PsiClass[answer.size()]); } + // implements a method added in 14.1 + @NotNull + public PsiFile[] getPackageFiles(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { + List result = new ArrayList(); + FqName packageFQN = new FqName(psiPackage.getQualifiedName()); + result.addAll(lightClassGenerationSupport.findFilesForPackage(packageFQN, scope)); + return result.toArray(new PsiFile[result.size()]); + } + + // implements a method added in IDEA 14.1 + @SuppressWarnings({"UnusedDeclaration", "MethodMayBeStatic"}) + @Nullable + public Predicate getPackageFilesFilter(@NotNull final PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { + return new Predicate() { + @Override + public boolean apply(@Nullable PsiFile input) { + if (input instanceof JetFile && !(psiPackage.getQualifiedName().equals(((JetFile) input).getPackageFqName().asString()))) { + return false; + } + return true; + } + }; + } + private static class FindClassesRequest { private final String fqName; private final GlobalSearchScope scope; diff --git a/idea/src/org/jetbrains/jet/plugin/coverage/KotlinCoverageExtension.kt b/idea/src/org/jetbrains/jet/plugin/coverage/KotlinCoverageExtension.kt index 1bdb6aea475..2c3f25f6a87 100644 --- a/idea/src/org/jetbrains/jet/plugin/coverage/KotlinCoverageExtension.kt +++ b/idea/src/org/jetbrains/jet/plugin/coverage/KotlinCoverageExtension.kt @@ -31,15 +31,106 @@ import org.jetbrains.jet.lang.psi.JetTreeVisitor import org.jetbrains.jet.lang.psi.JetDeclaration import com.intellij.openapi.roots.ProjectRootManager import org.jetbrains.jet.lang.psi.JetTreeVisitorVoid -import org.jetbrains.annotations.TestOnly import com.intellij.openapi.application.ApplicationManager import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression import com.intellij.psi.PsiElement import java.util.LinkedHashSet +import com.intellij.psi.PsiClass +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiNamedElement +import com.intellij.coverage.PackageAnnotator +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.roots.CompilerModuleExtension +import com.intellij.coverage.JavaCoverageAnnotator +import org.jetbrains.jet.lang.psi.JetClassOrObject +import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.diagnostic.Logger public class KotlinCoverageExtension(): JavaCoverageEngineExtension() { + private val LOG = Logger.getInstance(javaClass()) + override fun isApplicableTo(conf: RunConfigurationBase?): Boolean = conf is JetRunConfiguration + override fun suggestQualifiedName(sourceFile: PsiFile, classes: Array, names: MutableSet): Boolean { + if (sourceFile is JetFile) { + names.addAll(collectOutputClassNames(sourceFile).map { StringUtil.replaceChar(it, '/', '.' )}) + return true + } + return false + } + + // Implements API added in IDEA 14.1 + fun getSummaryCoverageInfo(coverageAnnotator: JavaCoverageAnnotator, + element: PsiNamedElement): PackageAnnotator.ClassCoverageInfo? { + if (element is JetFile) { + LOG.info("Retrieving coverage for " + element.getName()) + val module = ModuleUtilCore.findModuleForPsiElement(element) + if (module == null) return null + val fileIndex = ProjectRootManager.getInstance(element.getProject()).getFileIndex() + val inTests = fileIndex.isInTestSourceContent(element.getVirtualFile()) + val compilerOutputExtension = CompilerModuleExtension.getInstance(module) + val outputRoot = if (inTests) + compilerOutputExtension.getCompilerOutputPathForTests() + else + compilerOutputExtension.getCompilerOutputPath() + if (outputRoot == null) return null + val fqName = element.getPackageFqName().asString().replace('.', '/') + val packageOutputDir = outputRoot.findFileByRelativePath(fqName) + if (packageOutputDir == null) return null + val prefixes = collectClassFilePrefixes(element) + LOG.debug("Classfile prefixes: [${prefixes.join(", ")}]") + val existingClassFiles = packageOutputDir.getChildren().filter { + file -> prefixes.any { file.getName().startsWith(it + "$") || file.getName().equals(it + ".class") } + } + if (existingClassFiles.size() > 0) { + LOG.debug("Classfiles: [${existingClassFiles.map { it.getName() }.join()}]") + val result = PackageAnnotator.ClassCoverageInfo() + result.totalClassCount = 0 + existingClassFiles.forEach { + val relativePath = VfsUtilCore.getRelativePath(it, outputRoot) + val qName = StringUtil.trimEnd(relativePath, ".class").replace("/", ".") + val classInfo = coverageAnnotator.getClassCoverageInfo(qName) + if (classInfo != null) { + result.totalClassCount += classInfo.totalClassCount + result.coveredClassCount += classInfo.coveredClassCount + result.totalMethodCount += classInfo.totalMethodCount + result.coveredMethodCount += classInfo.coveredMethodCount + result.totalLineCount += classInfo.totalLineCount + result.fullyCoveredLineCount += classInfo.fullyCoveredLineCount + result.partiallyCoveredLineCount += classInfo.partiallyCoveredLineCount + } else { + LOG.debug("Found no coverage for ${qName}") + } + } + return result + } + } + return null + } + + private fun collectClassFilePrefixes(file: JetFile): Collection { + val result = file.getChildren().filter { it is JetClassOrObject }.map { (it as JetClassOrObject).getName() } + val packagePartFqName = PackagePartClassUtils.getPackagePartFqName(file) + return result.union(arrayListOf(packagePartFqName.shortName().asString())) + } + + // Implements API added in IDEA 14.1 + fun keepCoverageInfoForClassWithoutSource(bundle: CoverageSuitesBundle, classFile: File): Boolean { + // TODO check scope and source roots + return true // keep everything, sort it out later + } + + // Implements API added in IDEA 14.1 + fun ignoreCoverageForClass(bundle: CoverageSuitesBundle, classFile: File): Boolean { + val packageName = classFile.getParentFile().getName() + // Ignore classes that only contain bridge methods delegating to package parts. + if (classFile.getName().equals(StringUtil.capitalize(packageName) + "Package.class")) { + return true + } + return false; + } + override fun collectOutputFiles(srcFile: PsiFile, output: VirtualFile?, testoutput: VirtualFile?,