diff --git a/flutter-idea/src/io/flutter/run/MainFile.java b/flutter-idea/src/io/flutter/run/MainFile.java index 959ba9334..0207fc1a2 100644 --- a/flutter-idea/src/io/flutter/run/MainFile.java +++ b/flutter-idea/src/io/flutter/run/MainFile.java @@ -6,8 +6,8 @@ package io.flutter.run; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.BaseProjectDirectories; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -79,11 +79,15 @@ public boolean hasFlutterImports() { * If there is an error, {@link Result#canLaunch} will return false and the error is available via {@link Result#getError} */ @NotNull - public static MainFile.Result verify(@Nullable String path, Project project) { + public static MainFile.Result verify(@Nullable String path, @Nullable Project project) { if (!ApplicationManager.getApplication().isReadAccessAllowed()) { throw new IllegalStateException("need read access"); } + if (project == null) { + return error("Project is not set."); + } + if (StringUtil.isEmptyOrSpaces(path)) { return error(FlutterBundle.message("entrypoint.not.set")); } @@ -123,7 +127,7 @@ public static MainFile.Result verify(@Nullable String path, Project project) { private static VirtualFile findAppDir(@Nullable VirtualFile file, @NotNull Project project) { if (WorkspaceCache.getInstance(project).isBazel()) { final Workspace workspace = WorkspaceCache.getInstance(project).get(); - assert(workspace != null); + assert (workspace != null); return workspace.getRoot(); } @@ -134,7 +138,7 @@ private static VirtualFile findAppDir(@Nullable VirtualFile file, @NotNull Proje } private static boolean isAppDir(@NotNull VirtualFile dir, @NotNull Project project) { - assert(!WorkspaceCache.getInstance(project).isBazel()); + assert (!WorkspaceCache.getInstance(project).isBazel()); return dir.isDirectory() && ( dir.findChild(PubRoot.PUBSPEC_YAML) != null || dir.findChild(PubRoot.DOT_DART_TOOL) != null || @@ -143,7 +147,10 @@ private static boolean isAppDir(@NotNull VirtualFile dir, @NotNull Project proje } private static boolean inProject(@Nullable VirtualFile file, @NotNull Project project) { - return file != null && ProjectRootManager.getInstance(project).getFileIndex().isInContent(file); + // Do a speedy check for containment over accessing the file index (which we did historically) + // but is very slow and unacceptably blocks the UI thread. + // See: https://github.com/flutter/flutter-intellij/issues/8089 + return file != null && BaseProjectDirectories.getInstance(project).contains(file); } /** diff --git a/flutter-idea/src/io/flutter/run/SdkRunConfig.java b/flutter-idea/src/io/flutter/run/SdkRunConfig.java index 462d37987..46aff35fc 100644 --- a/flutter-idea/src/io/flutter/run/SdkRunConfig.java +++ b/flutter-idea/src/io/flutter/run/SdkRunConfig.java @@ -136,9 +136,10 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro final MainFile mainFile = MainFile.verify(launchFields.getFilePath(), env.getProject()).get(); final Project project = env.getProject(); final RunMode mode = RunMode.fromEnv(env); - final Module module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject()); + final LaunchState.CreateAppCallback createAppCallback = (@Nullable FlutterDevice device) -> { if (device == null) return null; + if (mainFile == null) return null; final GeneralCommandLine command = getCommand(env, device); @@ -190,20 +191,23 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro } } + var module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject()); + if (module == null) return null; + return getFlutterApp(env, device, project, module, mode, command); }; final LaunchState launcher = new LaunchState(env, mainFile.getAppDir(), mainFile.getFile(), this, createAppCallback); - addConsoleFilters(launcher, env, mainFile, module); + addConsoleFilters(launcher, env, mainFile, null /* look up the module in an async read context */); return launcher; } static @NotNull FlutterApp getFlutterApp(@NotNull ExecutionEnvironment env, - @NotNull FlutterDevice device, - Project project, - Module module, - RunMode mode, - GeneralCommandLine command) throws ExecutionException { + @NotNull FlutterDevice device, + Project project, + Module module, + RunMode mode, + GeneralCommandLine command) throws ExecutionException { final FlutterApp app = FlutterApp.start(env, project, module, mode, device, command, StringUtil.capitalize(mode.mode()) + "App", "StopApp"); @@ -224,10 +228,18 @@ public void flutterSdkRemoved() { protected void addConsoleFilters(@NotNull LaunchState launcher, @NotNull ExecutionEnvironment env, @NotNull MainFile mainFile, + // If unspecified, we'll try and find it in a non-blocking read context. @Nullable Module module) { // Creating console filters is expensive so we want to make sure we are not blocking. // See: https://github.com/flutter/flutter-intellij/issues/8089 ReadAction.nonBlocking(() -> { + // Make a copy of the module reference, since we may update it in this lambda. + var moduleReference = module; + // If no module was passed in, try and find one. + if (moduleReference == null) { + moduleReference = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject()); + } + // Set up additional console filters. final TextConsoleBuilder builder = launcher.getConsoleBuilder(); if (builder == null) return null; @@ -235,9 +247,9 @@ protected void addConsoleFilters(@NotNull LaunchState launcher, builder.addFilter(new DartConsoleFilter(env.getProject(), mainFile.getFile())); //// links often found when running tests //builder.addFilter(new DartRelativePathsConsoleFilter(env.getProject(), mainFile.getAppDir().getPath())); - if (module != null) { + if (moduleReference != null) { // various flutter run links - builder.addFilter(new FlutterConsoleFilter(module)); + builder.addFilter(new FlutterConsoleFilter(moduleReference)); } // general urls builder.addFilter(new UrlFilter());