Skip to content

Commit

Permalink
Mismatched SDK checking and service plumbing (#54,#61).
Browse files Browse the repository at this point in the history
* adds an inspection that checks for mismatched SDKs and pops up an editor notification (#54)
* introduces `FlutterSettings` for persisting settings
* adds a new `FlutterSdkService` with Small and IDEA IDE implementations (#61)
  • Loading branch information
pq committed Aug 25, 2016
1 parent 361cd43 commit d6b1cd1
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 12 deletions.
8 changes: 8 additions & 0 deletions resources/META-INF/idea-contribs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!-- Defines IDEA IDE-specific contributions and implementations. -->
<idea-plugin version="2">
<extensions defaultExtensionNs="com.intellij">
<projectService serviceInterface="io.flutter.sdk.FlutterSdkService" serviceImplementation="io.flutter.sdk.FlutterIdeaSdkService"
overrides="true"/>
<editorNotificationProvider implementation="io.flutter.inspections.WrongDartSdkConfigurationNotificationProvider"/>
</extensions>
</idea-plugin>
20 changes: 16 additions & 4 deletions resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
<idea-plugin version="2">
<id>io.flutter</id>
<name>flutter-intellij</name>
<description><![CDATA[
Flutter-specific extensions for Dart.
]]></description>

<vendor>flutter.io</vendor>

<depends>Dart</depends>

<description><![CDATA[
Flutter-specific extensions for Dart.
]]></description>
<!-- Contributes IDEA-specific features and implementations. -->
<depends optional="true" config-file="idea-contribs.xml">com.intellij.modules.java</depends>

<!-- Everything following should be SmallIDE-friendly.-->
<!-- See: http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html -->

<extensions defaultExtensionNs="com.intellij">
<configurationType implementation="io.flutter.run.FlutterRunConfigurationType"/>
<runConfigurationProducer implementation="io.flutter.run.FlutterRunConfigurationProducer"/>
<programRunner implementation="io.flutter.run.FlutterRunner"/>

<moduleType id="FLUTTER_MODULE_TYPE" implementationClass="io.flutter.module.FlutterModuleType"/>

<projectService serviceInterface="io.flutter.settings.FlutterSettings" serviceImplementation="io.flutter.settings.FlutterSettings"/>

<!-- SDK type support WIP; see: #55 /-->
<!--sdkType implementation="io.flutter.sdk.FlutterSdkType"/-->

<!-- Plugin service with SmallIDE default, optionally overridden by product-specific implementations -->
<projectService serviceInterface="io.flutter.sdk.FlutterSdkService" serviceImplementation="io.flutter.sdk.FlutterSmallIDESdkService"
overrides="false"/>

<applicationConfigurable groupId="language" instance="io.flutter.sdk.FlutterSettingsConfigurable"
id="flutter.settings" key="flutter.title" bundle="io.flutter.FlutterBundle" nonDefaultProject="true"/>
id="flutter.settings" key="flutter.title" bundle="io.flutter.FlutterBundle" nonDefaultProject="true"/>
</extensions>

</idea-plugin>
2 changes: 2 additions & 0 deletions src/io/flutter/FlutterBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
@@ -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<EditorNotificationPanel>
implements DumbAware {
private static final Key<EditorNotificationPanel> 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<EditorNotificationPanel> 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;
}
}
24 changes: 24 additions & 0 deletions src/io/flutter/sdk/FlutterIdeaSdkService.java
Original file line number Diff line number Diff line change
@@ -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());
}

}
5 changes: 5 additions & 0 deletions src/io/flutter/sdk/FlutterSdk.java
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
33 changes: 33 additions & 0 deletions src/io/flutter/sdk/FlutterSdkService.java
Original file line number Diff line number Diff line change
@@ -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);
}
28 changes: 20 additions & 8 deletions src/io/flutter/sdk/FlutterSdkUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
23 changes: 23 additions & 0 deletions src/io/flutter/sdk/FlutterSmallIDESdkService.java
Original file line number Diff line number Diff line change
@@ -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
}

}
52 changes: 52 additions & 0 deletions src/io/flutter/settings/FlutterSettings.java
Original file line number Diff line number Diff line change
@@ -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<FlutterSettings> {

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;
}
}

0 comments on commit d6b1cd1

Please sign in to comment.