diff --git a/platform/lvcs-impl/BUILD.bazel b/platform/lvcs-impl/BUILD.bazel
index bc9fc16d7c0b..02b04c88296e 100644
--- a/platform/lvcs-impl/BUILD.bazel
+++ b/platform/lvcs-impl/BUILD.bazel
@@ -85,6 +85,7 @@ jvm_library(
"//platform/vcs-impl/shared:shared_test_lib",
"@lib//:jetbrains-annotations",
"//platform/projectModel-impl",
+ "//platform/lang-core",
]
)
### auto-generated section `build intellij.platform.lvcs.impl` end
diff --git a/platform/lvcs-impl/intellij.platform.lvcs.impl.iml b/platform/lvcs-impl/intellij.platform.lvcs.impl.iml
index 95138f59d431..19891e2701a4 100644
--- a/platform/lvcs-impl/intellij.platform.lvcs.impl.iml
+++ b/platform/lvcs-impl/intellij.platform.lvcs.impl.iml
@@ -39,6 +39,7 @@
+
diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java
index cfc494e45301..8d92921f3c98 100644
--- a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java
+++ b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java
@@ -66,7 +66,7 @@ public class IdeaGateway {
return isVersioned(f, false);
}
- public boolean isVersioned(@NotNull VirtualFile f, boolean shouldBeInContent) {
+ public boolean isVersioned(@NotNull VirtualFile f, boolean shouldBeUnderAnyContentRoot) {
if (VersionManagingFileSystem.isDisabled(f)) {
return false;
}
@@ -84,31 +84,48 @@ public class IdeaGateway {
VersionedFilterData versionedFilterData = getVersionedFilterData();
- int numberOfOpenProjects = versionedFilterData.myOpenedProjects.size();
+ int numberOfOpenProjects = versionedFilterData.myProjectFileIndices.size();
// optimisation: FileTypeManager.isFileIgnored(f) will be checked inside ProjectFileIndex.isUnderIgnored()
if (numberOfOpenProjects == 0) {
- if (shouldBeInContent) return false; // there is no project, so the file can't be in content
+ if (shouldBeUnderAnyContentRoot) return false; // there is no project, so the file can't be in content
if (FileTypeManager.getInstance().isFileIgnored(f)) return false;
return true;
}
- boolean isExcludedFromAll = true;
- boolean isInContent = false;
-
+ boolean underAnyProjectContentRoot = false;
for (int i = 0; i < numberOfOpenProjects; ++i) {
- ProjectFileIndex index = versionedFilterData.myProjectFileIndices.get(i);
-
- if (index.isUnderIgnored(f)) return false;
- isInContent |= index.isInContent(f);
- isExcludedFromAll &= index.isExcluded(f);
+ ProjectFileIndex projectFileIndex = versionedFilterData.myProjectFileIndices.get(i);
+ boolean isProjectRelated = isProjectRelated(projectFileIndex, f);
+ // isIsContent returns true when the file or directory is under the content root of the project,
+ // AND is not excluded or ignored
+ if (isProjectRelated && projectFileIndex.isInContent(f)) {
+ // File is under the content root and isn't ignored/excluded. Track it in LVCS.
+ return true;
+ }
+ underAnyProjectContentRoot |= isProjectRelated;
}
- if (isExcludedFromAll) return false;
- if (shouldBeInContent && !isInContent) return false;
+ if (underAnyProjectContentRoot) {
+ // File is under the content root of at least one project, but it is ignored/excluded by all these projects.
+ // Do not track it in LVCS.
+ return false;
+ }
+ else if (shouldBeUnderAnyContentRoot) {
+ // At this moment the file isn't under any content root, and callsite asks us only for files that
+ // are under any content root. Do not track it in LVCS.
+ return false;
+ }
+ else {
+ // File isn't under the content root of any project, call-site doesn't care if it is.
+ // Check only if the file matches any ignored pattern.
+ return !FileTypeManager.getInstance().isFileIgnored(f);
+ }
+ }
- return true;
+ static boolean isProjectRelated(ProjectFileIndex projectFileIndex, VirtualFile file) {
+ return projectFileIndex.isInProjectOrExcluded(file) || projectFileIndex.isUnderIgnored(file);
}
public @NotNull String getPathOrUrl(@NotNull VirtualFile file) {
@@ -172,7 +189,6 @@ public class IdeaGateway {
}
protected static final class VersionedFilterData {
- final List myOpenedProjects = new ArrayList<>();
final List myProjectFileIndices = new ArrayList<>();
VersionedFilterData() {
@@ -182,7 +198,6 @@ public class IdeaGateway {
if (each.isDefault()) continue;
if (!each.isInitialized()) continue;
- myOpenedProjects.add(each);
myProjectFileIndices.add(ProjectRootManager.getInstance(each).getFileIndex());
}
}
diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.kt b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.kt
index a58846ebaf95..fec8fe14249d 100644
--- a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.kt
+++ b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.kt
@@ -3,6 +3,7 @@ package com.intellij.history.integration
import com.intellij.history.ActivityId
import com.intellij.history.core.LocalHistoryFacade
+import com.intellij.history.integration.IdeaGateway.isProjectRelated
import com.intellij.history.integration.LocalHistoryImpl.Companion.getInstanceImpl
import com.intellij.openapi.Disposable
import com.intellij.openapi.command.CommandEvent
@@ -63,7 +64,7 @@ internal class LocalHistoryEventDispatcher(private val facade: LocalHistoryFacad
val projectIndexes = IdeaGateway.getVersionedFilterData().myProjectFileIndices
var containingProjectIndex: ProjectFileIndex? = null
for (projectIndex in projectIndexes) {
- val isProjectRelated = projectIndex.isInProjectOrExcluded(dir) || projectIndex.isUnderIgnored(dir)
+ val isProjectRelated = isProjectRelated(projectIndex, dir)
if (!isProjectRelated) continue
if (containingProjectIndex != null) return false // more than 1 project contains this dir
diff --git a/platform/lvcs-impl/testSrc/com/intellij/history/integration/IdeaGatewayTest.java b/platform/lvcs-impl/testSrc/com/intellij/history/integration/IdeaGatewayTest.java
index 72e4984c1bdb..01c7029939d0 100644
--- a/platform/lvcs-impl/testSrc/com/intellij/history/integration/IdeaGatewayTest.java
+++ b/platform/lvcs-impl/testSrc/com/intellij/history/integration/IdeaGatewayTest.java
@@ -3,10 +3,24 @@ package com.intellij.history.integration;
import com.intellij.history.core.tree.Entry;
import com.intellij.history.core.tree.RootEntry;
+import com.intellij.ide.scratch.ScratchFileActions;
+import com.intellij.ide.scratch.ScratchFileCreationHelper;
import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
+import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.testFramework.OpenProjectTaskBuilder;
+import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -18,6 +32,8 @@ import java.util.Collection;
import java.util.Comparator;
import java.util.List;
+import static com.intellij.openapi.roots.ModuleRootModificationUtil.updateExcludedFolders;
+
public class IdeaGatewayTest extends IntegrationTestCase {
public void testFindingFile() {
assertEquals(myRoot, myGateway.findVirtualFile(myRoot.getPath()));
@@ -148,6 +164,118 @@ public class IdeaGatewayTest extends IntegrationTestCase {
getAllPaths(rootEntry));
}
+ public void testFileVersionedWhenInContentRoot() throws Exception {
+ // GIVEN
+ VirtualFile sourceFile = createFile("src/main/java/Main.java");
+ VirtualFile sourceDirectory = sourceFile.getParent();
+
+ // WHEN - THEN
+ assertTrue(myGateway.isVersioned(sourceDirectory));
+ assertTrue(myGateway.isVersioned(sourceFile));
+ assertTrue(myGateway.isVersioned(sourceDirectory, true));
+ assertTrue(myGateway.isVersioned(sourceFile, true));
+ }
+
+ public void testScratchFileVersionedOnlyWhenCallerDontCareAboutContentRoot() {
+ // GIVEN there is no module, so the project doesn't have content roots
+ ModifiableModuleModel model = ModuleManager.getInstance(getProject()).getModifiableModel();
+ model.disposeModule(getModule());
+ WriteCommandAction.runWriteCommandAction(getProject(), () -> model.commit());
+
+ // AND scratch file
+ ScratchFileCreationHelper.Context context = new ScratchFileCreationHelper.Context();
+ VirtualFile scratchFile = ScratchFileActions.doCreateNewScratch(getProject(), context).getVirtualFile();
+
+ // WHEN - THEN
+ assertTrue(myGateway.isVersioned(scratchFile));
+ assertFalse(myGateway.isVersioned(scratchFile, true));
+ }
+
+ public void testFileNotVersionedWhenHasSpecificName() throws Exception {
+ // GIVEN
+ List ignoredFiles = List.of(
+ createFile("workspace.xml"),
+ createFile("project.iws"),
+ createFile("some.class")
+ );
+
+ // WHEN - THEN
+ ignoredFiles.forEach(file -> {
+ assertFalse(myGateway.isVersioned(file));
+ assertFalse(myGateway.isVersioned(file, true));
+ });
+ }
+
+ public void testFileNotVersionedWhenInExcludedDirectory() throws Exception {
+ // GIVEN
+ VirtualFile fileInExcludedDirectory = createFile("out/example-file.txt");
+ VirtualFile excludedDirectory = fileInExcludedDirectory.getParent();
+
+ updateExcludedFolders(myModule, myRoot, List.of(), List.of(excludedDirectory.getUrl()));
+
+ // WHEN - THEN
+ assertFalse(myGateway.isVersioned(excludedDirectory));
+ assertFalse(myGateway.isVersioned(fileInExcludedDirectory));
+ assertFalse(myGateway.isVersioned(excludedDirectory, true));
+ assertFalse(myGateway.isVersioned(fileInExcludedDirectory, true));
+ }
+
+ public void testFileNotVersionedWhenGloballyIgnored() throws Exception {
+ // GIVEN
+ VirtualFile ignoredFile = createFile("src/main/resources/ignored.file");
+ VirtualFile resourcesDirectory = ignoredFile.getParent();
+
+ FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+ String originalIgnoredFileList = fileTypeManager.getIgnoredFilesList();
+ try {
+ WriteAction.runAndWait(() -> fileTypeManager.setIgnoredFilesList(originalIgnoredFileList + ";ignored.file"));
+
+ // WHEN - THEN
+ assertTrue(myGateway.isVersioned(resourcesDirectory));
+ assertFalse(myGateway.isVersioned(ignoredFile));
+ assertTrue(myGateway.isVersioned(resourcesDirectory, true));
+ assertFalse(myGateway.isVersioned(ignoredFile, true));
+ } finally {
+ WriteAction.runAndWait(() -> fileTypeManager.setIgnoredFilesList(originalIgnoredFileList));
+ }
+ }
+
+ public void testFileNotVersionedWhenIgnoredByOneOfTheProjects() throws Exception {
+ // GIVEN a second opened project
+ VirtualFile anotherProjectRootDir = getTempDir().createVirtualDir();
+ Project anotherProject = ProjectManagerEx.getInstanceEx().openProject(anotherProjectRootDir.toNioPath(), new OpenProjectTaskBuilder().build());
+ Disposer.register(getTestRootDisposable(), () -> PlatformTestUtil.forceCloseProjectWithoutSaving(anotherProject));
+
+ // AND a module in the second project
+ Module module = doCreateRealModuleIn(anotherProject.getName(), anotherProject, getModuleType());
+ ModuleRootModificationUtil.addContentRoot(module, anotherProjectRootDir.getPath());
+
+ // AND an ignored directory
+ VirtualFile ignoredDirectory = WriteAction.compute(() -> anotherProjectRootDir.createChildDirectory(this, "out"));
+ updateExcludedFolders(module, anotherProjectRootDir, List.of(), List.of(ignoredDirectory.getUrl()));
+
+ // AND a file inside the ignored directory
+ VirtualFile fileInIgnoredDirectory = WriteAction.compute(() -> ignoredDirectory.createChildData(this, "fileInAnotherProject.txt"));
+
+ // WHEN - THEN
+ assertSize(2, ProjectManagerEx.getInstanceEx().getOpenProjects());
+ assertFalse(myGateway.isVersioned(fileInIgnoredDirectory));
+ assertFalse(myGateway.isVersioned(fileInIgnoredDirectory, true));
+ }
+
+ public void testFileVersionedInOptimizedPath() throws Exception {
+ // GIVEN there are no opened projects
+ PlatformTestUtil.forceCloseProjectWithoutSaving(myProject);
+
+ // AND a file outside the content root (because the project isn't opened)
+ VirtualFile fileOutsideContentRoot = createFile("file/outside/content/root/Main.java");
+
+ // WHEN - THEN
+ assertEmpty(ProjectManagerEx.getInstanceEx().getOpenProjects());
+ assertTrue(myGateway.isVersioned(fileOutsideContentRoot));
+ assertFalse(myGateway.isVersioned(fileOutsideContentRoot, true));
+ }
+
public static @NotNull String getAllPaths(@NotNull RootEntry rootEntry) {
List result = new ArrayList<>();
printAllPaths(rootEntry, result);