Introduce TestSourcesFilter extension point
authorAlexander Zolotov <alexander.zolotov@jetbrains.com>
Mon, 18 Jul 2016 17:26:27 +0000 (20:26 +0300)
committerAlexander Zolotov <alexander.zolotov@jetbrains.com>
Mon, 18 Jul 2016 19:02:44 +0000 (22:02 +0300)
16 files changed:
platform/analysis-api/src/com/intellij/analysis/AnalysisScope.java
platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java
platform/analysis-api/src/com/intellij/psi/search/scope/ProjectProductionScope.java
platform/analysis-api/src/com/intellij/psi/search/scope/TestsScope.java
platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/PatternPackageSet.java
platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesInspectionBase.java
platform/indexing-impl/src/com/intellij/openapi/module/impl/scopes/ModuleWithDependentsScope.java
platform/indexing-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java
platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyTreeStructure.java
platform/platform-resources/src/META-INF/LangExtensionPoints.xml
platform/platform-resources/src/META-INF/LangExtensions.xml
platform/projectModel-api/src/com/intellij/openapi/roots/FileIndex.java
platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java [new file with mode: 0644]
platform/projectModel-impl/src/com/intellij/openapi/roots/ProjectRootTestSourcesFilter.java [new file with mode: 0644]
platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java
plugins/coverage-common/src/com/intellij/coverage/SimpleCoverageAnnotator.java

index bb818772286fadab5f5c6d6d7ef33dd9943de1f2..2b25cc0281b6461cde60b9c52d6023c3be6c7b85 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -39,10 +39,12 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileFilter;
 import com.intellij.openapi.vfs.VirtualFileVisitor;
 import com.intellij.psi.*;
-import com.intellij.psi.search.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.GlobalSearchScopesCore;
+import com.intellij.psi.search.LocalSearchScope;
+import com.intellij.psi.search.SearchScope;
 import com.intellij.psi.util.PsiUtilCore;
 import com.intellij.util.ArrayUtil;
-import com.intellij.util.Function;
 import com.intellij.util.Processor;
 import gnu.trove.THashSet;
 import org.intellij.lang.annotations.MagicConstant;
@@ -192,7 +194,7 @@ public class AnalysisScope {
     if (myFilter != null && !myFilter.contains(virtualFile)) {
       return true;
     }
-    return !myIncludeTestSource && fileIndex.isInTestSourceContent(virtualFile);
+    return !myIncludeTestSource && TestSourcesFilter.isTestSources(virtualFile, myProject);
   }
 
   @NotNull
