From 923fd6be5796bbf2c2f4bc65e7c4188a67b39690 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 19 Apr 2019 16:54:35 -0700 Subject: [PATCH 1/4] clean up to our flutter web support --- src/io/flutter/FlutterUtils.java | 4 +- src/io/flutter/actions/ReloadFlutterApp.java | 10 ++ .../flutter/console/FlutterConsoleFilter.java | 7 +- src/io/flutter/devtools/DevToolsManager.java | 19 +-- src/io/flutter/run/FlutterReloadManager.java | 14 +- src/io/flutter/run/LaunchState.java | 88 +++++++------ src/io/flutter/run/OpenDevToolsAction.java | 16 +-- src/io/flutter/run/SdkAttachConfig.java | 26 ++-- src/io/flutter/run/SdkRunConfig.java | 99 +++++++------- src/io/flutter/run/bazel/BazelRunConfig.java | 3 +- src/io/flutter/run/daemon/DaemonApi.java | 14 -- src/io/flutter/run/daemon/DaemonEvent.java | 28 +++- src/io/flutter/run/daemon/DeviceDaemon.java | 9 +- src/io/flutter/run/daemon/FlutterApp.java | 37 ++++-- src/io/flutter/sdk/FlutterCommand.java | 72 ++-------- src/io/flutter/sdk/FlutterSdk.java | 18 ++- src/io/flutter/sdk/FlutterWebCommand.java | 40 ++++++ .../vmService/DartVmServiceDebugProcess.java | 28 ++-- src/io/flutter/view/FlutterPerfView.java | 1 - src/io/flutter/view/FlutterView.java | 25 ++-- .../flutter/run/daemon/DaemonEventTest.java | 4 +- .../dartlang/vm/service/VmServiceBase.java | 124 +++++++++++------- 22 files changed, 360 insertions(+), 326 deletions(-) create mode 100644 src/io/flutter/sdk/FlutterWebCommand.java diff --git a/src/io/flutter/FlutterUtils.java b/src/io/flutter/FlutterUtils.java index b00ea2b5b6..f44a5c4bf1 100644 --- a/src/io/flutter/FlutterUtils.java +++ b/src/io/flutter/FlutterUtils.java @@ -340,8 +340,8 @@ public static boolean declaresFlutter(@NotNull final VirtualFile pubspec) { */ public static boolean declaresFlutterWeb(@NotNull final VirtualFile pubspec) { // It uses Flutter if it contains: - //dependencies: - // flutter_web: any + // dependencies: + // flutter_web: try { final Map yamlMap = readPubspecFileToMap(pubspec); diff --git a/src/io/flutter/actions/ReloadFlutterApp.java b/src/io/flutter/actions/ReloadFlutterApp.java index babd828602..9293742f10 100644 --- a/src/io/flutter/actions/ReloadFlutterApp.java +++ b/src/io/flutter/actions/ReloadFlutterApp.java @@ -51,4 +51,14 @@ public void actionPerformed(@NotNull AnActionEvent e) { FlutterReloadManager.getInstance(project).saveAllAndReload(getApp(), FlutterConstants.RELOAD_REASON_MANUAL); } } + + // Override to disable the hot reload action when running flutter web apps. + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + + if (!getApp().appSupportsHotReload()) { + e.getPresentation().setEnabled(false); + } + } } diff --git a/src/io/flutter/console/FlutterConsoleFilter.java b/src/io/flutter/console/FlutterConsoleFilter.java index e1627c480d..4b9aeedcf5 100644 --- a/src/io/flutter/console/FlutterConsoleFilter.java +++ b/src/io/flutter/console/FlutterConsoleFilter.java @@ -76,10 +76,15 @@ public FlutterConsoleFilter(@NotNull Module module) { @VisibleForTesting @Nullable public VirtualFile fileAtPath(@NotNull String pathPart) { - // "lib/main.dart:6" pathPart = pathPart.split(":")[0]; + // We require the pathPart reference to be a file reference, otherwise we'd match things like + // "Build: Running build completed, took 191ms". + if (pathPart.indexOf('.') == -1) { + return null; + } + final VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots(); for (VirtualFile root : roots) { if (!pathPart.isEmpty()) { diff --git a/src/io/flutter/devtools/DevToolsManager.java b/src/io/flutter/devtools/DevToolsManager.java index 860fd9872e..85d14a6e94 100644 --- a/src/io/flutter/devtools/DevToolsManager.java +++ b/src/io/flutter/devtools/DevToolsManager.java @@ -106,16 +106,16 @@ public void onCancel() { } public void openBrowser() { - openBrowserImpl(-1); + openBrowserImpl(null); } - public void openBrowserAndConnect(int port) { - openBrowserImpl(port); + public void openBrowserAndConnect(String uri) { + openBrowserImpl(uri); } - private void openBrowserImpl(int port) { + private void openBrowserImpl(String uri) { if (devToolsInstance != null) { - devToolsInstance.openBrowserAndConnect(port); + devToolsInstance.openBrowserAndConnect(uri); return; } @@ -133,7 +133,7 @@ private void openBrowserImpl(int port) { DevToolsInstance.startServer(project, sdk, pubRoots.get(0), instance -> { devToolsInstance = instance; - devToolsInstance.openBrowserAndConnect(port); + devToolsInstance.openBrowserAndConnect(uri); }, instance -> { // Listen for closing, null out the devToolsInstance. devToolsInstance = null; @@ -209,13 +209,14 @@ public void processTerminated(@NotNull ProcessEvent event) { this.devtoolsPort = devtoolsPort; } - public void openBrowserAndConnect(int serviceProtocolPort) { - if (serviceProtocolPort == -1) { + public void openBrowserAndConnect(String serviceProtocolUri) { + if (serviceProtocolUri == null) { BrowserLauncher.getInstance().browse("http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&", null); } else { + // TODO: do we need to url encode uri? BrowserLauncher.getInstance().browse( - "http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&port=" + serviceProtocolPort, + "http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&uri=" + serviceProtocolUri, null ); } diff --git a/src/io/flutter/run/FlutterReloadManager.java b/src/io/flutter/run/FlutterReloadManager.java index 84024d25f4..78233590c7 100644 --- a/src/io/flutter/run/FlutterReloadManager.java +++ b/src/io/flutter/run/FlutterReloadManager.java @@ -29,7 +29,6 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindowId; @@ -72,10 +71,6 @@ * Handle the mechanics of performing a hot reload on file save. */ public class FlutterReloadManager { - private static final String RESTART_SUGGESTED_TEXT = - "Not all changed program elements ran during view reassembly; consider restarting (" - + (SystemInfo.isMac ? "⇧⌘S" : "⇧^S") + ")."; - private static final Logger LOG = Logger.getInstance(FlutterReloadManager.class); private static final Map toolWindowNotificationGroups = new HashMap<>(); @@ -175,7 +170,7 @@ private void handleSaveAllNotification(@Nullable Editor editor) { return; } - if (!app.getLaunchMode().supportsReload()) { + if (!app.getLaunchMode().supportsReload() || !app.appSupportsHotReload()) { return; } @@ -211,10 +206,6 @@ private void handleSaveAllNotification(@Nullable Editor editor) { notification.expire(); showRunNotification(app, "Hot Reload Error", result.getMessage(), true); } - else if (result.isRestartRecommended()) { - notification.expire(); - showRunNotification(app, "Reloading…", RESTART_SUGGESTED_TEXT, false); - } else { // Make sure the reloading message is displayed for at least 2 seconds (so it doesn't just flash by). final long delay = Math.max(0, 2000 - (System.currentTimeMillis() - startTime)); @@ -238,9 +229,6 @@ private void reloadApp(@NotNull FlutterApp app, @NotNull String reason) { if (!result.ok()) { showRunNotification(app, "Hot Reload", result.getMessage(), true); } - else if (result.isRestartRecommended()) { - showRunNotification(app, "Reloading…", RESTART_SUGGESTED_TEXT, false); - } }).exceptionally(throwable -> { showRunNotification(app, "Hot Reload", throwable.getMessage(), true); return null; diff --git a/src/io/flutter/run/LaunchState.java b/src/io/flutter/run/LaunchState.java index 41b511eb2f..7756b74b3a 100644 --- a/src/io/flutter/run/LaunchState.java +++ b/src/io/flutter/run/LaunchState.java @@ -119,25 +119,31 @@ protected RunContentDescriptor launch(@NotNull ExecutionEnvironment env) throws } final Project project = getEnvironment().getProject(); - final FlutterDevice device = DeviceService.getInstance(project).getSelectedDevice(); - - // Flutter web does not yet support device objects yet, hence the null as the indicator that this might be a flutter web project. - if (device == null) { - boolean isFlutterWeb = false; - final String filePath = ((SdkRunConfig)runConfig).getFields().getFilePath(); - if (filePath != null) { - final MainFile main = MainFile.verify(filePath, project).get(); - final PubRoot root = PubRoot.forDirectory(main.getAppDir()); - if (root != null) { - isFlutterWeb = FlutterUtils.declaresFlutterWeb(root.getPubspec()); - } - } - if (!isFlutterWeb) { - showNoDeviceConnectedMessage(project); - return null; + @Nullable FlutterDevice device = DeviceService.getInstance(project).getSelectedDevice(); + + boolean isFlutterWeb = false; + final String filePath = ((SdkRunConfig)runConfig).getFields().getFilePath(); + if (filePath != null) { + final MainFile main = MainFile.verify(filePath, project).get(); + final PubRoot root = PubRoot.forDirectory(main.getAppDir()); + if (root != null) { + isFlutterWeb = FlutterUtils.declaresFlutterWeb(root.getPubspec()); } } + + // Flutter web does not yet support devices. + if (isFlutterWeb) { + device = null; + } + else if (device == null) { + showNoDeviceConnectedMessage(project); + return null; + } + final FlutterApp app = myCreateAppCallback.createApp(device); + if (isFlutterWeb) { + app.setIsFlutterWeb(true); + } // Cache for use in console configuration. FlutterApp.addToEnvironment(env, app); @@ -147,10 +153,8 @@ protected RunContentDescriptor launch(@NotNull ExecutionEnvironment env) throws final ExecutionResult result = setUpConsoleAndActions(app); - // For Bazel run configurations, - // where the console is not null, - // and we find the expected process handler type, - // print the command line command to the console. + // For Bazel run configurations, where the console is not null, and we find the expected + // process handler type, print the command line command to the console. if (runConfig instanceof BazelRunConfig && app.getConsole() != null && app.getProcessHandler() instanceof OSProcessHandler) { @@ -394,31 +398,35 @@ protected RunContentDescriptor doExecute(@NotNull RunProfileState state, @NotNul final FlutterApp app = FlutterApp.fromProcess(process); final String selectedDeviceId = getSelectedDeviceId(env.getProject()); - if (app != null && StringUtil.equals(app.deviceId(), selectedDeviceId)) { - if (executorId.equals(app.getMode().mode())) { - if (!identicalCommands(app.getCommand(), launchState.runConfig.getCommand(env, app.device()))) { - // To be safe, relaunch as the arguments to launch have changed. - try { - // TODO(jacobr): ideally we shouldn't be synchronously waiting for futures like this - // but I don't see a better option. In practice this seems fine. - app.shutdownAsync().get(); + if (app != null) { + final boolean sameDevice = app.getIsFlutterWeb() || StringUtil.equals(app.deviceId(), selectedDeviceId); + + if (sameDevice) { + if (executorId.equals(app.getMode().mode())) { + if (!identicalCommands(app.getCommand(), launchState.runConfig.getCommand(env, app.device()))) { + // To be safe, relaunch as the arguments to launch have changed. + try { + // TODO(jacobr): ideally we shouldn't be synchronously waiting for futures like this + // but I don't see a better option. In practice this seems fine. + app.shutdownAsync().get(); + } + catch (InterruptedException | java.util.concurrent.ExecutionException e) { + FlutterUtils.warn(LOG, e); + } + return launchState.launch(env); } - catch (InterruptedException | java.util.concurrent.ExecutionException e) { - FlutterUtils.warn(LOG, e); + + final FlutterLaunchMode launchMode = FlutterLaunchMode.fromEnv(env); + if (launchMode.supportsReload() && app.isStarted()) { + // Map a re-run action to a flutter hot restart. + FileDocumentManager.getInstance().saveAllDocuments(); + FlutterInitializer.sendAnalyticsAction(RestartFlutterApp.class.getSimpleName()); + app.performRestartApp(FlutterConstants.RELOAD_REASON_SAVE); } - return launchState.launch(env); } - final FlutterLaunchMode launchMode = FlutterLaunchMode.fromEnv(env); - if (launchMode.supportsReload() && app.isStarted()) { - // Map a re-run action to a flutter hot restart. - FileDocumentManager.getInstance().saveAllDocuments(); - FlutterInitializer.sendAnalyticsAction(RestartFlutterApp.class.getSimpleName()); - app.performRestartApp(FlutterConstants.RELOAD_REASON_SAVE); - } + return null; } - - return null; } } diff --git a/src/io/flutter/run/OpenDevToolsAction.java b/src/io/flutter/run/OpenDevToolsAction.java index f8c31dbb9d..d65f545bd5 100644 --- a/src/io/flutter/run/OpenDevToolsAction.java +++ b/src/io/flutter/run/OpenDevToolsAction.java @@ -15,8 +15,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.net.MalformedURLException; -import java.net.URL; import java.util.concurrent.CompletableFuture; public class OpenDevToolsAction extends DumbAwareAction { @@ -70,22 +68,12 @@ public void actionPerformed(@NotNull final AnActionEvent event) { return; } - final URL url; - try { - url = new URL(urlString); - } - catch (MalformedURLException e) { - return; - } - - final int port = url.getPort(); - if (devToolsManager.hasInstalledDevTools()) { - devToolsManager.openBrowserAndConnect(port); + devToolsManager.openBrowserAndConnect(urlString); } else { final CompletableFuture result = devToolsManager.installDevTools(); - result.thenAccept(o -> devToolsManager.openBrowserAndConnect(port)); + result.thenAccept(o -> devToolsManager.openBrowserAndConnect(urlString)); } } } diff --git a/src/io/flutter/run/SdkAttachConfig.java b/src/io/flutter/run/SdkAttachConfig.java index 134a1ffabf..4cbdd63c8f 100644 --- a/src/io/flutter/run/SdkAttachConfig.java +++ b/src/io/flutter/run/SdkAttachConfig.java @@ -71,22 +71,22 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro throw new ExecutionException(e); } - SdkFields launchFields = getFields(); - MainFile mainFile = MainFile.verify(launchFields.getFilePath(), env.getProject()).get(); - Project project = env.getProject(); - RunMode mode = RunMode.fromEnv(env); - Module module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject()); - LaunchState.CreateAppCallback createAppCallback = (device) -> { + final SdkFields launchFields = getFields(); + 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; - GeneralCommandLine command = getCommand(env, device); + final GeneralCommandLine command = getCommand(env, device); - FlutterApp app = FlutterApp.start(env, project, module, mode, device, command, + final FlutterApp app = FlutterApp.start(env, project, module, mode, device, command, StringUtil.capitalize(mode.mode()) + "App", - "StopApp"); + "StopApp"); // Stop the app if the Flutter SDK changes. - FlutterSdkManager.Listener sdkListener = new FlutterSdkManager.Listener() { + final FlutterSdkManager.Listener sdkListener = new FlutterSdkManager.Listener() { @Override public void flutterSdkRemoved() { app.shutdownAsync(); @@ -98,7 +98,7 @@ public void flutterSdkRemoved() { return app; }; - LaunchState launcher = new AttachState(env, mainFile.getAppDir(), mainFile.getFile(), this, createAppCallback); + final LaunchState launcher = new AttachState(env, mainFile.getAppDir(), mainFile.getFile(), this, createAppCallback); addConsoleFilters(launcher, env, mainFile, module); return launcher; } @@ -110,12 +110,12 @@ public GeneralCommandLine getCommand(@NotNull ExecutionEnvironment env, FlutterD } private void checkRunnable(@NotNull Project project) throws RuntimeConfigurationError { - DartSdk sdk = DartPlugin.getDartSdk(project); + final DartSdk sdk = DartPlugin.getDartSdk(project); if (sdk == null) { throw new RuntimeConfigurationError(FlutterBundle.message("dart.sdk.is.not.configured"), () -> DartConfigurable.openDartSettings(project)); } - MainFile.Result main = MainFile.verify(getFields().getFilePath(), project); + final MainFile.Result main = MainFile.verify(getFields().getFilePath(), project); if (!main.canLaunch()) { throw new RuntimeConfigurationError(main.getError()); } diff --git a/src/io/flutter/run/SdkRunConfig.java b/src/io/flutter/run/SdkRunConfig.java index 5f4c32b4e1..a25cb851ca 100644 --- a/src/io/flutter/run/SdkRunConfig.java +++ b/src/io/flutter/run/SdkRunConfig.java @@ -92,13 +92,11 @@ public static class RecursiveDeleter private final PathMatcher matcher; RecursiveDeleter(String pattern) { - matcher = FileSystems.getDefault() - .getPathMatcher("glob:" + pattern); + matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern); } @Override - public FileVisitResult visitFile(Path file, - BasicFileAttributes attrs) { + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { final Path name = file.getFileName(); if (name != null && matcher.matches(name)) { try { @@ -113,14 +111,12 @@ public FileVisitResult visitFile(Path file, } @Override - public FileVisitResult preVisitDirectory(Path dir, - BasicFileAttributes attrs) { + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { return CONTINUE; } @Override - public FileVisitResult visitFileFailed(Path file, - IOException exc) { + public FileVisitResult visitFileFailed(Path file, IOException exc) { FlutterUtils.warn(LOG, exc); return CONTINUE; } @@ -141,58 +137,57 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro final Project project = env.getProject(); final RunMode mode = RunMode.fromEnv(env); final Module module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject()); - final LaunchState.CreateAppCallback createAppCallback = (device) -> { - // Up until the FlutterWeb support, device was checked for null and returned. The device - // can only be null if this is a FlutterWeb execution, this expecation is checked elsewhere. + final LaunchState.CreateAppCallback createAppCallback = (@Nullable FlutterDevice device) -> { + // Up until the support for Flutter Web, 'device' was checked for null and returned. The device + // can only be null if this is a Flutter Web execution; this expecation is checked elsewhere. final GeneralCommandLine command = getCommand(env, device); - { - // Workaround for https://github.com/flutter/flutter/issues/16766 - // TODO(jacobr): remove once flutter tool incremental building works - // properly with --track-widget-creation. - final Path buildPath = command.getWorkDirectory().toPath().resolve("build"); - final Path cachedParametersPath = buildPath.resolve("last_build_run.json"); - final String[] parametersToTrack = {"--preview-dart-2", "--track-widget-creation"}; - final JsonArray jsonArray = new JsonArray(); - for (String parameter : command.getParametersList().getList()) { - for (String allowedParameter : parametersToTrack) { - if (parameter.startsWith(allowedParameter)) { - jsonArray.add(new JsonPrimitive(parameter)); - break; - } + + // Workaround for https://github.com/flutter/flutter/issues/16766 + // TODO(jacobr): remove once flutter tool incremental building works + // properly with --track-widget-creation. + final Path buildPath = command.getWorkDirectory().toPath().resolve("build"); + final Path cachedParametersPath = buildPath.resolve("last_build_run.json"); + final String[] parametersToTrack = {"--preview-dart-2", "--track-widget-creation"}; + final JsonArray jsonArray = new JsonArray(); + for (String parameter : command.getParametersList().getList()) { + for (String allowedParameter : parametersToTrack) { + if (parameter.startsWith(allowedParameter)) { + jsonArray.add(new JsonPrimitive(parameter)); + break; } } - final String json = new Gson().toJson(jsonArray); - String existingJson = null; - if (Files.exists(cachedParametersPath)) { - try { - existingJson = new String(Files.readAllBytes(cachedParametersPath), StandardCharsets.UTF_8); - } - catch (IOException e) { - FlutterUtils.warn(LOG, "Unable to get existing json from " + cachedParametersPath); - } + } + final String json = new Gson().toJson(jsonArray); + String existingJson = null; + if (Files.exists(cachedParametersPath)) { + try { + existingJson = new String(Files.readAllBytes(cachedParametersPath), StandardCharsets.UTF_8); } - if (!StringUtil.equals(json, existingJson)) { - // We don't have proof the current run is consistent with the existing run. - // Be safe and delete cached files that could cause problems due to - // https://github.com/flutter/flutter/issues/16766 - // We could just delete app.dill and snapshot_blob.bin.d.fingerprint - // but it is safer to just delete everything as we won't be broken by future changes - // to the underlying Flutter build rules. - try { - if (Files.exists(buildPath)) { - if (Files.isDirectory(buildPath)) { - Files.walkFileTree(buildPath, new RecursiveDeleter("*.{fingerprint,dill}")); - } - } - else { - Files.createDirectory(buildPath); + catch (IOException e) { + FlutterUtils.warn(LOG, "Unable to get existing json from " + cachedParametersPath); + } + } + if (!StringUtil.equals(json, existingJson)) { + // We don't have proof the current run is consistent with the existing run. + // Be safe and delete cached files that could cause problems due to + // https://github.com/flutter/flutter/issues/16766 + // We could just delete app.dill and snapshot_blob.bin.d.fingerprint + // but it is safer to just delete everything as we won't be broken by future changes + // to the underlying Flutter build rules. + try { + if (Files.exists(buildPath)) { + if (Files.isDirectory(buildPath)) { + Files.walkFileTree(buildPath, new RecursiveDeleter("*.{fingerprint,dill}")); } - Files.write(cachedParametersPath, json.getBytes(StandardCharsets.UTF_8)); } - catch (IOException e) { - FlutterUtils.warn(LOG, e); + else { + Files.createDirectory(buildPath); } + Files.write(cachedParametersPath, json.getBytes(StandardCharsets.UTF_8)); + } + catch (IOException e) { + FlutterUtils.warn(LOG, e); } } diff --git a/src/io/flutter/run/bazel/BazelRunConfig.java b/src/io/flutter/run/bazel/BazelRunConfig.java index 9f5868bd5b..799c4f7c24 100644 --- a/src/io/flutter/run/bazel/BazelRunConfig.java +++ b/src/io/flutter/run/bazel/BazelRunConfig.java @@ -25,6 +25,7 @@ import io.flutter.run.daemon.RunMode; import org.jdom.Element; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class BazelRunConfig extends RunConfigurationBase implements RunConfigurationWithSuppressedDefaultRunAction, LaunchState.RunConfig { @@ -71,7 +72,7 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro final RunMode mode = RunMode.fromEnv(env); final Module module = ModuleUtil.findModuleForFile(workspaceRoot, env.getProject()); - final LaunchState.CreateAppCallback createAppCallback = (device) -> { + final LaunchState.CreateAppCallback createAppCallback = (@Nullable FlutterDevice device) -> { if (device == null) return null; final GeneralCommandLine command = getCommand(env, device); diff --git a/src/io/flutter/run/daemon/DaemonApi.java b/src/io/flutter/run/daemon/DaemonApi.java index b92dc5cba8..eafd90d0cd 100644 --- a/src/io/flutter/run/daemon/DaemonApi.java +++ b/src/io/flutter/run/daemon/DaemonApi.java @@ -313,8 +313,6 @@ private static PrintWriter getStdin(ProcessHandler processHandler) { public static class RestartResult { private int code; private String message; - private String hintMessage; - private String hintId; public boolean ok() { return code == 0; @@ -328,18 +326,6 @@ public String getMessage() { return message; } - public String getHintMessage() { - return hintMessage; - } - - public String getHintId() { - return hintId; - } - - public boolean isRestartRecommended() { - return "restartRecommended".equals(hintId); - } - @Override public String toString() { return getCode() + ":" + getMessage(); diff --git a/src/io/flutter/run/daemon/DaemonEvent.java b/src/io/flutter/run/daemon/DaemonEvent.java index 773b59bd16..6f77755cfc 100644 --- a/src/io/flutter/run/daemon/DaemonEvent.java +++ b/src/io/flutter/run/daemon/DaemonEvent.java @@ -56,10 +56,12 @@ static void dispatch(@NotNull JsonObject obj, @NotNull Listener listener) { static DaemonEvent create(@NotNull String eventName, @NotNull JsonObject params) { try { switch (eventName) { + case "daemon.log": + return GSON.fromJson(params, DaemonLog.class); case "daemon.logMessage": - return GSON.fromJson(params, LogMessage.class); + return GSON.fromJson(params, DaemonLogMessage.class); case "daemon.showMessage": - return GSON.fromJson(params, ShowMessage.class); + return GSON.fromJson(params, DaemonShowMessage.class); case "app.start": return GSON.fromJson(params, AppStarting.class); case "app.debugPort": @@ -108,10 +110,13 @@ default void processTerminated(int exitCode) { // daemon domain - default void onDaemonLogMessage(LogMessage event) { + default void onDaemonLog(DaemonLog event) { } - default void onDaemonShowMessage(ShowMessage event) { + default void onDaemonLogMessage(DaemonLogMessage event) { + } + + default void onDaemonShowMessage(DaemonShowMessage event) { } // app domain @@ -149,7 +154,18 @@ default void onDeviceRemoved(DeviceRemoved event) { // daemon domain @SuppressWarnings("unused") - static class LogMessage extends DaemonEvent { + static class DaemonLog extends DaemonEvent { + // "event":"daemon.log" + String log; + boolean error; + + void accept(Listener listener) { + listener.onDaemonLog(this); + } + } + + @SuppressWarnings("unused") + static class DaemonLogMessage extends DaemonEvent { // "event":"daemon.logMessage" String level; String message; @@ -161,7 +177,7 @@ void accept(Listener listener) { } @SuppressWarnings("unused") - static class ShowMessage extends DaemonEvent { + static class DaemonShowMessage extends DaemonEvent { // "event":"daemon.showMessage" String level; String title; diff --git a/src/io/flutter/run/daemon/DeviceDaemon.java b/src/io/flutter/run/daemon/DeviceDaemon.java index d6722778c8..0e2a25a5fa 100644 --- a/src/io/flutter/run/daemon/DeviceDaemon.java +++ b/src/io/flutter/run/daemon/DeviceDaemon.java @@ -140,10 +140,11 @@ static Command chooseCommand(@NotNull final Project project) { try { final String path = FlutterSdkUtil.pathToFlutterTool(sdk.getHomePath()); - ImmutableList list; + final ImmutableList list; if (FlutterUtils.isIntegrationTestingMode()) { list = ImmutableList.of("--show-test-device", "daemon"); - } else { + } + else { list = ImmutableList.of("daemon"); } return new Command(sdk.getHomePath(), path, list, androidHome); @@ -319,12 +320,12 @@ private static class Listener implements DaemonEvent.Listener { // daemon domain @Override - public void onDaemonLogMessage(@NotNull DaemonEvent.LogMessage message) { + public void onDaemonLogMessage(@NotNull DaemonEvent.DaemonLogMessage message) { LOG.info("flutter device daemon #" + daemonId + ": " + message.message); } @Override - public void onDaemonShowMessage(@NotNull DaemonEvent.ShowMessage event) { + public void onDaemonShowMessage(@NotNull DaemonEvent.DaemonShowMessage event) { if ("error".equals(event.level)) { FlutterMessages.showError(event.title, event.message); } diff --git a/src/io/flutter/run/daemon/FlutterApp.java b/src/io/flutter/run/daemon/FlutterApp.java index 913cbbc918..c90340f55c 100644 --- a/src/io/flutter/run/daemon/FlutterApp.java +++ b/src/io/flutter/run/daemon/FlutterApp.java @@ -68,7 +68,8 @@ public class FlutterApp { private final @NotNull Project myProject; private final @Nullable Module myModule; private final @NotNull RunMode myMode; - // TODO(github.com/flutter/flutter-intellij/issues/3293) myDevice is not-null for all run configurations except flutter web configurations + // TODO(jwren): myDevice is not-null for all run configurations except flutter web configurations + // See https://github.com/flutter/flutter-intellij/issues/3293. private final @Nullable FlutterDevice myDevice; private final @NotNull ProcessHandler myProcessHandler; private final @NotNull ExecutionEnvironment myExecutionEnvironment; @@ -80,6 +81,8 @@ public class FlutterApp { private @Nullable String myBaseUri; private @Nullable ConsoleView myConsole; + private boolean isFlutterWeb = false; + /** * The command with which the app was launched. *

@@ -302,6 +305,19 @@ public ObservatoryConnector getConnector() { return myConnector; } + public void setIsFlutterWeb(boolean value) { + isFlutterWeb = value; + } + + public boolean getIsFlutterWeb() { + return isFlutterWeb; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean appSupportsHotReload() { + return !isFlutterWeb; + } + public State getState() { return myState.get(); } @@ -593,13 +609,9 @@ public FlutterDevice device() { return myDevice; } + @Nullable public String deviceId() { - return myDevice != null ? myDevice.deviceId() : ""; - } - - // TODO this should be a temporary hack until there is some mocked out "browser" device - public boolean isWebDev() { - return myDevice == null; + return myDevice != null ? myDevice.deviceId() : null; } public void setFlutterDebugProcess(FlutterDebugProcess flutterDebugProcess) { @@ -737,7 +749,16 @@ public void processTerminated(int exitCode) { // daemon domain @Override - public void onDaemonLogMessage(@NotNull DaemonEvent.LogMessage message) { + public void onDaemonLog(@NotNull DaemonEvent.DaemonLog message) { + final ConsoleView console = app.getConsole(); + if (console == null) return; + if (message.log != null) { + console.print(message.log + "\n", message.error ? ConsoleViewContentType.ERROR_OUTPUT : ConsoleViewContentType.NORMAL_OUTPUT); + } + } + + @Override + public void onDaemonLogMessage(@NotNull DaemonEvent.DaemonLogMessage message) { LOG.info("flutter app: " + message.message); } diff --git a/src/io/flutter/sdk/FlutterCommand.java b/src/io/flutter/sdk/FlutterCommand.java index 439e54f582..6a3aa12dbd 100644 --- a/src/io/flutter/sdk/FlutterCommand.java +++ b/src/io/flutter/sdk/FlutterCommand.java @@ -37,39 +37,24 @@ public class FlutterCommand { Arrays.asList(Type.PACKAGES_GET, Type.PACKAGES_UPGRADE, Type.UPGRADE)); @NotNull - private final FlutterSdk sdk; + protected final FlutterSdk sdk; @Nullable - private final VirtualFile workDir; + protected final VirtualFile workDir; @NotNull private final Type type; @NotNull - private final List args; - - // TODO(https://github.com/flutter/flutter-intellij/issues/3348) This is a temporary "/bin/webdev" that can be provided to test - // some webdev directly. - @Nullable - private final String localWebDevExe = "webdev"; - - private final boolean isFlutterWeb; + protected final List args; /** * @see FlutterSdk for methods to create specific commands. */ FlutterCommand(@NotNull FlutterSdk sdk, @Nullable VirtualFile workDir, @NotNull Type type, String... args) { - this(sdk, workDir, type, false, args); - } - - /** - * @see FlutterSdk for methods to create specific commands. - */ - FlutterCommand(@NotNull FlutterSdk sdk, @Nullable VirtualFile workDir, @NotNull Type type, boolean isFlutterWeb, String... args) { this.sdk = sdk; this.workDir = workDir; this.type = type; - this.isFlutterWeb = isFlutterWeb; this.args = ImmutableList.copyOf(args); } @@ -260,54 +245,20 @@ public OSProcessHandler startProcessOrShowError(@Nullable Project project) { public GeneralCommandLine createGeneralCommandLine(@Nullable Project project) { final GeneralCommandLine line = new GeneralCommandLine(); line.setCharset(CharsetToolkit.UTF8_CHARSET); - if (isFlutterWeb) { - if (workDir != null) { - line.setWorkDirectory(workDir.getPath()); - } - if (localWebDevExe != null || !localWebDevExe.isEmpty()) { - line.setExePath(localWebDevExe); - line.addParameters("daemon"); - if (args.contains("--debug")) { - line.addParameters("--debug"); - } - if (args.contains("--hot-reload")) { - line.addParameters("--hot-reload"); - } - } - else { - line.setExePath(FileUtil.toSystemDependentName(sdk.getHomePath() + "/bin/" + FlutterSdkUtil.flutterScriptName())); - line.addParameters(args); - } + line.withEnvironment(FlutterSdkUtil.FLUTTER_HOST_ENV, FlutterSdkUtil.getFlutterHostEnvValue()); + final String androidHome = IntelliJAndroidSdk.chooseAndroidHome(project, false); + if (androidHome != null) { + line.withEnvironment("ANDROID_HOME", androidHome); } - else { - line.withEnvironment(FlutterSdkUtil.FLUTTER_HOST_ENV, FlutterSdkUtil.getFlutterHostEnvValue()); - final String androidHome = IntelliJAndroidSdk.chooseAndroidHome(project, false); - if (androidHome != null) { - line.withEnvironment("ANDROID_HOME", androidHome); - } - line.setExePath(FileUtil.toSystemDependentName(sdk.getHomePath() + "/bin/" + FlutterSdkUtil.flutterScriptName())); - if (workDir != null) { - line.setWorkDirectory(workDir.getPath()); - } - if (!isDoctorCommand()) { - line.addParameter("--no-color"); - } - line.addParameters(type.subCommand); - line.addParameters(args); - } - return line; - } - - @NotNull - public GeneralCommandLine createFlutterWebCommandLine(@Nullable Project project) { - final GeneralCommandLine line = new GeneralCommandLine(); - line.setCharset(CharsetToolkit.UTF8_CHARSET); + line.setExePath(FileUtil.toSystemDependentName(sdk.getHomePath() + "/bin/" + FlutterSdkUtil.flutterScriptName())); if (workDir != null) { line.setWorkDirectory(workDir.getPath()); } + if (!isDoctorCommand()) { + line.addParameter("--no-color"); + } line.addParameters(type.subCommand); line.addParameters(args); - return line; } @@ -327,6 +278,7 @@ enum Type { PACKAGES_UPGRADE("Flutter packages upgrade", "packages", "upgrade"), PACKAGES_PUB("Flutter packages pub", "packages", "pub"), RUN("Flutter run", "run"), + FLUTTER_WEB_RUN("Flutter Web run", "flutter_web_run"), UPGRADE("Flutter upgrade", "upgrade"), VERSION("Flutter version", "--version"), TEST("Flutter test", "test"); diff --git a/src/io/flutter/sdk/FlutterSdk.java b/src/io/flutter/sdk/FlutterSdk.java index 5202f2dbed..86d58b5855 100644 --- a/src/io/flutter/sdk/FlutterSdk.java +++ b/src/io/flutter/sdk/FlutterSdk.java @@ -270,16 +270,14 @@ else if (flutterLaunchMode == FlutterLaunchMode.RELEASE) { } public FlutterCommand flutterRunWeb(@NotNull PubRoot root, @NotNull RunMode mode) { - // flutter packages pub global run webdev serve [--debug] [--hot-reload] - final List args = new ArrayList<>(); - args.add("global"); - args.add("run"); - args.add("webdev"); - args.add("daemon"); - // TODO(https://github.com/flutter/flutter-intellij/issues/3349) After debug is supported by webdev, this should be modified to check - // for debug and add any additional needed flags: - // i.e. if (mode == RunMode.DEBUG) { args.add("--debug"); } - return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.PACKAGES_PUB, true, args.toArray(new String[]{ })); + // TODO(devoncarew): We need to provision the webdev cli here. + + // TODO(jwren): After debugging is supported by webdev, this should be modified to check for debug and add + // any additional needed flags: i.e. if (mode == RunMode.DEBUG) { args.add("--debug"); } + // See https://github.com/flutter/flutter-intellij/issues/3349. + + // flutter packages pub global run webdev daemon + return new FlutterWebCommand(this, root.getRoot(), FlutterCommand.Type.FLUTTER_WEB_RUN, new String[]{ }); } public FlutterCommand flutterRunOnTester(@NotNull PubRoot root, @NotNull String mainPath) { diff --git a/src/io/flutter/sdk/FlutterWebCommand.java b/src/io/flutter/sdk/FlutterWebCommand.java new file mode 100644 index 0000000000..b8db7645ac --- /dev/null +++ b/src/io/flutter/sdk/FlutterWebCommand.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019 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.sdk; + +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * This subclasses FlutterCommand specifically to override the command line creation behavior for + * Flutter Web run commands. + */ +public class FlutterWebCommand extends FlutterCommand { + public FlutterWebCommand(@NotNull FlutterSdk sdk, @Nullable VirtualFile workDir, @NotNull FlutterCommand.Type type, String... args) { + super(sdk, workDir, type, args); + } + + @NotNull + public GeneralCommandLine createGeneralCommandLine(@Nullable Project project) { + final GeneralCommandLine line = new GeneralCommandLine(); + line.setCharset(CharsetToolkit.UTF8_CHARSET); + if (workDir != null) { + line.setWorkDirectory(workDir.getPath()); + } + line.setExePath(FileUtil.toSystemDependentName(sdk.getHomePath() + "/bin/" + FlutterSdkUtil.flutterScriptName())); + // flutter packages pub + line.addParameters(Type.PACKAGES_PUB.subCommand); + // TODO(devoncarew): We need to provision webdev locally. + line.addParameters("global", "run", "webdev", "daemon"); + line.addParameters(args); + return line; + } +} diff --git a/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java b/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java index 502da528f6..1d1ca65c06 100644 --- a/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java +++ b/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java @@ -39,7 +39,6 @@ import io.flutter.FlutterBundle; import io.flutter.FlutterUtils; import io.flutter.ObservatoryConnector; -import io.flutter.run.FlutterDebugProcess; import io.flutter.run.FlutterLaunchMode; import io.flutter.server.vmService.frame.DartVmServiceEvaluator; import io.flutter.server.vmService.frame.DartVmServiceStackFrame; @@ -249,25 +248,20 @@ public void scheduleConnect() { return; } - // "Flutter run" has given us a websocket; we can assume it's ready immediately, - // because "flutter run" has already connected to it. + // "flutter run" has given us a websocket; we can assume it's ready immediately, because + // "flutter run" has already connected to it. final VmService vmService; final VmOpenSourceLocationListener vmOpenSourceLocationListener; - // TODO(github.com/dart-lang/webdev/issues/233) The following check disables the WebSocket connection for all FlutterWeb run - // configurations, for some reason the WebSocket port can't be connected to, see listed issue above. - if (this instanceof FlutterDebugProcess && (!((FlutterDebugProcess)this).getApp().isWebDev())) { - try { - vmService = VmService.connect(url); - vmOpenSourceLocationListener = VmOpenSourceLocationListener.connect(url); - } - catch (IOException | RuntimeException e) { - onConnectFailed("Failed to connect to the VM observatory service at: " + url + "\n" - + e.toString() + "\n" + - formatStackTraces(e)); - return; - } - onConnectSucceeded(vmService, vmOpenSourceLocationListener); + try { + vmService = VmService.connect(url); + vmOpenSourceLocationListener = VmOpenSourceLocationListener.connect(url); + } + catch (IOException | RuntimeException e) { + onConnectFailed("Failed to connect to the VM observatory service at: " + url + "\n" + + e.toString() + "\n" + formatStackTraces(e)); + return; } + onConnectSucceeded(vmService, vmOpenSourceLocationListener); }); } diff --git a/src/io/flutter/view/FlutterPerfView.java b/src/io/flutter/view/FlutterPerfView.java index 89b0caf520..0bab9dcd7c 100644 --- a/src/io/flutter/view/FlutterPerfView.java +++ b/src/io/flutter/view/FlutterPerfView.java @@ -98,7 +98,6 @@ private void updateToolWindowVisibility(ToolWindow toolWindow) { } void debugActive(@NotNull FlutterViewMessages.FlutterDebugEvent event) { - final FlutterApp app = event.app; final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject); if (!(toolWindowManager instanceof ToolWindowManagerEx)) { diff --git a/src/io/flutter/view/FlutterView.java b/src/io/flutter/view/FlutterView.java index c874310ef7..4dddc72e9b 100644 --- a/src/io/flutter/view/FlutterView.java +++ b/src/io/flutter/view/FlutterView.java @@ -64,8 +64,6 @@ import javax.swing.*; import java.awt.*; -import java.net.MalformedURLException; -import java.net.URL; import java.util.List; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -386,17 +384,26 @@ private void showFlutterPerformanceWindow(FlutterApp app) { public void debugActive(@NotNull FlutterViewMessages.FlutterDebugEvent event) { final FlutterApp app = event.app; + System.out.println("FlutterView.debugActive()"); + if (app.getMode().isProfiling() || app.getLaunchMode().isProfiling()) { ApplicationManager.getApplication().invokeLater(() -> debugActiveHelper(app, null)); } else { + System.out.println("FlutterView.whenCompleteUiThread()"); + whenCompleteUiThread( InspectorService.create(app, app.getFlutterDebugProcess(), app.getVmService()), (InspectorService inspectorService, Throwable throwable) -> { + System.out.println("FlutterView InspectorService.create()"); + if (throwable != null) { FlutterUtils.warn(LOG, throwable); return; } + + System.out.println("FlutterView debugActiveHelper()"); + debugActiveHelper(app, inspectorService); }); } @@ -613,24 +620,14 @@ public void perform(AnActionEvent event) { return; } - final URL url; - try { - url = new URL(urlString); - } - catch (MalformedURLException e) { - return; - } - - final int port = url.getPort(); - final DevToolsManager devToolsManager = DevToolsManager.getInstance(app.getProject()); if (devToolsManager.hasInstalledDevTools()) { - devToolsManager.openBrowserAndConnect(port); + devToolsManager.openBrowserAndConnect(urlString); } else { final CompletableFuture result = devToolsManager.installDevTools(); - result.thenAccept(o -> devToolsManager.openBrowserAndConnect(port)); + result.thenAccept(o -> devToolsManager.openBrowserAndConnect(urlString)); } } } diff --git a/testSrc/unit/io/flutter/run/daemon/DaemonEventTest.java b/testSrc/unit/io/flutter/run/daemon/DaemonEventTest.java index 6766523d2e..50a3e870cc 100644 --- a/testSrc/unit/io/flutter/run/daemon/DaemonEventTest.java +++ b/testSrc/unit/io/flutter/run/daemon/DaemonEventTest.java @@ -33,12 +33,12 @@ public void setUp() { // daemon domain @Override - public void onDaemonLogMessage(DaemonEvent.LogMessage event) { + public void onDaemonLogMessage(DaemonEvent.DaemonLogMessage event) { logEvent(event, event.level, event.message, event.stackTrace); } @Override - public void onDaemonShowMessage(DaemonEvent.ShowMessage event) { + public void onDaemonShowMessage(DaemonEvent.DaemonShowMessage event) { logEvent(event, event.level, event.title, event.message); } diff --git a/third_party/vmServiceDrivers/org/dartlang/vm/service/VmServiceBase.java b/third_party/vmServiceDrivers/org/dartlang/vm/service/VmServiceBase.java index 14846538a2..108064949f 100644 --- a/third_party/vmServiceDrivers/org/dartlang/vm/service/VmServiceBase.java +++ b/third_party/vmServiceDrivers/org/dartlang/vm/service/VmServiceBase.java @@ -55,7 +55,8 @@ public static VmService connect(final String url) throws IOException { URI uri; try { uri = new URI(url); - } catch (URISyntaxException e) { + } + catch (URISyntaxException e) { throw new IOException("Invalid URL: " + url, e); } String wsScheme = uri.getScheme(); @@ -67,7 +68,8 @@ public static VmService connect(final String url) throws IOException { WebSocket webSocket; try { webSocket = new WebSocket(uri); - } catch (WebSocketException e) { + } + catch (WebSocketException e) { throw new IOException("Failed to create websocket: " + url, e); } final VmService vmService = new VmService(); @@ -86,13 +88,16 @@ public void onMessage(WebSocketMessage message) { Logging.getLogger().logInformation("VM message: " + message.getText()); try { vmService.processMessage(message.getText()); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError(e.getMessage(), e); } } @Override public void onOpen() { + System.out.println("VmServiceBase.onOpen()"); + vmService.connectionOpened(); Logging.getLogger().logInformation("VM connection open: " + url); @@ -111,9 +116,11 @@ public void onPong() { //noinspection TryWithIdenticalCatches try { webSocket.connect(); - } catch (WebSocketException e) { + } + catch (WebSocketException e) { throw new IOException("Failed to connect: " + url, e); - } catch (ArrayIndexOutOfBoundsException e) { + } + catch (ArrayIndexOutOfBoundsException e) { // The weberknecht can occasionally throw an array index exception if a connect terminates on initial connect // (de.roderick.weberknecht.WebSocket.connect, WebSocket.java:126). throw new IOException("Failed to connect: " + url, e); @@ -127,7 +134,7 @@ public void onPong() { @Override public void onError(RPCError error) { String msg = "Failed to determine protocol version: " + error.getCode() + "\n message: " - + error.getMessage() + "\n details: " + error.getDetails(); + + error.getMessage() + "\n details: " + error.getDetails(); Logging.getLogger().logInformation(msg); errMsg[0] = msg; } @@ -139,11 +146,12 @@ public void received(Version response) { if (major != VmService.versionMajor || minor != VmService.versionMinor) { if (major == 2 || major == 3) { Logging.getLogger().logInformation( - "Difference in protocol version: client=" + VmService.versionMajor + "." - + VmService.versionMinor + " vm=" + major + "." + minor); - } else { + "Difference in protocol version: client=" + VmService.versionMajor + "." + + VmService.versionMinor + " vm=" + major + "." + minor); + } + else { String msg = "Incompatible protocol version: client=" + VmService.versionMajor + "." - + VmService.versionMinor + " vm=" + major + "." + minor; + + VmService.versionMinor + " vm=" + major + "." + minor; Logging.getLogger().logError(msg); errMsg[0] = msg; } @@ -158,7 +166,8 @@ public void received(Version response) { if (errMsg[0] != null) { throw new IOException(errMsg[0]); } - } catch (InterruptedException e) { + } + catch (InterruptedException e) { throw new RuntimeException("Interrupted while waiting for response", e); } @@ -254,8 +263,9 @@ public void onError(RPCError error) { @Override public void received(Obj response) { if (response instanceof Instance) { - consumer.received((Instance) response); - } else { + consumer.received((Instance)response); + } + else { onError(RPCError.unexpected("Instance", response)); } } @@ -281,8 +291,9 @@ public void onError(RPCError error) { @Override public void received(Obj response) { if (response instanceof Library) { - consumer.received((Library) response); - } else { + consumer.received((Library)response); + } + else { onError(RPCError.unexpected("Library", response)); } } @@ -341,10 +352,13 @@ protected void request(String method, JsonObject params, Consumer consumer) { } public void connectionOpened() { + System.out.println("VmServiceBase.connectionOpened()"); + for (VmServiceListener listener : vmListeners) { try { listener.connectionOpened(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Exception notifying listener", e); } } @@ -354,7 +368,8 @@ private void forwardEvent(String streamId, Event event) { for (VmServiceListener listener : vmListeners) { try { listener.received(streamId, event); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Exception processing event: " + streamId + ", " + event.getJson(), e); } } @@ -364,7 +379,8 @@ public void connectionClosed() { for (VmServiceListener listener : vmListeners) { try { listener.connectionClosed(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Exception notifying listener", e); } } @@ -395,8 +411,9 @@ void processMessage(String jsonText) { // Decode the JSON JsonObject json; try { - json = (JsonObject) new JsonParser().parse(jsonText); - } catch (Exception e) { + json = (JsonObject)new JsonParser().parse(jsonText); + } + catch (Exception e) { Logging.getLogger().logError("Parse message failed: " + jsonText, e); return; } @@ -416,12 +433,15 @@ void processMessage(String jsonText) { } if (json.has("id")) { processRequest(json); - } else { + } + else { processNotification(json); } - } else if (json.has("result") || json.has("error")) { + } + else if (json.has("result") || json.has("error")) { processResponse(json); - } else { + } + else { Logging.getLogger().logError("Malformed message"); } } @@ -434,7 +454,8 @@ void processRequest(JsonObject json) { String id; try { id = json.get(ID).getAsString(); - } catch (Exception e) { + } + catch (Exception e) { final String message = "Request malformed " + ID; Logging.getLogger().logError(message, e); final JsonObject error = new JsonObject(); @@ -450,7 +471,8 @@ void processRequest(JsonObject json) { String method; try { method = json.get(METHOD).getAsString(); - } catch (Exception e) { + } + catch (Exception e) { final String message = "Request malformed " + METHOD; Logging.getLogger().logError(message, e); final JsonObject error = new JsonObject(); @@ -464,7 +486,8 @@ void processRequest(JsonObject json) { JsonObject params; try { params = json.get(PARAMS).getAsJsonObject(); - } catch (Exception e) { + } + catch (Exception e) { final String message = "Request malformed " + METHOD; Logging.getLogger().logError(message, e); final JsonObject error = new JsonObject(); @@ -505,7 +528,8 @@ public void error(int code, String message, JsonObject data) { requestSink.add(response); } }); - } catch (Exception e) { + } + catch (Exception e) { final String message = "Internal Server Error"; Logging.getLogger().logError(message, e); final JsonObject error = new JsonObject(); @@ -517,28 +541,30 @@ public void error(int code, String message, JsonObject data) { } private static final RemoteServiceCompleter ignoreCallback = - new RemoteServiceCompleter() { - public void result(JsonObject result) { - // ignore - } + new RemoteServiceCompleter() { + public void result(JsonObject result) { + // ignore + } - public void error(int code, String message, JsonObject data) { - // ignore - } - }; + public void error(int code, String message, JsonObject data) { + // ignore + } + }; void processNotification(JsonObject json) { String method; try { method = json.get(METHOD).getAsString(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Request malformed " + METHOD, e); return; } JsonObject params; try { params = json.get(PARAMS).getAsJsonObject(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Event missing " + PARAMS, e); return; } @@ -546,19 +572,22 @@ void processNotification(JsonObject json) { String streamId; try { streamId = params.get(STREAM_ID).getAsString(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Event missing " + STREAM_ID, e); return; } Event event; try { event = new Event(params.get(EVENT).getAsJsonObject()); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Event missing " + EVENT, e); return; } forwardEvent(streamId, event); - } else { + } + else { if (!remoteServiceRunners.containsKey(method)) { Logging.getLogger().logError("Unknown service " + method); return; @@ -567,7 +596,8 @@ void processNotification(JsonObject json) { final RemoteServiceRunner runner = remoteServiceRunners.get(method); try { runner.run(params, ignoreCallback); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Internal Server Error", e); } } @@ -584,7 +614,8 @@ void processResponse(JsonObject json) { String id; try { id = idElem.getAsString(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Response missing " + ID, e); return; } @@ -600,14 +631,16 @@ void processResponse(JsonObject json) { JsonObject result; try { result = resultElem.getAsJsonObject(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Response has invalid " + RESULT, e); return; } String responseType; try { responseType = result.get(TYPE).getAsString(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Response missing " + TYPE, e); return; } @@ -621,7 +654,8 @@ void processResponse(JsonObject json) { JsonObject error; try { error = resultElem.getAsJsonObject(); - } catch (Exception e) { + } + catch (Exception e) { Logging.getLogger().logError("Response has invalid " + RESULT, e); return; } From 0d74da0c574c019a55772711d1395b86cd87f9b3 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 19 Apr 2019 16:59:15 -0700 Subject: [PATCH 2/4] remove printlns --- src/io/flutter/view/FlutterView.java | 8 -------- .../org/dartlang/vm/service/VmServiceBase.java | 4 ---- 2 files changed, 12 deletions(-) diff --git a/src/io/flutter/view/FlutterView.java b/src/io/flutter/view/FlutterView.java index 4dddc72e9b..8e1762c0d7 100644 --- a/src/io/flutter/view/FlutterView.java +++ b/src/io/flutter/view/FlutterView.java @@ -384,26 +384,18 @@ private void showFlutterPerformanceWindow(FlutterApp app) { public void debugActive(@NotNull FlutterViewMessages.FlutterDebugEvent event) { final FlutterApp app = event.app; - System.out.println("FlutterView.debugActive()"); - if (app.getMode().isProfiling() || app.getLaunchMode().isProfiling()) { ApplicationManager.getApplication().invokeLater(() -> debugActiveHelper(app, null)); } else { - System.out.println("FlutterView.whenCompleteUiThread()"); - whenCompleteUiThread( InspectorService.create(app, app.getFlutterDebugProcess(), app.getVmService()), (InspectorService inspectorService, Throwable throwable) -> { - System.out.println("FlutterView InspectorService.create()"); - if (throwable != null) { FlutterUtils.warn(LOG, throwable); return; } - System.out.println("FlutterView debugActiveHelper()"); - debugActiveHelper(app, inspectorService); }); } diff --git a/third_party/vmServiceDrivers/org/dartlang/vm/service/VmServiceBase.java b/third_party/vmServiceDrivers/org/dartlang/vm/service/VmServiceBase.java index 108064949f..746beee3c0 100644 --- a/third_party/vmServiceDrivers/org/dartlang/vm/service/VmServiceBase.java +++ b/third_party/vmServiceDrivers/org/dartlang/vm/service/VmServiceBase.java @@ -96,8 +96,6 @@ public void onMessage(WebSocketMessage message) { @Override public void onOpen() { - System.out.println("VmServiceBase.onOpen()"); - vmService.connectionOpened(); Logging.getLogger().logInformation("VM connection open: " + url); @@ -352,8 +350,6 @@ protected void request(String method, JsonObject params, Consumer consumer) { } public void connectionOpened() { - System.out.println("VmServiceBase.connectionOpened()"); - for (VmServiceListener listener : vmListeners) { try { listener.connectionOpened(); From a9be66e61048c000733319334ad4fdb922020f9d Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sun, 21 Apr 2019 19:01:47 -0700 Subject: [PATCH 3/4] fix inspector npes --- src/io/flutter/devtools/DevToolsManager.java | 12 ++++- src/io/flutter/inspector/DiagnosticsNode.java | 53 +++++++++++-------- .../flutter/inspector/InspectorService.java | 2 +- src/io/flutter/run/daemon/FlutterApp.java | 3 +- src/io/flutter/view/FlutterPerfView.java | 16 ++++-- src/io/flutter/view/FlutterView.java | 20 +++++-- 6 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/io/flutter/devtools/DevToolsManager.java b/src/io/flutter/devtools/DevToolsManager.java index 85d14a6e94..6248a94cc1 100644 --- a/src/io/flutter/devtools/DevToolsManager.java +++ b/src/io/flutter/devtools/DevToolsManager.java @@ -26,6 +26,8 @@ import io.flutter.utils.JsonUtils; import org.jetbrains.annotations.NotNull; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -214,9 +216,15 @@ public void openBrowserAndConnect(String serviceProtocolUri) { BrowserLauncher.getInstance().browse("http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&", null); } else { - // TODO: do we need to url encode uri? + String urlParam = serviceProtocolUri; + try { + urlParam = URLEncoder.encode(serviceProtocolUri, "UTF-8"); + } + catch (UnsupportedEncodingException ignored) { + } + BrowserLauncher.getInstance().browse( - "http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&uri=" + serviceProtocolUri, + "http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&uri=" + urlParam, null ); } diff --git a/src/io/flutter/inspector/DiagnosticsNode.java b/src/io/flutter/inspector/DiagnosticsNode.java index 3cd4454ba8..4ae7215753 100644 --- a/src/io/flutter/inspector/DiagnosticsNode.java +++ b/src/io/flutter/inspector/DiagnosticsNode.java @@ -9,13 +9,14 @@ import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonObject; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.xdebugger.XSourcePosition; import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService; -import io.flutter.server.vmService.frame.DartVmServiceValue; import io.flutter.run.daemon.FlutterApp; +import io.flutter.server.vmService.frame.DartVmServiceValue; import io.flutter.utils.CustomIconMaker; import io.flutter.utils.JsonUtils; import org.apache.commons.lang.StringUtils; @@ -49,6 +50,8 @@ * also available via the getValue() method. */ public class DiagnosticsNode { + private static final Logger LOG = Logger.getInstance(DiagnosticsNode.class); + private static final CustomIconMaker iconMaker = new CustomIconMaker(); private final FlutterApp app; @@ -104,7 +107,8 @@ public boolean isDisposed() { final InspectorService.ObjectGroup service = inspectorService.getNow(null); // If the service isn't created yet it can't have been disposed. return service != null && service.isDisposed(); - } catch (Exception e) { + } + catch (Exception e) { // If the service can't be acquired then it is disposed. return false; } @@ -638,12 +642,13 @@ public CompletableFuture> getChildren() { final JsonArray jsonArray = json.get("children").getAsJsonArray(); final ArrayList nodes = new ArrayList<>(); for (JsonElement element : jsonArray) { - DiagnosticsNode child = new DiagnosticsNode(element.getAsJsonObject(), inspectorService, app,false, parent); + DiagnosticsNode child = new DiagnosticsNode(element.getAsJsonObject(), inspectorService, app, false, parent); child.setParent(this); nodes.add(child); } children = CompletableFuture.completedFuture(nodes); - } else if (hasChildren()) { + } + else if (hasChildren()) { children = inspectorService.thenComposeAsync((service) -> { if (service == null) { return null; @@ -720,12 +725,12 @@ ArrayList trackPropertiesMatchingParameters(ArrayList getPropertyDoc() { if (propertyDocFuture == null) { - propertyDocFuture = createPropertyDocFurure(); + propertyDocFuture = createPropertyDocFuture(); } return propertyDocFuture; } - private CompletableFuture createPropertyDocFurure() { + private CompletableFuture createPropertyDocFuture() { final DiagnosticsNode parent = getParent(); if (parent != null) { return inspectorService.thenComposeAsync((service) -> service.toDartVmServiceValueForSourceLocation(parent.getValueRef()) @@ -734,23 +739,26 @@ private CompletableFuture createPropertyDocFurure() { return CompletableFuture.completedFuture(null); } return inspectorService.getNow(null).getPropertyLocation(vmValue.getInstanceRef(), getName()) - .thenApplyAsync((XSourcePosition sourcePosition) -> { - if (sourcePosition != null) { - final VirtualFile file = sourcePosition.getFile(); - final int offset = sourcePosition.getOffset(); - - final Project project = getProject(file); - if (project != null) { - final List hovers = - DartAnalysisServerService.getInstance(project).analysis_getHover(file, offset); - if (!hovers.isEmpty()) { - return hovers.get(0).getDartdoc(); + .thenApplyAsync((XSourcePosition sourcePosition) -> { + if (sourcePosition != null) { + final VirtualFile file = sourcePosition.getFile(); + final int offset = sourcePosition.getOffset(); + + final Project project = getProject(file); + if (project != null) { + final List hovers = + DartAnalysisServerService.getInstance(project).analysis_getHover(file, offset); + if (!hovers.isEmpty()) { + return hovers.get(0).getDartdoc(); + } } } - } - return "Unable to find property source"; - }); - })); + return "Unable to find property source"; + }); + })).exceptionally(t -> { + LOG.info("ignoring exception from toObjectForSourceLocation: " + t.toString()); + return null; + }); } return CompletableFuture.completedFuture("Unable to find property source"); @@ -834,7 +842,8 @@ public void safeWhenComplete(CompletableFuture future, BiConsumer create(@NotNull FlutterApp app @NotNull FlutterDebugProcess debugProcess, @NotNull VmService vmService) { assert app.getVMServiceManager() != null; - Set inspectorLibraryNames = new HashSet<>(); + final Set inspectorLibraryNames = new HashSet<>(); inspectorLibraryNames.add("package:flutter/src/widgets/widget_inspector.dart"); inspectorLibraryNames.add("package:flutter_web/src/widgets/widget_inspector.dart"); final EvalOnDartLibrary inspectorLibrary = new EvalOnDartLibrary( diff --git a/src/io/flutter/run/daemon/FlutterApp.java b/src/io/flutter/run/daemon/FlutterApp.java index c90340f55c..0399c67af8 100644 --- a/src/io/flutter/run/daemon/FlutterApp.java +++ b/src/io/flutter/run/daemon/FlutterApp.java @@ -605,6 +605,7 @@ public boolean isSessionActive() { !debugProcess.getSession().isStopped(); } + @Nullable public FlutterDevice device() { return myDevice; } @@ -754,7 +755,7 @@ public void onDaemonLog(@NotNull DaemonEvent.DaemonLog message) { if (console == null) return; if (message.log != null) { console.print(message.log + "\n", message.error ? ConsoleViewContentType.ERROR_OUTPUT : ConsoleViewContentType.NORMAL_OUTPUT); - } + } } @Override diff --git a/src/io/flutter/view/FlutterPerfView.java b/src/io/flutter/view/FlutterPerfView.java index 0bab9dcd7c..ac0a32a23e 100644 --- a/src/io/flutter/view/FlutterPerfView.java +++ b/src/io/flutter/view/FlutterPerfView.java @@ -163,13 +163,21 @@ private void addPerformanceViewContent(FlutterApp app, ToolWindow toolWindow) { final JBRunnerTabs runnerTabs = new JBRunnerTabs(myProject, ActionManager.getInstance(), null, this); runnerTabs.setSelectionChangeHandler(this::onTabSelectionChange); - final List existingDevices = new ArrayList<>(); - for (FlutterApp otherApp : perAppViewState.keySet()) { - existingDevices.add(otherApp.device()); + final String tabName; + final FlutterDevice device = app.device(); + if (device == null) { + tabName = app.getProject().getName(); + } + else { + final List existingDevices = new ArrayList<>(); + for (FlutterApp otherApp : perAppViewState.keySet()) { + existingDevices.add(otherApp.device()); + } + tabName = device.getUniqueName(existingDevices); } final JPanel tabContainer = new JPanel(new BorderLayout()); - final Content content = contentManager.getFactory().createContent(null, app.device().getUniqueName(existingDevices), false); + final Content content = contentManager.getFactory().createContent(null, tabName, false); tabContainer.add(runnerTabs.getComponent(), BorderLayout.CENTER); content.setComponent(tabContainer); content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE); diff --git a/src/io/flutter/view/FlutterView.java b/src/io/flutter/view/FlutterView.java index 8e1762c0d7..a4e471d77f 100644 --- a/src/io/flutter/view/FlutterView.java +++ b/src/io/flutter/view/FlutterView.java @@ -216,12 +216,22 @@ private void addInspectorViewContent(FlutterApp app, @Nullable InspectorService final SimpleToolWindowPanel toolWindowPanel = new SimpleToolWindowPanel(true); final JBRunnerTabs runnerTabs = new JBRunnerTabs(myProject, ActionManager.getInstance(), null, this); runnerTabs.setSelectionChangeHandler(this::onTabSelectionChange); - final List existingDevices = new ArrayList<>(); - for (FlutterApp otherApp : perAppViewState.keySet()) { - existingDevices.add(otherApp.device()); - } final JPanel tabContainer = new JPanel(new BorderLayout()); - final Content content = contentManager.getFactory().createContent(null, app.device().getUniqueName(existingDevices), false); + + final String tabName; + final FlutterDevice device = app.device(); + if (device == null) { + tabName = app.getProject().getName(); + } + else { + final List existingDevices = new ArrayList<>(); + for (FlutterApp otherApp : perAppViewState.keySet()) { + existingDevices.add(otherApp.device()); + } + tabName = device.getUniqueName(existingDevices); + } + + final Content content = contentManager.getFactory().createContent(null, tabName, false); tabContainer.add(runnerTabs.getComponent(), BorderLayout.CENTER); content.setComponent(tabContainer); content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE); From 69be096d9aa759ec7dba3aebf92b3e3e9bc61447 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 22 Apr 2019 15:27:59 -0700 Subject: [PATCH 4/4] review comments --- src/io/flutter/devtools/DevToolsManager.java | 12 +++++------- src/io/flutter/inspector/DiagnosticsNode.java | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/io/flutter/devtools/DevToolsManager.java b/src/io/flutter/devtools/DevToolsManager.java index 6248a94cc1..e58864d20d 100644 --- a/src/io/flutter/devtools/DevToolsManager.java +++ b/src/io/flutter/devtools/DevToolsManager.java @@ -216,17 +216,15 @@ public void openBrowserAndConnect(String serviceProtocolUri) { BrowserLauncher.getInstance().browse("http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&", null); } else { - String urlParam = serviceProtocolUri; try { - urlParam = URLEncoder.encode(serviceProtocolUri, "UTF-8"); + final String urlParam = URLEncoder.encode(serviceProtocolUri, "UTF-8"); + BrowserLauncher.getInstance().browse( + "http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&uri=" + urlParam, + null + ); } catch (UnsupportedEncodingException ignored) { } - - BrowserLauncher.getInstance().browse( - "http://" + devtoolsHost + ":" + devtoolsPort + "/?hide=debugger&uri=" + urlParam, - null - ); } } } diff --git a/src/io/flutter/inspector/DiagnosticsNode.java b/src/io/flutter/inspector/DiagnosticsNode.java index 4ae7215753..c202d1135c 100644 --- a/src/io/flutter/inspector/DiagnosticsNode.java +++ b/src/io/flutter/inspector/DiagnosticsNode.java @@ -843,7 +843,7 @@ public void safeWhenComplete(CompletableFuture future, BiConsumer