diff --git a/resources/META-INF/idea-contribs.xml b/resources/META-INF/idea-contribs.xml
new file mode 100644
index 0000000000..7d57edd8f3
--- /dev/null
+++ b/resources/META-INF/idea-contribs.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
index e665712397..68542b2b4e 100644
--- a/resources/META-INF/plugin.xml
+++ b/resources/META-INF/plugin.xml
@@ -1,26 +1,38 @@
io.flutter
flutter-intellij
+
+
flutter.io
Dart
-
+
+ com.intellij.modules.java
+
+
+
+
+
+
+
+
+ id="flutter.settings" key="flutter.title" bundle="io.flutter.FlutterBundle" nonDefaultProject="true"/>
diff --git a/src/io/flutter/FlutterBundle.properties b/src/io/flutter/FlutterBundle.properties
index cd12506e5a..758b6bc9c7 100644
--- a/src/io/flutter/FlutterBundle.properties
+++ b/src/io/flutter/FlutterBundle.properties
@@ -20,3 +20,5 @@ no.project.dir=Project directory not given
flutter.sdk.name=Flutter SDK
flutter.sdk.label.0=Flutter {0}
flutter.sdk.label.unknown.0=Unknown Flutter version at {0}
+dart.sdk.configuration.action.label=Configure Dart SDK
+flutter.wrong.dart.sdk.warning=The Dart SDK for this module is not the same as Flutter's. Setting Dart to use the Flutter vended SDK is strongly recommended.
diff --git a/src/io/flutter/inspections/WrongDartSdkConfigurationNotificationProvider.java b/src/io/flutter/inspections/WrongDartSdkConfigurationNotificationProvider.java
new file mode 100644
index 0000000000..b2021680f4
--- /dev/null
+++ b/src/io/flutter/inspections/WrongDartSdkConfigurationNotificationProvider.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2016 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.inspections;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.ui.EditorNotificationPanel;
+import com.intellij.ui.EditorNotifications;
+import com.jetbrains.lang.dart.DartFileType;
+import com.jetbrains.lang.dart.DartLanguage;
+import com.jetbrains.lang.dart.sdk.DartSdk;
+import io.flutter.FlutterBundle;
+import io.flutter.module.FlutterModuleType;
+import io.flutter.sdk.FlutterSdk;
+import io.flutter.sdk.FlutterSdkService;
+import io.flutter.settings.FlutterSettings;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class WrongDartSdkConfigurationNotificationProvider extends EditorNotifications.Provider
+ implements DumbAware {
+ private static final Key KEY = Key.create("Setup Dart SDK");
+
+ private final Project project;
+
+ public WrongDartSdkConfigurationNotificationProvider(@NotNull Project project, @NotNull EditorNotifications notifications) {
+ this.project = project;
+ }
+
+ @NotNull
+ private static EditorNotificationPanel createWrongSdkPanel(@NotNull Project project, @Nullable Module module) {
+
+ final FlutterSettings settings = FlutterSettings.getInstance(project);
+ if (settings.ignoreMismatchedDartSdks()) return null;
+
+ EditorNotificationPanel panel = new EditorNotificationPanel();
+ panel.setText(FlutterBundle.message("flutter.wrong.dart.sdk.warning"));
+ panel.createActionLabel(FlutterBundle.message("dart.sdk.configuration.action.label"),
+ () -> FlutterSdkService.getInstance(project).configureDartSdk(module));
+ panel.createActionLabel("Dismiss", () -> {
+ settings.setIgnoreMismatchedDartSdks(true);
+ panel.setVisible(false);
+ });
+
+ return panel;
+ }
+
+ @NotNull
+ @Override
+ public Key getKey() {
+ return KEY;
+ }
+
+ @Override
+ public EditorNotificationPanel createNotificationPanel(@NotNull VirtualFile file, @NotNull FileEditor fileEditor) {
+ if (file.getFileType() != DartFileType.INSTANCE) return null;
+
+ PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
+ if (psiFile == null) return null;
+
+ if (psiFile.getLanguage() != DartLanguage.INSTANCE) return null;
+
+ Module module = ModuleUtilCore.findModuleForPsiElement(psiFile);
+ if (module == null) return null;
+
+ if (!ModuleType.is(module, FlutterModuleType.getInstance())) return null;
+
+ try {
+ final String flutterDartSdkPath = FlutterSdk.getFlutterSdk(project).getDartSdkPath();
+ final String dartSdkPath = DartSdk.getDartSdk(project).getHomePath();
+ if (!StringUtil.equals(flutterDartSdkPath, dartSdkPath)) {
+ return createWrongSdkPanel(project, module);
+ }
+ }
+ catch (ExecutionException e) {
+ //TODO(pq): add panel for unconfigured Flutter SDK.
+ }
+
+ return null;
+ }
+}
diff --git a/src/io/flutter/sdk/FlutterIdeaSdkService.java b/src/io/flutter/sdk/FlutterIdeaSdkService.java
new file mode 100644
index 0000000000..e3c1f8ca11
--- /dev/null
+++ b/src/io/flutter/sdk/FlutterIdeaSdkService.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 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.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.lang.dart.sdk.DartConfigurable;
+import org.jetbrains.annotations.NotNull;
+
+public class FlutterIdeaSdkService extends FlutterSdkService {
+ public FlutterIdeaSdkService(Project project) {
+ super(project);
+ }
+
+ @Override
+ public void configureDartSdk(@NotNull Module module) {
+ //TODO(pq): consider a service that sets this value or seeds the dialog with a proposed path
+ DartConfigurable.openDartSettings(module.getProject());
+ }
+
+}
diff --git a/src/io/flutter/sdk/FlutterSdk.java b/src/io/flutter/sdk/FlutterSdk.java
index 081a6ff2f4..0e79e21321 100644
--- a/src/io/flutter/sdk/FlutterSdk.java
+++ b/src/io/flutter/sdk/FlutterSdk.java
@@ -179,6 +179,11 @@ public String getVersion() {
return myVersion;
}
+ @NotNull
+ public String getDartSdkPath() throws ExecutionException {
+ return FlutterSdkUtil.pathToDartSdk(getHomePath());
+ }
+
public enum Command {
CREATE("create", "Flutter: Create") {
diff --git a/src/io/flutter/sdk/FlutterSdkService.java b/src/io/flutter/sdk/FlutterSdkService.java
new file mode 100644
index 0000000000..8ebcaa25e2
--- /dev/null
+++ b/src/io/flutter/sdk/FlutterSdkService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 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.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A service to coordinate SDK configuration with specific IDEA and SmallIDE
+ * implementations.
+ */
+public abstract class FlutterSdkService {
+
+ @NotNull
+ protected final Project project;
+
+ protected FlutterSdkService(@NotNull Project project) {
+ this.project = project;
+ }
+
+ public static FlutterSdkService getInstance(@NotNull Project project) {
+ return ServiceManager.getService(project, FlutterSdkService.class);
+ }
+
+ public abstract void configureDartSdk(@Nullable Module module);
+}
diff --git a/src/io/flutter/sdk/FlutterSdkUtil.java b/src/io/flutter/sdk/FlutterSdkUtil.java
index 308e510243..247664e3fa 100644
--- a/src/io/flutter/sdk/FlutterSdkUtil.java
+++ b/src/io/flutter/sdk/FlutterSdkUtil.java
@@ -64,18 +64,30 @@ private static void updateKnownPaths(@NotNull final String propertyKey, @Nullabl
}
}
-
@NotNull
public static String pathToFlutterTool(@NotNull String sdkPath) throws ExecutionException {
- VirtualFile sdk = LocalFileSystem.getInstance().findFileByPath(sdkPath);
- if (sdk == null) throw new ExecutionException(FlutterBundle.message("flutter.sdk.is.not.configured"));
- VirtualFile bin = sdk.findChild("bin");
- if (bin == null) throw new ExecutionException(FlutterBundle.message("flutter.sdk.is.not.configured"));
- VirtualFile exec = bin.findChild("flutter"); // TODO Use flutter.bat on Windows
- if (exec == null) throw new ExecutionException(FlutterBundle.message("flutter.sdk.is.not.configured"));
- return exec.getPath();
+ return sdkRelativePathTo(sdkPath, "bin", "flutter"); // TODO Use flutter.bat on Windows
+ }
+
+ @NotNull
+ public static String pathToDartSdk(@NotNull String sdkPath) throws ExecutionException {
+ return sdkRelativePathTo(sdkPath, "bin", "cache", "dart-sdk");
}
+ @NotNull
+ private static String sdkRelativePathTo(@NotNull String sdkPath, @NotNull String... segments) throws ExecutionException {
+ VirtualFile child = LocalFileSystem.getInstance().findFileByPath(sdkPath);
+ if (child == null) throw new ExecutionException(FlutterBundle.message("flutter.sdk.is.not.configured"));
+ for (String segment : segments) {
+ child = child.findChild(segment);
+ if (child == null) {
+ throw new ExecutionException(FlutterBundle.message("flutter.sdk.is.not.configured"));
+ }
+ }
+ return child.getPath();
+ }
+
+
public static boolean isFlutterSdkLibRoot(@Nullable VirtualFile sdk) {
if (sdk == null) return false;
VirtualFile bin = sdk.findChild("bin");
diff --git a/src/io/flutter/sdk/FlutterSmallIDESdkService.java b/src/io/flutter/sdk/FlutterSmallIDESdkService.java
new file mode 100644
index 0000000000..c7a30bcb7a
--- /dev/null
+++ b/src/io/flutter/sdk/FlutterSmallIDESdkService.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2016 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.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class FlutterSmallIDESdkService extends FlutterSdkService {
+
+ public FlutterSmallIDESdkService(Project project) {
+ super(project);
+ }
+
+ @Override
+ public void configureDartSdk(@NotNull Module module) {
+ //TODO(pq): implement
+ }
+
+}
diff --git a/src/io/flutter/settings/FlutterSettings.java b/src/io/flutter/settings/FlutterSettings.java
new file mode 100644
index 0000000000..4c4d8bc307
--- /dev/null
+++ b/src/io/flutter/settings/FlutterSettings.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 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.settings;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.Nullable;
+
+
+/**
+ * Persists Flutter settings.
+ */
+@State(
+ name = "FlutterSettings",
+ storages = {
+ @Storage("FlutterSettings.xml")}
+)
+public class FlutterSettings implements PersistentStateComponent {
+
+ private boolean ignoreMismatchedDartSdks;
+
+ @Nullable
+ public static FlutterSettings getInstance(Project project) {
+ return ServiceManager.getService(project, FlutterSettings.class);
+ }
+
+ @Nullable
+ @Override
+ public FlutterSettings getState() {
+ return this;
+ }
+
+ @Override
+ public void loadState(FlutterSettings settings) {
+ XmlSerializerUtil.copyBean(settings, this);
+ }
+
+ public boolean ignoreMismatchedDartSdks() {
+ return ignoreMismatchedDartSdks;
+ }
+
+ public void setIgnoreMismatchedDartSdks(boolean ignoreMismatchedDartSdks) {
+ this.ignoreMismatchedDartSdks = ignoreMismatchedDartSdks;
+ }
+}
\ No newline at end of file