@@ -512,7 +514,7 @@ public class AnalysisScope {
         return AnalysisScopeBundle.message("scope.option.module", myModule.getName());
 
       case MODULES:
-        String modules = StringUtil.join(myModules, module -> module.getName(), ", ");
+        String modules = StringUtil.join(myModules, Module::getName, ", ");
         return AnalysisScopeBundle.message("scope.module.list", modules, Integer.valueOf(myModules.size()));
 
       case PROJECT:
@@ -588,12 +590,12 @@ public class AnalysisScope {
       if (myElement instanceof PsiDirectory) {
         final VirtualFile directory = ((PsiFileSystemItem)myElement).getVirtualFile();
         if (index.isInSourceContent(directory)) {
-          return isTest ? index.isInTestSourceContent(directory) : !index.isInTestSourceContent(directory);
+          return isTest == TestSourcesFilter.isTestSources(directory, myProject);
         }
       } else if (myElement instanceof PsiFile) {
         final VirtualFile file = ((PsiFileSystemItem)myElement).getVirtualFile();
         if (file != null) {
-          return isTest ? index.isInTestSourceContent(file) : !index.isInTestSourceContent(file);
+          return isTest == TestSourcesFilter.isTestSources(file, myProject);
         }
       }
     }
@@ -726,11 +728,10 @@ public class AnalysisScope {
   public boolean isAnalyzeTestsByDefault() {
     switch (myType) {
       case DIRECTORY:
-        return ProjectRootManager.getInstance(myElement.getProject()).getFileIndex()
-          .isInTestSourceContent(((PsiDirectory)myElement).getVirtualFile());
+        return TestSourcesFilter.isTestSources(((PsiDirectory)myElement).getVirtualFile(), myElement.getProject());
       case FILE:
         final PsiFile containingFile = myElement.getContainingFile();
-        return ProjectRootManager.getInstance(containingFile.getProject()).getFileIndex().isInTestSourceContent(containingFile.getVirtualFile());
+        return TestSourcesFilter.isTestSources(containingFile.getVirtualFile(), containingFile.getProject());
       case MODULE:
         return isTestOnly(myModule);
       case MODULES:
index 5a88cc4aa779ff121a4af10dba57de7c25fbd265..23ec76fd38bae7b19eaefe6b678477af9bd3e180 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -19,6 +19,7 @@ import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectFileIndex;
 import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.TestSourcesFilter;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -28,7 +29,7 @@ import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiManager;
 import com.intellij.psi.search.scope.packageSet.*;
 import com.intellij.util.ArrayUtil;
-import com.intellij.util.Function;
+import com.intellij.util.ObjectUtils;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
@@ -135,7 +136,7 @@ public class GlobalSearchScopesCore {
 
     @Override
     public boolean contains(@NotNull VirtualFile file) {
-      return myFileIndex.isInSourceContent(file) && !myFileIndex.isInTestSourceContent(file);
+      return myFileIndex.isInSourceContent(file) && !TestSourcesFilter.isTestSources(file, ObjectUtils.assertNotNull(getProject()));
     }
 
     @Override
@@ -166,16 +167,13 @@ public class GlobalSearchScopesCore {
   }
 
   private static class TestScopeFilter extends GlobalSearchScope {
-    private final ProjectFileIndex myFileIndex;
-
     private TestScopeFilter(@NotNull Project project) {
       super(project);
-      myFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
     }
 
     @Override
     public boolean contains(@NotNull VirtualFile file) {
-      return myFileIndex.isInTestSourceContent(file);
+      return TestSourcesFilter.isTestSources(file, ObjectUtils.assertNotNull(getProject()));
     }
 
     @Override
index 6aa1c933824c1045d308160fecb4f8b97d1eeb37..4adf5a623cee2f99bde49a5a7df5b980d2430dc6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -19,6 +19,7 @@ import com.intellij.ide.IdeBundle;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectFileIndex;
 import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.TestSourcesFilter;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.search.scope.packageSet.AbstractPackageSet;
 import com.intellij.psi.search.scope.packageSet.NamedScope;
@@ -44,9 +45,9 @@ public class ProjectProductionScope extends NamedScope {
         final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex();
         return file != null
                && index.isInSource(file)
-               && !index.isInTestSourceContent(file)
                && !index.isInLibraryClasses(file)
-               && !index.isInLibrarySource(file);
+               && !index.isInLibrarySource(file)
+               && !TestSourcesFilter.isTestSources(file, project);
       }
     });
   }
index d52ab2d8cf4503405fef3436757bcdfeb8f0c97d..c952df3fbd02a8ff13a3fc765ccd63613ad33c30 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -17,8 +17,7 @@ package com.intellij.psi.search.scope;
 
 import com.intellij.ide.IdeBundle;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ProjectFileIndex;
-import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.TestSourcesFilter;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.search.scope.packageSet.AbstractPackageSet;
 import com.intellij.psi.search.scope.packageSet.NamedScope;
@@ -43,8 +42,7 @@ public class TestsScope extends NamedScope {
 
       @Override
       public boolean contains(VirtualFile file, @NotNull Project project, @Nullable NamedScopesHolder holder) {
-        final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex();
-        return file != null && index.isInTestSourceContent(file);
+        return file != null && TestSourcesFilter.isTestSources(file, project);
       }
     });
   }
index 41ff050b3ce5dd8deefe147f8bf47a1956c053ea..7deb3e147c74bee7e384ee16cbe2fcaa7300092b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -89,15 +89,15 @@ public class PatternPackageSet extends PatternBasedPackageSet {
       return fileIndex.isInContent(file) && FilePatternPackageSet.matchesModule(myModuleGroupPattern, myModulePattern, file, fileIndex);
     }
     if (myScope == SCOPE_SOURCE) {
-      return isSource && !fileIndex.isInTestSourceContent(file) && FilePatternPackageSet.matchesModule(myModuleGroupPattern, myModulePattern,
-                                                                                                       file, fileIndex);
+      return isSource && !TestSourcesFilter.isTestSources(file, project)
+             && FilePatternPackageSet.matchesModule(myModuleGroupPattern, myModulePattern, file, fileIndex);
     }
     if (myScope == SCOPE_LIBRARY) {
       return (fileIndex.isInLibraryClasses(file) || fileIndex.isInLibrarySource(file)) && matchesLibrary(myModulePattern, file, fileIndex);
     }
     if (myScope == SCOPE_TEST) {
-      return isSource && fileIndex.isInTestSourceContent(file) && FilePatternPackageSet.matchesModule(myModuleGroupPattern, myModulePattern,
-                                                                                                      file, fileIndex);
+      return isSource && TestSourcesFilter.isTestSources(file, project) &&
+             FilePatternPackageSet.matchesModule(myModuleGroupPattern, myModulePattern, file, fileIndex);
     }
     if (myScope == SCOPE_PROBLEM) {
       return isSource && WolfTheProblemSolver.getInstance(project).isProblemFile(file) &&
index 48ebd2d23ed1ad71792a4a79ef2939bec8294661..d405c66a752d66db7ea6f2b5dff116ad991b98af 100644 (file)
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2000-2016 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 com.intellij.dupLocator.index;
 
 import com.intellij.codeInspection.*;
@@ -15,6 +30,7 @@ import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.FileIndex;
 import com.intellij.openapi.roots.GeneratedSourcesFilter;
 import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.TestSourcesFilter;
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.vfs.VfsUtilCore;
@@ -251,7 +267,7 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
 
         if (myFileIndex.isInSourceContent(virtualFile)) {
           if (!myFileIndex.isInSourceContent(file)) return true;
-          if (!myFileIndex.isInTestSourceContent(virtualFile) && myFileIndex.isInTestSourceContent(file)) return true;
+          if (!TestSourcesFilter.isTestSources(virtualFile, project) && TestSourcesFilter.isTestSources(file, project)) return true;
           if (mySkipGeneratedCode) {
             if (!myFileWithinGeneratedCode && GeneratedSourcesFilter.isGeneratedSourceByAnyFilter(file, project)) return true;
           }
index d7c1792277ec11fd797c409902bd63b87987fe18..fedbcfb378bc8a1aad066b376692e15c5170584c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -29,7 +29,6 @@ import com.intellij.util.containers.Queue;
 import gnu.trove.THashSet;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 import java.util.Set;
 
@@ -109,7 +108,7 @@ class ModuleWithDependentsScope extends GlobalSearchScope {
   boolean contains(@NotNull VirtualFile file, boolean myOnlyTests) {
     Module moduleOfFile = myProjectFileIndex.getModuleForFile(file);
     if (moduleOfFile == null || !myModules.contains(moduleOfFile)) return false;
-    if (myOnlyTests && !myProjectFileIndex.isInTestSourceContent(file)) return false;
+    if (myOnlyTests && !TestSourcesFilter.isTestSources(file, moduleOfFile.getProject())) return false;
     return myProjectScope.contains(file);
   }
 
index 14b4949e282f8c2a932d5c40d50011e5fb2e6692..b99a71e5b44a7740990cda910ebae41458af030b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -19,7 +19,10 @@ import com.intellij.injected.editor.VirtualFileWindow;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.progress.ProgressIndicatorProvider;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.TestSourcesFilter;
 import com.intellij.openapi.roots.impl.LibraryScopeCache;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileWithId;
@@ -78,7 +81,7 @@ public class ResolveScopeManagerImpl extends ResolveScopeManager {
     ProjectFileIndex projectFileIndex = myProjectRootManager.getFileIndex();
     Module module = projectFileIndex.getModuleForFile(vFile);
     if (module != null) {
-      boolean includeTests = projectFileIndex.isInTestSourceContent(vFile);
+      boolean includeTests = TestSourcesFilter.isTestSources(vFile, myProject);
       return GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, includeTests);
     }
 
index 551fd38b9dc2d1daa7ff1e2e83f81eb88af40c61..d53565cba4deb442f65118624937390e0ef4cd84 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -19,13 +19,16 @@ package com.intellij.ide.hierarchy;
 import com.intellij.ide.util.treeView.AbstractTreeStructure;
 import com.intellij.ide.util.treeView.NodeDescriptor;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.TestSourcesFilter;
 import com.intellij.openapi.util.ActionCallback;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiDocumentManager;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
-import com.intellij.psi.search.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.GlobalSearchScopesCore;
+import com.intellij.psi.search.LocalSearchScope;
+import com.intellij.psi.search.SearchScope;
 import com.intellij.psi.search.scope.packageSet.NamedScope;
 import com.intellij.psi.search.scope.packageSet.NamedScopeManager;
 import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
@@ -160,14 +163,13 @@ public abstract class HierarchyTreeStructure extends AbstractTreeStructure {
     }
     else if (HierarchyBrowserBaseEx.SCOPE_PROJECT.equals(scopeType)) {
       final VirtualFile virtualFile = srcElement.getContainingFile().getVirtualFile();
-      if (virtualFile != null && ProjectRootManager.getInstance(myProject).getFileIndex().isInTestSourceContent(virtualFile)) {
+      if (virtualFile != null && TestSourcesFilter.isTestSources(virtualFile, myProject)) {
         return false;
       }
     }
     else if (HierarchyBrowserBaseEx.SCOPE_TEST.equals(scopeType)) {
-
       final VirtualFile virtualFile = srcElement.getContainingFile().getVirtualFile();
-      if (virtualFile != null && !ProjectRootManager.getInstance(myProject).getFileIndex().isInTestSourceContent(virtualFile)) {
+      if (virtualFile != null && !TestSourcesFilter.isTestSources(virtualFile, myProject)) {
         return false;
       }
     } else if (!HierarchyBrowserBaseEx.SCOPE_ALL.equals(scopeType)) {
index 513c8ff2367d7596ca7fdb2db957a8bb566f3226..d25176856a702a49e83ab303a5acb6d0439ffbd9 100644 (file)
                     interface="com.intellij.testIntegration.TestFinder"/>
     <extensionPoint name="testSrcLocator"
                     interface="com.intellij.testIntegration.TestLocationProvider"/>
+    <extensionPoint name="testSourcesFilter" interface="com.intellij.openapi.roots.TestSourcesFilter"/>
 
     <extensionPoint name="testCreator"
                     beanClass="com.intellij.lang.LanguageExtensionPoint">
index 1603d66c139f60dbd1e5081fe6fbb56d0a3337bc..9ebe7af9091f37e58bf5162e9ca61c7e89319375 100644 (file)
                     serviceImplementation="com.intellij.psi.impl.file.impl.ResolveScopeManagerImpl"/>
     <projectService serviceInterface="com.intellij.openapi.roots.ProjectFileIndex"
                     serviceImplementation="com.intellij.openapi.roots.impl.ProjectFileIndexImpl"/>
+    <testSourcesFilter implementation="com.intellij.openapi.roots.ProjectRootTestSourcesFilter"/>
     <moduleService serviceInterface="com.intellij.openapi.roots.ModuleFileIndex"
                     serviceImplementation="com.intellij.openapi.roots.impl.ModuleFileIndexImpl"/>
     <projectService serviceInterface="com.intellij.psi.impl.source.resolve.ResolveCache"
index 99eec952f1d9f8c67a7ca24ed97d13406c0f12a4..a53a4bfbc741581bc26e29e66577a5ff9bc2253d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -15,6 +15,7 @@
  */
 package com.intellij.openapi.roots;
 
+import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
@@ -78,6 +79,8 @@ public interface FileIndex {
   /**
    * Returns true if <code>fileOrDir</code> is a file or directory from the test content source
    *
+   * @see TestSourcesFilter#isTestSources(VirtualFile, Project)
+   * 
    * @param fileOrDir the file or directory to check.
    * @return true if the file or directory belongs to a test source root, false otherwise.
    */
diff --git a/platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java b/platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java
new file mode 100644 (file)
index 0000000..66b9b61
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2016 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 com.intellij.openapi.roots;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Implementations of this extension point can tell IDE whether some particular file is a test file despite project roots configuration.
+ * <p>
+ * By default, IntelliJ Platform considers files as tests only if they are located under test
+ * sources root {@link FileIndex#isInTestSourceContent(VirtualFile)}.
+ * <p>
+ * However there plenty frameworks and languages which keep test files just nearby production files.
+ * E.g. *_test.go files are test files in Go language and some js/dart files are test files depending
+ * on their content. The extensions allow IDE to highlight such files with a green background,
+ * properly check if they are included in built-in search scopes, etc.
+ *
+ * @see FileIndex#isInTestSourceContent(VirtualFile)
+ * @since 2016.3
+ * @author zolotov
+ */
+
+public abstract class TestSourcesFilter {
+  public static final ExtensionPointName<TestSourcesFilter> EP_NAME = ExtensionPointName.create("com.intellij.testSourcesFilter");
+
+  public static boolean isTestSources(@NotNull VirtualFile file, @NotNull Project project) {
+    for (TestSourcesFilter filter : EP_NAME.getExtensions()) {
+      if (filter.isTestSource(file, project)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public abstract boolean isTestSource(@NotNull VirtualFile file, @NotNull Project project);
+}
diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/ProjectRootTestSourcesFilter.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/ProjectRootTestSourcesFilter.java
new file mode 100644 (file)
index 0000000..345f557
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2016 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 com.intellij.openapi.roots;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+public class ProjectRootTestSourcesFilter extends TestSourcesFilter {
+  @Override
+  public boolean isTestSource(@NotNull VirtualFile file, @NotNull Project project) {
+    return ProjectFileIndex.SERVICE.getInstance(project).isInTestSourceContent(file);
+  }
+}
index b62d93533d12db5ac24cd5d33bfa405adfb17ca4..80b216211e84641ff50b7768e007af4a50d53145 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -17,8 +17,10 @@ package com.intellij.usages.impl.rules;
 
 import com.intellij.icons.AllIcons;
 import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectFileIndex;
 import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.TestSourcesFilter;
 import com.intellij.openapi.vcs.FileStatus;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiElement;
@@ -50,11 +52,11 @@ public class UsageScopeGroupingRule implements UsageGroupingRule, DumbAware {
     if (virtualFile == null) {
       return null;
     }
-    ProjectFileIndex fileIndex = ProjectRootManager.getInstance(element.getProject()).getFileIndex();
+    Project project = element.getProject();
+    ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
     boolean isInLib = fileIndex.isInLibraryClasses(virtualFile) || fileIndex.isInLibrarySource(virtualFile);
     if (isInLib) return LIBRARY;
-    boolean isInTest = fileIndex.isInTestSourceContent(virtualFile);
-    return isInTest ? TEST : PRODUCTION;
+    return TestSourcesFilter.isTestSources(virtualFile, project) ? TEST : PRODUCTION;
   }
 
   private static final UsageScopeGroup TEST = new UsageScopeGroup(0) {
index 8afe8628db3d9065a48ef5cafe0846f8e48a1d8a..10c24d5596cb7d9124e5e06b99d9bb037d8e0fe1 100644 (file)
@@ -1,9 +1,24 @@
+/*
+ * Copyright 2000-2016 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 com.intellij.coverage;
 
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectFileIndex;
 import com.intellij.openapi.roots.ProjectRootManager;
-import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.roots.TestSourcesFilter;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vfs.VfsUtilCore;
@@ -51,10 +66,7 @@ public abstract class SimpleCoverageAnnotator extends BaseCoverageAnnotator {
                                                @NotNull final CoverageSuitesBundle currentSuite) {
     final VirtualFile dir = directory.getVirtualFile();
 
-    final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(directory.getProject()).getFileIndex();
-    //final Module module = projectFileIndex.getModuleForFile(dir);
-
-    final boolean isInTestContent = projectFileIndex.isInTestSourceContent(dir);
+    final boolean isInTestContent = TestSourcesFilter.isTestSources(dir, directory.getProject());
     if (!currentSuite.isTrackTestFolders() && isInTestContent) {
       return null;
     }
@@ -171,8 +183,7 @@ public abstract class SimpleCoverageAnnotator extends BaseCoverageAnnotator {
                                                   @NotNull final ProjectFileIndex index,
                                                   @NotNull final CoverageEngine coverageEngine,
                                                   Set<VirtualFile> visitedDirs,
-                                                  @NotNull final Map<String, String> normalizedFiles2Files)
-  {
+                                                  @NotNull final Map<String, String> normalizedFiles2Files) {
     if (!index.isInContent(dir)) {
       return null;
     }
@@ -188,15 +199,14 @@ public abstract class SimpleCoverageAnnotator extends BaseCoverageAnnotator {
     }
     visitedDirs.add(dir);
 
-    final boolean isInTestSrcContent = index.isInTestSourceContent(dir);
+    final boolean isInTestSrcContent = TestSourcesFilter.isTestSources(dir, getProject());
 
     // Don't count coverage for tests folders if track test folders is switched off
     if (!trackTestFolders && isInTestSrcContent) {
       return null;
     }
 
-    final VirtualFile[] children = dataManager.doInReadActionIfProjectOpen(() -> dir.getChildren());
-
+    final VirtualFile[] children = dataManager.doInReadActionIfProjectOpen(dir::getChildren);
     if (children == null) {
       return null;
     }
@@ -281,7 +291,7 @@ public abstract class SimpleCoverageAnnotator extends BaseCoverageAnnotator {
                           suite.isTrackTestFolders(),
                           index,
                           suite.getCoverageEngine(),
-                          ContainerUtil.<VirtualFile>newHashSet(),
+                          ContainerUtil.newHashSet(),
                           Collections.unmodifiableMap(normalizedFiles2Files));
   }