IJPL-228507 quick fix for IDE misclassifies a file as a project when opening it via the command line

GitOrigin-RevId: 2f962b13c78edc1923585216c9e89ff8d0553524
This commit is contained in:
Andrei.Kuznetsov
2026-02-09 19:38:23 +00:00
committed by intellij-monorepo-bot
parent 04f5cbcd7f
commit e4ee59e043
2 changed files with 51 additions and 11 deletions
@@ -188,11 +188,15 @@ object ProjectUtil {
}
}
val storePathManager = serviceAsync<ProjectStorePathManager>()
val descriptor = withContext(Dispatchers.IO) {
storePathManager.getStoreDescriptor(file)
}
if (descriptor.testStoreDirectoryExistsForProjectRoot()) {
// `isDirectory` test here is for backward compatibility with 252: in 252 we never entered this method with regular files - only with
// directories. The problem here is that any regular file now has a storeDescriptor (getStoreDescriptor return type is not nullable),
// which evaluates to `regularFile.parent.resolve(".idea")`, which, if exists, pushes IDE to open a new project, instead of a file in
// the opened project. This is a tiny quick-fix for 253. We should rework the project open flow to avoid this strange check here.
if (Files.isDirectory(file) && isValidProjectPath(file)) {
val descriptor = withContext(Dispatchers.IO) {
serviceAsync<ProjectStorePathManager>().getStoreDescriptor(file)
}
LOG.info("Opening existing project with .idea at $file")
// see OpenProjectTest.`open valid existing project dir with inability to attach using OpenFileAction` test about why `runConfigurators = true` is specified here
val options = options.copy(
@@ -6,6 +6,7 @@ import com.intellij.ide.impl.ProjectUtil
import com.intellij.ide.impl.ProjectUtil.FolderOpeningMode.AS_FOLDER
import com.intellij.ide.impl.ProjectUtil.FolderOpeningMode.AS_PROJECT
import com.intellij.ide.impl.SelectProjectOpenProcessorDialog
import com.intellij.openapi.project.TestOpenMode.ModeFileOrFolderDefault
import com.intellij.openapi.project.TestOpenMode.ModeFolderAsFolder
import com.intellij.openapi.project.TestOpenMode.ModeFolderAsProject
import com.intellij.openapi.project.TestProjectSource.SourceCLI
@@ -41,15 +42,15 @@ import kotlin.io.path.writeText
// clean: .idea doesn't exists
// existing: project directory exists
// nested: .idea exists and ../.idea exists too
// multibuild: does not exist, and there are 2 marker build files (pom.xml and build.gradle)
// TODO: open regular file
// multibuild: .idea does not exist, and there are 2 marker build files (pom.xml and build.gradle)
// regular file: regular file that is not a folder
// with ability to attach - there is some defined ProjectAttachProcessor extension (e.g. WS, PS).
// with inability to attach - there is no any defined ProjectAttachProcessor extension (e.g. IU, IC).
enum class TestProjectSource { SourceOpenFileAction, SourceCLI }
enum class TestOpenMode { ModeFolderAsProject, ModeFolderAsFolder }
enum class TestOpenMode { ModeFileOrFolderDefault, ModeFolderAsProject, ModeFolderAsFolder }
@RunWith(Parameterized::class)
internal class OpenProjectTest(private val opener: Opener) {
@@ -79,7 +80,7 @@ internal class OpenProjectTest(private val opener: Opener) {
// I don't have strong opinion about defaultProjectTemplateShouldBeAppliedOverride.
// Weak opinion: a folder is not a project => we don't need default project settings.
// Feel free to change the test if you have strong opinion about desired behavior.
Opener(SourceCLI, ModeFolderAsFolder, expectedModules = emptyList(), expectedRoots = listOf($$"$ROOT$"), defaultProjectTemplateShouldBeAppliedOverride = false) {
Opener(SourceCLI, ModeFileOrFolderDefault, expectedModules = emptyList(), expectedRoots = listOf($$"$ROOT$"), defaultProjectTemplateShouldBeAppliedOverride = false) {
runBlocking { CommandLineProcessor.doOpenFileOrProject(it, createOrOpenExistingProject = false, false) }.project!!
},
)
@@ -152,9 +153,9 @@ internal class OpenProjectTest(private val opener: Opener) {
@Test
fun `open multibuild existing project dir with inability to attach`() = runBlocking(Dispatchers.Default) {
Assume.assumeTrue(
"This test does not handle ProjectAsProject mode yet, because `null` from SelectProjectOpenProcessorDialog" +
"This test does not handle ModeFolderAsProject mode yet, because `null` from SelectProjectOpenProcessorDialog" +
" has different behavior when opening folder from CLI and from open action, and we don't want to cement this behavior in tests.",
opener.mode == ModeFolderAsFolder,
opener.mode != ModeFolderAsProject,
)
val processorNames = ProjectOpenProcessor.EXTENSION_POINT_NAME.extensionList.map(ProjectOpenProcessor::name)
@@ -171,6 +172,41 @@ internal class OpenProjectTest(private val opener: Opener) {
}
}
@Test
fun `open project then open regular file in the same valid existing project dir with inability to attach`() = runBlocking(Dispatchers.Default) {
Assume.assumeTrue(
"Ignore ModeFolderAsProject/ModeFolderAsFolder, because we are checking open of regular files here, not folders",
opener.mode != ModeFolderAsProject && opener.mode != ModeFolderAsFolder,
)
val projectDir = tempDir.newPath("project")
projectDir.resolve(".idea").createDirectories()
val javaFile = projectDir.resolve("MyClass.java")
javaFile.writeText("public class MyClass {}")
ExtensionTestUtil.maskExtensions(ProjectAttachProcessor.EP_NAME, listOf(), disposableRule.disposable)
opener.opener(projectDir)!!.useProject { openedProject ->
val project = opener.opener(javaFile)
// the file should be opened in the already opened project
assertThat(project).isSameAs(openedProject)
}
Unit
}
@Test
fun `open project then open the the same valid existing project dir with inability to attach`() = runBlocking(Dispatchers.Default) {
val projectDir = tempDir.newPath("project")
projectDir.resolve(".idea").createDirectories()
ExtensionTestUtil.maskExtensions(ProjectAttachProcessor.EP_NAME, listOf(), disposableRule.disposable)
opener.opener(projectDir)!!.useProject { openedProject ->
val project = opener.opener(projectDir)
// this should bring already opened project to foreground
assertThat(project).isSameAs(openedProject)
}
Unit
}
private fun setupMultibuildProject(): Path {
val projectDir = tempDir.newPath("project")
projectDir.createDirectories()