Skip to content

Commit 6d80aab

Browse files
committed
Fix IllegalStateException in ProjectOpenProcessor
Migrate [FlutterProjectOpenProcessor](cci:2://file:///usr/local/google/home/jwren/src/flutter-intellij/src/io/flutter/project/FlutterProjectOpenProcessor.kt:21:0-89:1) and [FlutterStudioProjectOpenProcessor](cci:2://file:///usr/local/google/home/jwren/src/flutter-intellij/src/io/flutter/editor/FlutterStudioProjectOpenProcessor.kt:17:0-61:1) from the deprecated [doOpenProject](cci:1://file:///usr/local/google/home/jwren/src/flutter-intellij/src/io/flutter/editor/FlutterStudioProjectOpenProcessor.kt:54:2-60:3) to [openProjectAsync](cci:1://file:///usr/local/google/home/jwren/src/flutter-intellij/src/io/flutter/project/FlutterProjectOpenProcessor.kt:38:2-54:3) to fix compatibility with IntelliJ IDEA 2025.3+. This change involves: - Converting [FlutterProjectOpenProcessor](cci:2://file:///usr/local/google/home/jwren/src/flutter-intellij/src/io/flutter/project/FlutterProjectOpenProcessor.kt:21:0-89:1) to Kotlin. - Converting [FlutterStudioProjectOpenProcessor](cci:2://file:///usr/local/google/home/jwren/src/flutter-intellij/src/io/flutter/editor/FlutterStudioProjectOpenProcessor.kt:17:0-61:1) to Kotlin. - Implementing [openProjectAsync](cci:1://file:///usr/local/google/home/jwren/src/flutter-intellij/src/io/flutter/project/FlutterProjectOpenProcessor.kt:38:2-54:3) using `writeAction` for project setup. - Enabling Kotlin compilation for the main source set in [build.gradle.kts](cci:7://file:///usr/local/google/home/jwren/src/flutter-intellij/build.gradle.kts:0:0-0:0). Fixes #8629 See https://plugins.jetbrains.com/docs/intellij/project-open-processor.html
1 parent 8a0804d commit 6d80aab

File tree

6 files changed

+204
-154
lines changed

6 files changed

+204
-154
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Fixed
1010

1111
- Fixed crash when using 3rd party loggers that don't implement `setLevel`. (#8631)
12+
- Fixed "Slow operations are prohibited on EDT" by migrating `FlutterProjectOpenProcessor` to Kotlin and using `openProjectAsync`. (#8629)
1213

1314
## 88.1.0
1415

build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ sourceSets {
141141
"third_party/vmServiceDrivers"
142142
)
143143
)
144+
kotlin.srcDirs(
145+
listOf(
146+
"src",
147+
"third_party/vmServiceDrivers"
148+
)
149+
)
144150
// Add kotlin.srcDirs if we start using Kotlin in the main plugin.
145151
resources.srcDirs(
146152
listOf(

src/io/flutter/editor/FlutterStudioProjectOpenProcessor.java

Lines changed: 0 additions & 57 deletions
This file was deleted.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2025 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
package io.flutter.editor
7+
8+
import com.intellij.openapi.application.writeAction
9+
import com.intellij.openapi.project.Project
10+
import com.intellij.openapi.vfs.VirtualFile
11+
import com.intellij.projectImport.ProjectOpenProcessor
12+
import com.intellij.ui.EditorNotifications
13+
import io.flutter.FlutterUtils
14+
import io.flutter.project.FlutterProjectOpenProcessor
15+
import io.flutter.pub.PubRoot
16+
import io.flutter.utils.FlutterModuleUtils
17+
18+
/**
19+
* Originally `FlutterStudioProjectOpenProcessor.java`.
20+
*
21+
* This processor is specific to Android Studio (or when the Flutter Studio plugin is active).
22+
* It extends `FlutterProjectOpenProcessor` to provide specialized handling for Android Studio,
23+
* particularly ensuring that the project is correctly re-detected if the opening process causes a reload.
24+
*
25+
* Converted to Kotlin to support `openProjectAsync`.
26+
*/
27+
class FlutterStudioProjectOpenProcessor : FlutterProjectOpenProcessor() {
28+
override val name: String
29+
get() = "Flutter Studio"
30+
31+
override fun canOpenProject(file: VirtualFile): Boolean {
32+
val root = PubRoot.forDirectory(file)
33+
return root != null && root.declaresFlutter()
34+
}
35+
36+
/**
37+
* Replaces the deprecated `doOpenProject`.
38+
*
39+
* Performs the same logic as the original Java implementation but using `suspend` and `writeAction`.
40+
*
41+
* Key differences from the base class:
42+
* - Explicitly looks up the project again using `FlutterUtils.findProject` after opening, as the project instance might have changed
43+
* (e.g. if the platform closed and reopened it during import).
44+
* - Ensures Dart SDK is enabled and notifications are updated.
45+
*/
46+
override suspend fun openProjectAsync(
47+
virtualFile: VirtualFile,
48+
projectToClose: Project?,
49+
forceOpenInNewFrame: Boolean,
50+
): Project? {
51+
val importProvider = getDelegateImportProvider(virtualFile) ?: return null
52+
val project = importProvider.openProjectAsync(virtualFile, projectToClose, forceOpenInNewFrame)
53+
54+
// A callback may have caused the project to be reloaded. Find the new Project object.
55+
val newProject = FlutterUtils.findProject(virtualFile.path)
56+
if (newProject == null || newProject.isDisposed) {
57+
return newProject
58+
}
59+
60+
writeAction {
61+
for (module in FlutterModuleUtils.getModules(newProject)) {
62+
if (FlutterModuleUtils.declaresFlutter(module) && !FlutterModuleUtils.isFlutterModule(module)) {
63+
FlutterModuleUtils.setFlutterModuleType(module)
64+
FlutterModuleUtils.enableDartSDK(module)
65+
}
66+
}
67+
newProject.save()
68+
EditorNotifications.getInstance(newProject).updateAllNotifications()
69+
}
70+
71+
return newProject
72+
}
73+
74+
override fun doOpenProject(
75+
virtualFile: VirtualFile,
76+
projectToClose: Project?,
77+
forceOpenInNewFrame: Boolean,
78+
): Project? {
79+
return null
80+
}
81+
}

src/io/flutter/project/FlutterProjectOpenProcessor.java

Lines changed: 0 additions & 97 deletions
This file was deleted.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2025 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
package io.flutter.project
7+
8+
import com.intellij.openapi.application.ApplicationInfo
9+
import com.intellij.openapi.application.writeAction
10+
import com.intellij.openapi.module.Module
11+
import com.intellij.openapi.project.Project
12+
import com.intellij.openapi.vfs.VirtualFile
13+
import com.intellij.projectImport.ProjectOpenProcessor
14+
import icons.FlutterIcons
15+
import io.flutter.FlutterBundle
16+
import io.flutter.FlutterUtils
17+
import io.flutter.pub.PubRoot
18+
import io.flutter.utils.FlutterModuleUtils
19+
import java.util.Objects
20+
import javax.swing.Icon
21+
22+
/**
23+
* Originally `FlutterProjectOpenProcessor.java`.
24+
*
25+
* This processor handles opening Flutter projects when they are selected directly (e.g. via "Open" in the IDE).
26+
* It delegates the actual opening to the platform's default processor (e.g. Gradle or Maven processor if applicable,
27+
* or the generic project opener) and then ensures that any modules in the project are correctly configured as Flutter modules.
28+
*
29+
* Converted to Kotlin to support `openProjectAsync` which is a suspend function.
30+
*/
31+
open class FlutterProjectOpenProcessor : ProjectOpenProcessor() {
32+
override val name: String
33+
get() = FlutterBundle.message("flutter.module.name")
34+
35+
override fun getIcon(file: VirtualFile): Icon? {
36+
return FlutterIcons.Flutter
37+
}
38+
39+
override fun canOpenProject(file: VirtualFile): Boolean {
40+
val info = ApplicationInfo.getInstance()
41+
if (FlutterUtils.isAndroidStudio()) {
42+
return false
43+
}
44+
val root = PubRoot.forDirectory(file)
45+
return root != null && root.declaresFlutter()
46+
}
47+
48+
/**
49+
* Replaces the deprecated `doOpenProject`.
50+
*
51+
* This method is `suspend` and must be used instead of `doOpenProject` to avoid `IllegalStateException` in newer IDE versions.
52+
*
53+
* It performs the following steps:
54+
* 1. Finds a delegate processor (e.g. Gradle) to open the project.
55+
* 2. Opens the project asynchronously.
56+
* 3. Once opened, checks if the project contains Flutter modules that are not yet configured as such (e.g. missing module type).
57+
* 4. Configures these modules as Flutter modules within a write action.
58+
*/
59+
override suspend fun openProjectAsync(
60+
virtualFile: VirtualFile,
61+
projectToClose: Project?,
62+
forceOpenInNewFrame: Boolean,
63+
): Project? {
64+
// Delegate opening to the platform open processor.
65+
val importProvider = getDelegateImportProvider(virtualFile) ?: return null
66+
val project = importProvider.openProjectAsync(virtualFile, projectToClose, forceOpenInNewFrame)
67+
if (project == null || project.isDisposed) return project
68+
69+
// Convert any modules that use Flutter but don't have IntelliJ Flutter metadata.
70+
writeAction {
71+
convertToFlutterProject(project)
72+
}
73+
74+
return project
75+
}
76+
77+
/**
78+
* Deprecated method, kept to satisfy the compiler/interface.
79+
*
80+
* We return `null` to indicate that this processor does not support the synchronous opening method
81+
* and that `openProjectAsync` should be used instead.
82+
*/
83+
override fun doOpenProject(
84+
virtualFile: VirtualFile,
85+
projectToClose: Project?,
86+
forceOpenInNewFrame: Boolean,
87+
): Project? {
88+
return null
89+
}
90+
91+
protected open fun getDelegateImportProvider(file: VirtualFile): ProjectOpenProcessor? {
92+
return EXTENSION_POINT_NAME.extensionList.stream().filter { processor: ProjectOpenProcessor ->
93+
processor.canOpenProject(file) && !Objects.equals(
94+
processor.name,
95+
name
96+
)
97+
}.findFirst().orElse(null)
98+
}
99+
100+
101+
companion object {
102+
/**
103+
* Sets up a project that doesn't have any Flutter modules.
104+
*
105+
*
106+
* (It probably wasn't created with "flutter create" and probably didn't have any IntelliJ configuration before.)
107+
*/
108+
private fun convertToFlutterProject(project: Project) {
109+
for (module in FlutterModuleUtils.getModules(project)) {
110+
if (FlutterModuleUtils.declaresFlutter(module) && !FlutterModuleUtils.isFlutterModule(module)) {
111+
FlutterModuleUtils.setFlutterModuleAndReload(module, project)
112+
}
113+
}
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)