callServiceExtension(String methodName, Map
return whenFlutterIsolateResumed().thenComposeAsync((ignored) ->
myDaemonApi.callAppServiceExtension(myAppId, methodName, params)
);
- } else {
+ }
+ else {
return myDaemonApi.callAppServiceExtension(myAppId, methodName, params);
}
}
@@ -526,7 +525,8 @@ public Future shutdownAsync() {
final Future stopDone;
if (DaemonEvent.AppStarting.LAUNCH_MODE_ATTACH.equals(myLaunchMode)) {
stopDone = myDaemonApi.detachApp(appId);
- } else {
+ }
+ else {
stopDone = myDaemonApi.stopApp(appId);
}
final Stopwatch watch = Stopwatch.createStarted();
diff --git a/src/io/flutter/run/test/TestDebugProcess.java b/src/io/flutter/run/test/TestDebugProcess.java
index 9795fbf0d4..41235c9f39 100644
--- a/src/io/flutter/run/test/TestDebugProcess.java
+++ b/src/io/flutter/run/test/TestDebugProcess.java
@@ -12,6 +12,7 @@
import com.jetbrains.lang.dart.ide.runner.ObservatoryConnector;
import com.jetbrains.lang.dart.util.DartUrlResolver;
import io.flutter.run.FlutterPopFrameAction;
+import io.flutter.run.OpenDevToolsAction;
import io.flutter.run.OpenObservatoryAction;
import io.flutter.server.vmService.DartVmServiceDebugProcess;
import org.jetbrains.annotations.NotNull;
@@ -39,6 +40,7 @@ public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar,
@NotNull DefaultActionGroup settings) {
topToolbar.addSeparator();
topToolbar.addAction(new FlutterPopFrameAction());
+ topToolbar.addAction(new OpenDevToolsAction(connector, this::isActive));
topToolbar.addAction(new OpenObservatoryAction(connector, this::isActive));
}
diff --git a/src/io/flutter/run/test/TestLaunchState.java b/src/io/flutter/run/test/TestLaunchState.java
index 51d917f293..7ba152056e 100644
--- a/src/io/flutter/run/test/TestLaunchState.java
+++ b/src/io/flutter/run/test/TestLaunchState.java
@@ -98,8 +98,6 @@ protected ProcessHandler startProcess() throws ExecutionException {
case OK:
assert result.processHandler != null;
return result.processHandler;
- case ANOTHER_RUNNING:
- throw new ExecutionException("Flutter instance already running");
case EXCEPTION:
assert result.exception != null;
throw new ExecutionException(FlutterBundle.message("flutter.command.exception.message" + result.exception.getMessage()));
diff --git a/src/io/flutter/sdk/FlutterCommand.java b/src/io/flutter/sdk/FlutterCommand.java
index ee857f6adc..4953a536c4 100644
--- a/src/io/flutter/sdk/FlutterCommand.java
+++ b/src/io/flutter/sdk/FlutterCommand.java
@@ -25,7 +25,6 @@
import org.jetbrains.annotations.Nullable;
import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
@@ -40,7 +39,7 @@ public class FlutterCommand {
@NotNull
private final FlutterSdk sdk;
- @NotNull
+ @Nullable
private final VirtualFile workDir;
@NotNull
@@ -52,7 +51,7 @@ public class FlutterCommand {
/**
* @see FlutterSdk for methods to create specific commands.
*/
- FlutterCommand(@NotNull FlutterSdk sdk, @NotNull VirtualFile workDir, @NotNull Type type, String... args) {
+ FlutterCommand(@NotNull FlutterSdk sdk, @Nullable VirtualFile workDir, @NotNull Type type, String... args) {
this.sdk = sdk;
this.workDir = workDir;
this.type = type;
@@ -158,13 +157,6 @@ public String toString() {
return "FlutterCommand(" + getDisplayCommand() + ")";
}
- /**
- * The currently running command.
- *
- * We only allow one command to run at a time across all IDEA projects.
- */
- private static final AtomicReference inProgress = new AtomicReference<>(null);
-
/**
* Starts a process that runs a flutter command, unless one is already running.
*
@@ -198,11 +190,6 @@ public OSProcessHandler startProcess(boolean sendAnalytics) {
*/
@NotNull
public FlutterCommandStartResult startProcess(@Nullable Project project) {
- // TODO(devoncarew): Many flutter commands can legitimately be run in parallel.
- if (!inProgress.compareAndSet(null, this)) {
- return new FlutterCommandStartResult(FlutterCommandStartResultStatus.ANOTHER_RUNNING);
- }
-
if (isPubRelatedCommand()) {
DartPlugin.setPubActionInProgress(true);
}
@@ -215,7 +202,6 @@ public FlutterCommandStartResult startProcess(@Nullable Project project) {
handler.addProcessListener(new ProcessAdapter() {
@Override
public void processTerminated(@NotNull final ProcessEvent event) {
- inProgress.compareAndSet(FlutterCommand.this, null);
if (isPubRelatedCommand()) {
DartPlugin.setPubActionInProgress(false);
}
@@ -225,7 +211,6 @@ public void processTerminated(@NotNull final ProcessEvent event) {
return new FlutterCommandStartResult(handler);
}
catch (ExecutionException e) {
- inProgress.compareAndSet(this, null);
if (isPubRelatedCommand()) {
DartPlugin.setPubActionInProgress(false);
}
@@ -269,7 +254,9 @@ public GeneralCommandLine createGeneralCommandLine(@Nullable Project project) {
}
line.setExePath(FileUtil.toSystemDependentName(sdk.getHomePath() + "/bin/" + FlutterSdkUtil.flutterScriptName()));
- line.setWorkDirectory(workDir.getPath());
+ if (workDir != null) {
+ line.setWorkDirectory(workDir.getPath());
+ }
if (!isDoctorCommand()) {
line.addParameter("--no-color");
}
@@ -292,6 +279,7 @@ enum Type {
MAKE_HOST_APP_EDITABLE("Flutter make-host-app-editable", "make-host-app-editable"),
PACKAGES_GET("Flutter packages get", "packages", "get"),
PACKAGES_UPGRADE("Flutter packages upgrade", "packages", "upgrade"),
+ PACKAGES_PUB("Flutter packages pub", "packages", "pub"),
RUN("Flutter run", "run"),
UPGRADE("Flutter upgrade", "upgrade"),
VERSION("Flutter version", "--version"),
diff --git a/src/io/flutter/sdk/FlutterCommandStartResultStatus.java b/src/io/flutter/sdk/FlutterCommandStartResultStatus.java
index 1552a6247c..562edd32fa 100644
--- a/src/io/flutter/sdk/FlutterCommandStartResultStatus.java
+++ b/src/io/flutter/sdk/FlutterCommandStartResultStatus.java
@@ -6,5 +6,5 @@
package io.flutter.sdk;
public enum FlutterCommandStartResultStatus {
- OK, ANOTHER_RUNNING, EXCEPTION
+ OK, EXCEPTION
}
diff --git a/src/io/flutter/sdk/FlutterSdk.java b/src/io/flutter/sdk/FlutterSdk.java
index 6ee1a53a85..8a4bc7893b 100644
--- a/src/io/flutter/sdk/FlutterSdk.java
+++ b/src/io/flutter/sdk/FlutterSdk.java
@@ -174,6 +174,10 @@ public FlutterCommand flutterPackagesUpgrade(@NotNull PubRoot root) {
return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.PACKAGES_UPGRADE);
}
+ public FlutterCommand flutterPackagesPub(@Nullable PubRoot root, String... args) {
+ return new FlutterCommand(this, root == null ? null : root.getRoot(), FlutterCommand.Type.PACKAGES_PUB, args);
+ }
+
public FlutterCommand flutterMakeHostAppEditable(@NotNull PubRoot root) {
return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.MAKE_HOST_APP_EDITABLE);
}
diff --git a/src/io/flutter/view/FlutterView.java b/src/io/flutter/view/FlutterView.java
index 1439e4aead..3890f6a31d 100644
--- a/src/io/flutter/view/FlutterView.java
+++ b/src/io/flutter/view/FlutterView.java
@@ -13,8 +13,6 @@
import com.intellij.ide.plugins.PluginManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
-import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
-import com.intellij.openapi.actionSystem.impl.ActionButton;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
@@ -48,6 +46,7 @@
import io.flutter.FlutterBundle;
import io.flutter.FlutterInitializer;
import io.flutter.FlutterUtils;
+import io.flutter.devtools.DevToolsManager;
import io.flutter.inspector.InspectorService;
import io.flutter.run.daemon.FlutterApp;
import io.flutter.run.daemon.FlutterDevice;
@@ -57,7 +56,6 @@
import io.flutter.settings.FlutterSettings;
import io.flutter.utils.AsyncUtils;
import io.flutter.utils.EventStream;
-import io.flutter.utils.UIUtils;
import io.flutter.utils.VmServiceListenerAdapter;
import org.dartlang.vm.service.VmService;
import org.dartlang.vm.service.element.Event;
@@ -66,6 +64,8 @@
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;
@@ -594,6 +594,42 @@ private void activateToolWindow() {
}
}
+class FlutterViewDevToolsAction extends FlutterViewAction {
+ FlutterViewDevToolsAction(@NotNull FlutterApp app) {
+ super(app, "Open DevTools", "Open Dart DevTools", FlutterIcons.Dart_16);
+ }
+
+ @Override
+ public void perform(AnActionEvent event) {
+ if (app.isSessionActive()) {
+ final String urlString = app.getConnector().getBrowserUrl();
+ if (urlString == null) {
+ 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);
+ }
+ else {
+ final CompletableFuture result = devToolsManager.installDevTools();
+ result.thenAccept(o -> devToolsManager.openBrowserAndConnect(port));
+ }
+ }
+ }
+}
+
class OpenObservatoryAction extends FlutterViewAction {
OpenObservatoryAction(@NotNull FlutterApp app) {
super(app, FlutterBundle.message("open.observatory.action.text"), FlutterBundle.message("open.observatory.action.description"),
@@ -635,7 +671,7 @@ class RepaintRainbowAction extends FlutterViewToggleableAction {
class ToggleInspectModeAction extends FlutterViewToggleableAction {
ToggleInspectModeAction(@NotNull FlutterApp app) {
- super(app, AllIcons.General.LocateHover, ServiceExtensions.toggleSelectWidgetMode);
+ super(app, AllIcons.General.Locate, ServiceExtensions.toggleSelectWidgetMode);
}
@Override
@@ -643,8 +679,7 @@ protected void perform(AnActionEvent event) {
super.perform(event);
if (app.isSessionActive()) {
- // If toggling inspect mode on, bring all devices to the foreground.
- // TODO(jacobr): consider only bringing the device for the currently open inspector TAB.
+ // If toggling inspect mode on, bring the app's device to the foreground.
if (isSelected()) {
final FlutterDevice device = app.device();
if (device != null) {
@@ -747,59 +782,11 @@ private static DefaultActionGroup createPopupActionGroup(FlutterView view, Flutt
group.add(view.registerAction(new AutoHorizontalScrollAction(app, view.shouldAutoHorizontalScroll)));
group.add(view.registerAction(new HighlightNodesShownInBothTrees(app, view.highlightNodesShownInBothTrees)));
group.addSeparator();
+ group.add(view.registerAction(new FlutterViewDevToolsAction(app)));
+ group.addSeparator();
group.add(view.registerAction(new OpenTimelineViewAction(app)));
group.add(view.registerAction(new OpenObservatoryAction(app)));
return group;
}
}
-
-class ObservatoryActionGroup extends AnAction implements CustomComponentAction {
- private final @NotNull FlutterApp app;
- private final DefaultActionGroup myActionGroup;
-
- public ObservatoryActionGroup(@NotNull FlutterView view, @NotNull FlutterApp app) {
- super("Observatory actions", null, FlutterIcons.OpenObservatoryGroup);
-
- this.app = app;
-
- myActionGroup = createPopupActionGroup(view, app);
- }
-
- @Override
- public final void update(AnActionEvent e) {
- e.getPresentation().setEnabled(app.isSessionActive());
- }
-
- @Override
- public void actionPerformed(@NotNull AnActionEvent e) {
- final JComponent button = UIUtils.getComponentOfActionEvent(e);
- if (button == null) {
- return;
- }
- final ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(
- ActionPlaces.UNKNOWN,
- myActionGroup);
- popupMenu.getComponent().show(button, button.getWidth(), 0);
- }
-
- @NotNull
- @Override
- public JComponent createCustomComponent(@NotNull Presentation presentation) {
- final ActionButton button = new ActionButton(
- this,
- presentation,
- ActionPlaces.UNKNOWN,
- ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE
- );
- presentation.putClientProperty("button", button);
- return button;
- }
-
- private static DefaultActionGroup createPopupActionGroup(FlutterView view, FlutterApp app) {
- final DefaultActionGroup group = new DefaultActionGroup();
- group.add(view.registerAction(new OpenTimelineViewAction(app)));
- group.add(view.registerAction(new OpenObservatoryAction(app)));
- return group;
- }
-}