diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
index 831984cac9..2f1a9602a7 100644
--- a/resources/META-INF/plugin.xml
+++ b/resources/META-INF/plugin.xml
@@ -927,6 +927,9 @@
+
diff --git a/resources/META-INF/plugin_template.xml b/resources/META-INF/plugin_template.xml
index 7000ec86e1..3c56175113 100644
--- a/resources/META-INF/plugin_template.xml
+++ b/resources/META-INF/plugin_template.xml
@@ -290,6 +290,9 @@
+
diff --git a/src/io/flutter/FlutterInitializer.java b/src/io/flutter/FlutterInitializer.java
index 2cdcccb1ac..0b9f6f65bd 100644
--- a/src/io/flutter/FlutterInitializer.java
+++ b/src/io/flutter/FlutterInitializer.java
@@ -23,6 +23,7 @@
import io.flutter.analytics.Analytics;
import io.flutter.analytics.ToolWindowTracker;
import io.flutter.android.IntelliJAndroidSdk;
+import io.flutter.devtools.WebDevManager;
import io.flutter.editor.FlutterSaveActionsManager;
import io.flutter.perf.FlutterWidgetPerfManager;
import io.flutter.pub.PubRoot;
@@ -32,6 +33,8 @@
import io.flutter.run.daemon.DeviceService;
import io.flutter.samples.FlutterSampleManager;
import io.flutter.sdk.FlutterPluginsLibraryManager;
+import io.flutter.sdk.FlutterSdk;
+import io.flutter.sdk.FlutterSdkManager;
import io.flutter.settings.FlutterSettings;
import io.flutter.utils.FlutterModuleUtils;
import io.flutter.view.FlutterPerfViewFactory;
@@ -82,14 +85,19 @@ public void runActivity(@NotNull Project project) {
// If the project declares a Flutter dependency, do some extra initialization.
boolean hasFlutterModule = false;
+ boolean hasFlutterWebModule = false;
for (Module module : ModuleManager.getInstance(project).getModules()) {
- if (!FlutterModuleUtils.declaresFlutter(module)) {
+ final boolean declaresFlutter = FlutterModuleUtils.declaresFlutter(module);
+ final boolean declaresFlutterWeb = FlutterModuleUtils.declaresFlutterWeb(module);
+
+ hasFlutterModule = hasFlutterModule || declaresFlutter;
+ hasFlutterWebModule = hasFlutterWebModule || declaresFlutterWeb;
+
+ if (!declaresFlutter && !declaresFlutterWeb) {
continue;
}
- hasFlutterModule = true;
-
// Ensure SDKs are configured; needed for clean module import.
FlutterModuleUtils.enableDartSDK(module);
@@ -119,6 +127,11 @@ public void runActivity(@NotNull Project project) {
performAndroidStudioCanaryCheck();
}
+ // Check if the project is a flutter web project; if so, install webdev.
+ if (hasFlutterWebModule) {
+ installWebDev(project);
+ }
+
FlutterRunNotifications.init(project);
// Start the widget perf manager.
@@ -188,6 +201,38 @@ public void actionPerformed(@NotNull AnActionEvent event) {
}
}
+ private void installWebDev(@NotNull Project project) {
+ final FlutterSdk flutterSdk = FlutterSdk.getFlutterSdk(project);
+
+ final WebDevManager webDevManager = WebDevManager.getInstance(project);
+
+ if (flutterSdk != null) {
+ if (!webDevManager.hasInstalledWebDev()) {
+ webDevManager.installWebdev();
+ }
+ }
+ else {
+ // Listen for sdk changes; on a valid flutter sdk, attempt to install webdev.
+ FlutterSdkManager.getInstance(project).addListener(new FlutterSdkManager.Listener() {
+ boolean installAttempted = false;
+
+ @Override
+ public void flutterSdkAdded() {
+ final FlutterSdk flutterSdk = FlutterSdk.getFlutterSdk(project);
+ if (flutterSdk == null) {
+ return;
+ }
+
+ if (!installAttempted && !webDevManager.hasInstalledWebDev()) {
+ installAttempted = true;
+
+ webDevManager.installWebdev();
+ }
+ }
+ });
+ }
+ }
+
/**
* Automatically set Android SDK based on ANDROID_HOME.
*/
diff --git a/src/io/flutter/devtools/DevToolsManager.java b/src/io/flutter/devtools/DevToolsManager.java
index e58864d20d..ca5525635b 100644
--- a/src/io/flutter/devtools/DevToolsManager.java
+++ b/src/io/flutter/devtools/DevToolsManager.java
@@ -12,6 +12,7 @@
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
+import com.intellij.execution.process.ProcessOutput;
import com.intellij.ide.browsers.BrowserLauncher;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -19,6 +20,7 @@
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
+import io.flutter.console.FlutterConsoles;
import io.flutter.pub.PubRoot;
import io.flutter.pub.PubRoots;
import io.flutter.sdk.FlutterCommand;
@@ -69,34 +71,40 @@ public CompletableFuture installDevTools() {
final ProgressManager progressManager = ProgressManager.getInstance();
progressManager.run(new Task.Backgroundable(project, "Installing DevTools...", true) {
- OSProcessHandler processHandler;
+ Process process;
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setText(getTitle());
+ indicator.setIndeterminate(true);
- processHandler = command.startInConsole(project);
+ process = command.start((ProcessOutput output) -> {
+ if (output.getExitCode() != 0) {
+ final String message = (output.getStdout() + "\n" + output.getStderr()).trim();
+ FlutterConsoles.displayMessage(project, null, message, true);
+ }
+ }, null);
try {
- final boolean value = processHandler.waitFor();
- if (value) {
+ final int resultCode = process.waitFor();
+ if (resultCode == 0) {
installedDevTools = true;
}
- result.complete(value);
+ result.complete(resultCode == 0);
}
- catch (RuntimeException re) {
+ catch (RuntimeException | InterruptedException re) {
if (!result.isDone()) {
result.complete(false);
}
}
- processHandler = null;
+ process = null;
}
@Override
public void onCancel() {
- if (processHandler != null && !processHandler.isProcessTerminated()) {
- processHandler.destroyProcess();
+ if (process != null && process.isAlive()) {
+ process.destroy();
if (!result.isDone()) {
result.complete(false);
}
@@ -158,6 +166,9 @@ public static void startServer(
Callback onClose
) {
final FlutterCommand command = sdk.flutterPackagesPub(pubRoot, "global", "run", "devtools", "--machine", "--port=0");
+
+ // TODO(devoncarew): Refactor this so that we don't use the console to display output - this steals
+ // focus away from the Run (or Debug) view.
final OSProcessHandler processHandler = command.startInConsole(project);
if (processHandler == null) {
@@ -170,7 +181,7 @@ public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType
final String text = event.getText().trim();
if (text.startsWith("{") && text.endsWith("}")) {
- // {"method":"server.started","params":{"host":"127.0.0.1","port":9100}}
+ // {"event":"server.started","params":{"host":"127.0.0.1","port":9100}}
try {
final JsonParser jsonParser = new JsonParser();
diff --git a/src/io/flutter/devtools/WebDevManager.java b/src/io/flutter/devtools/WebDevManager.java
new file mode 100644
index 0000000000..c337f1d2b4
--- /dev/null
+++ b/src/io/flutter/devtools/WebDevManager.java
@@ -0,0 +1,110 @@
+/*
+ * 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.devtools;
+
+import com.intellij.execution.process.ProcessOutput;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import io.flutter.console.FlutterConsoles;
+import io.flutter.pub.PubRoot;
+import io.flutter.pub.PubRoots;
+import io.flutter.sdk.FlutterCommand;
+import io.flutter.sdk.FlutterSdk;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Manage installing the webdev cli.
+ */
+public class WebDevManager {
+ public static WebDevManager getInstance(@NotNull Project project) {
+ return ServiceManager.getService(project, WebDevManager.class);
+ }
+
+ @NotNull private final Project project;
+
+ private boolean installedWebdev = false;
+
+ private WebDevManager(@NotNull Project project) {
+ this.project = project;
+ }
+
+ public boolean hasInstalledWebDev() {
+ return installedWebdev;
+ }
+
+ public CompletableFuture installWebdev() {
+ final FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
+ if (sdk == null) {
+ return createCompletedFuture(false);
+ }
+
+ final List pubRoots = PubRoots.forProject(project);
+ if (pubRoots.isEmpty()) {
+ return createCompletedFuture(false);
+ }
+
+ final CompletableFuture result = new CompletableFuture<>();
+ final FlutterCommand command = sdk.flutterPackagesPub(pubRoots.get(0), "global", "activate", "webdev");
+
+ final ProgressManager progressManager = ProgressManager.getInstance();
+ //noinspection DialogTitleCapitalization
+ progressManager.run(new Task.Backgroundable(project, "Installing webdev...", true) {
+ Process process;
+
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ indicator.setText(getTitle());
+ indicator.setIndeterminate(true);
+
+ process = command.start((ProcessOutput output) -> {
+ if (output.getExitCode() != 0) {
+ final String message = (output.getStdout() + "\n" + output.getStderr()).trim();
+ FlutterConsoles.displayMessage(project, null, message, true);
+ }
+ }, null);
+
+ try {
+ final int resultCode = process.waitFor();
+ if (resultCode == 0) {
+ installedWebdev = true;
+ }
+ result.complete(resultCode == 0);
+ }
+ catch (RuntimeException | InterruptedException re) {
+ if (!result.isDone()) {
+ result.complete(false);
+ }
+ }
+
+ process = null;
+ }
+
+ @Override
+ public void onCancel() {
+ if (process != null && process.isAlive()) {
+ process.destroy();
+ if (!result.isDone()) {
+ result.complete(false);
+ }
+ }
+ }
+ });
+
+ return result;
+ }
+
+ private CompletableFuture createCompletedFuture(boolean value) {
+ final CompletableFuture result = new CompletableFuture<>();
+ result.complete(value);
+ return result;
+ }
+}
diff --git a/src/io/flutter/pub/PubRoot.java b/src/io/flutter/pub/PubRoot.java
index 227b499dcd..3f269b5a14 100644
--- a/src/io/flutter/pub/PubRoot.java
+++ b/src/io/flutter/pub/PubRoot.java
@@ -241,6 +241,13 @@ public boolean declaresFlutter() {
return FlutterUtils.declaresFlutter(pubspec);
}
+ /**
+ * Returns true if the pubspec declares a flutter web dependency.
+ */
+ public boolean declaresFlutterWeb() {
+ return FlutterUtils.declaresFlutterWeb(pubspec);
+ }
+
/**
* Returns true if the pubspec indicates that it is a Flutter plugin.
*/
diff --git a/src/io/flutter/run/SdkFields.java b/src/io/flutter/run/SdkFields.java
index 4fb4322bc9..c46c89b333 100644
--- a/src/io/flutter/run/SdkFields.java
+++ b/src/io/flutter/run/SdkFields.java
@@ -125,13 +125,17 @@ public GeneralCommandLine createFlutterSdkRunCommand(@NotNull Project project,
throw new ExecutionException("Entrypoint isn't within a Flutter pub root");
}
+ final FlutterCommand command;
String[] args = additionalArgs == null ? new String[]{ } : additionalArgs.split(" ");
- if (buildFlavor != null) {
- args = ArrayUtil.append(args, "--flavor=" + buildFlavor);
+ if (FlutterUtils.declaresFlutterWeb(root.getPubspec())) {
+ command = flutterSdk.flutterRunWeb(root, runMode, args);
+ }
+ else {
+ if (buildFlavor != null) {
+ args = ArrayUtil.append(args, "--flavor=" + buildFlavor);
+ }
+ command = flutterSdk.flutterRun(root, main.getFile(), device, runMode, flutterLaunchMode, project, args);
}
- final FlutterCommand command = FlutterUtils.declaresFlutterWeb(root.getPubspec()) ?
- flutterSdk.flutterRunWeb(root, runMode) :
- flutterSdk.flutterRun(root, main.getFile(), device, runMode, flutterLaunchMode, project, args);
return command.createGeneralCommandLine(project);
}
diff --git a/src/io/flutter/sdk/FlutterSdk.java b/src/io/flutter/sdk/FlutterSdk.java
index aa99e1edbe..e7bd8b5977 100644
--- a/src/io/flutter/sdk/FlutterSdk.java
+++ b/src/io/flutter/sdk/FlutterSdk.java
@@ -277,15 +277,13 @@ else if (flutterLaunchMode == FlutterLaunchMode.RELEASE) {
return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.ATTACH, args.toArray(new String[]{ }));
}
- public FlutterCommand flutterRunWeb(@NotNull PubRoot root, @NotNull RunMode mode) {
- // TODO(devoncarew): We need to provision the webdev cli here.
-
+ public FlutterCommand flutterRunWeb(@NotNull PubRoot root, @NotNull RunMode mode, String... additionalArgs) {
// 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[]{ });
+ return new FlutterWebCommand(this, root.getRoot(), FlutterCommand.Type.FLUTTER_WEB_RUN, additionalArgs);
}
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
index b8db7645ac..c2d76c1bd3 100644
--- a/src/io/flutter/sdk/FlutterWebCommand.java
+++ b/src/io/flutter/sdk/FlutterWebCommand.java
@@ -32,7 +32,6 @@ public GeneralCommandLine createGeneralCommandLine(@Nullable Project project) {
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/utils/FlutterModuleUtils.java b/src/io/flutter/utils/FlutterModuleUtils.java
index 1b896144a2..f7f3c472d8 100644
--- a/src/io/flutter/utils/FlutterModuleUtils.java
+++ b/src/io/flutter/utils/FlutterModuleUtils.java
@@ -174,7 +174,7 @@ private static VirtualFile findPreferedXcodeMetadataFile(@Nullable VirtualFile i
@NotNull
public static Module[] getModules(@NotNull Project project) {
// A disposed project has no modules.
- if (project.isDisposed()) return new Module[]{};
+ if (project.isDisposed()) return new Module[]{ };
return ModuleManager.getInstance(project).getModules();
}
@@ -262,8 +262,7 @@ public static void autoShowMain(@NotNull Project project, @NotNull PubRoot root)
}
/**
- * Introspect into the module's content roots, looking for flutter.yaml or a pubspec.yaml that
- * references flutter.
+ * Introspect into the module's content roots, looking for a pubspec.yaml that references flutter.
*
* True is returned if any of the PubRoots associated with the {@link Module} have a pubspec that declares flutter.
*/
@@ -276,6 +275,18 @@ public static boolean declaresFlutter(@NotNull Module module) {
return false;
}
+ /**
+ * Introspect into the module's content roots, looking for a pubspec.yaml that references flutter web.
+ */
+ public static boolean declaresFlutterWeb(@NotNull Module module) {
+ for (PubRoot root : PubRoots.forModule(module)) {
+ if (root.declaresFlutterWeb()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Find flutter modules.
*