From 43a7e8f386bd4082c340057c081669082d849af0 Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Wed, 1 Apr 2026 15:20:12 -0700 Subject: [PATCH] Revert "Fix IntelliJ IDEA project open hang by removing FlutterProjectOpenProcessor (#8846)" This reverts commit 4681015cd82dfc4b675f7e3471648853238855e3. --- CHANGELOG.md | 2 - resources/META-INF/plugin.xml | 2 + src/io/flutter/FlutterInitializer.java | 14 +-- .../project/FlutterProjectOpenProcessor.kt | 107 ++++++++++++++++++ 4 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 src/io/flutter/project/FlutterProjectOpenProcessor.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ef88f647..1ec2074e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,6 @@ ### Fixed -- Silent failure when opening Flutter projects without `.idea` directory in IntelliJ IDEA, by removing `FlutterProjectOpenProcessor` and - migrating configuration logic to `FlutterInitializer`. (#8846) - Gutter buttons not running tests with non-ASCII characters in their names. (#7985) - Freeze from JX Browser close. (#8864) - Crash in split debugger mode in IntelliJ 2025.3+. (#8831) diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 5c943d83d..0e49834ac 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -335,6 +335,8 @@ + + diff --git a/src/io/flutter/FlutterInitializer.java b/src/io/flutter/FlutterInitializer.java index 7bc1e4107..cd2621ee4 100644 --- a/src/io/flutter/FlutterInitializer.java +++ b/src/io/flutter/FlutterInitializer.java @@ -67,6 +67,8 @@ * Runs actions after the project has started up and the index is up to date. * * @see ProjectOpenActivity for actions that run earlier. + * @see io.flutter.project.FlutterProjectOpenProcessor for additional actions that + * may run when a project is being imported. */ public class FlutterInitializer extends FlutterProjectActivity { private boolean toolWindowsInitialized = false; @@ -97,18 +99,6 @@ public void executeProjectStartup(@NotNull Project project) { // Start a DevTools server DevToolsService.getInstance(project); - // Ensure Flutter project configuration is applied for projects that may have been - // opened without a .idea directory. Previously this was handled by FlutterProjectOpenProcessor, - // but that processor silently failed when no delegate processor could open the project. - // Instead, we let the platform open the project normally and apply our configuration here. - // See https://github.com/flutter/flutter-intellij/issues/8661 (Android Studio equivalent) - for (Module module : FlutterModuleUtils.getModules(project)) { - if (FlutterModuleUtils.declaresFlutter(module) && !FlutterModuleUtils.isFlutterModule(module)) { - log().info("Fixing Flutter module configuration for " + module.getName()); - FlutterModuleUtils.setFlutterModuleAndReload(module, project); - } - } - // If the project declares a Flutter dependency, do some extra initialization. boolean hasFlutterModule = false; diff --git a/src/io/flutter/project/FlutterProjectOpenProcessor.kt b/src/io/flutter/project/FlutterProjectOpenProcessor.kt new file mode 100644 index 000000000..edadecba9 --- /dev/null +++ b/src/io/flutter/project/FlutterProjectOpenProcessor.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2025 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +package io.flutter.project + +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.projectImport.ProjectOpenProcessor +import icons.FlutterIcons +import io.flutter.FlutterBundle +import io.flutter.FlutterUtils +import io.flutter.pub.PubRoot +import io.flutter.utils.FlutterModuleUtils +import java.util.* +import javax.swing.Icon + +/** + * Originally `FlutterProjectOpenProcessor.java`. + * + * This processor handles opening Flutter projects when they are selected directly (e.g. via "Open" in the IDE). + * It delegates the actual opening to the platform's default processor (e.g. Gradle or Maven processor if applicable, + * or the generic project opener) and then ensures that any modules in the project are correctly configured as Flutter modules. + * + * Converted to Kotlin to support `openProjectAsync` which is a suspend function. + */ +open class FlutterProjectOpenProcessor : ProjectOpenProcessor() { + override val name: String + get() = FlutterBundle.message("flutter.module.name") + + override fun getIcon(file: VirtualFile): Icon? { + return FlutterIcons.Flutter + } + + override fun canOpenProject(file: VirtualFile): Boolean { + if (FlutterUtils.isAndroidStudio()) { + return false + } + val root = PubRoot.forDirectory(file) + return root != null && root.declaresFlutter() + } + + /** + * Replaces the deprecated `doOpenProject`. + * + * This method is `suspend` and must be used instead of `doOpenProject` to avoid `IllegalStateException` in newer IDE versions. + * + * It performs the following steps: + * 1. Finds a delegate processor (e.g. Gradle) to open the project. + * 2. Opens the project asynchronously. + * 3. Once opened, checks if the project contains Flutter modules that are not yet configured as such (e.g. missing module type). + * 4. Configures these modules as Flutter modules within a write action. + */ + override suspend fun openProjectAsync( + virtualFile: VirtualFile, + projectToClose: Project?, + forceOpenInNewFrame: Boolean, + ): Project? { + // Delegate opening to the platform open processor. + val importProvider = getDelegateImportProvider(virtualFile) ?: return null + val project = importProvider.openProjectAsync(virtualFile, projectToClose, forceOpenInNewFrame) + if (project == null || project.isDisposed) return project + + // Convert any modules that use Flutter but don't have IntelliJ Flutter metadata. + convertToFlutterProject(project) + + return project + } + + /** + * Deprecated method, kept to satisfy the compiler/interface. + * + * We return `null` to indicate that this processor does not support the synchronous opening method + * and that `openProjectAsync` should be used instead. + */ + override fun doOpenProject( + virtualFile: VirtualFile, + projectToClose: Project?, + forceOpenInNewFrame: Boolean, + ): Project? { + return null + } + + protected open fun getDelegateImportProvider(file: VirtualFile): ProjectOpenProcessor? { + return EXTENSION_POINT_NAME.extensionList.stream().filter { processor: ProjectOpenProcessor -> + processor.canOpenProject(file) && !Objects.equals( + processor.name, + name + ) + }.findFirst().orElse(null) + } +} + +/** + * Sets up a project that doesn't have any Flutter modules. + * + * + * (It probably wasn't created with "flutter create" and probably didn't have any IntelliJ configuration before.) + */ +private fun convertToFlutterProject(project: Project) { + for (module in FlutterModuleUtils.getModules(project)) { + if (FlutterModuleUtils.declaresFlutter(module) && !FlutterModuleUtils.isFlutterModule(module)) { + FlutterModuleUtils.setFlutterModuleAndReload(module, project) + } + } +} \ No newline at end of file