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