Rewrite Absent Jdk Annotations stripe to balloon
This commit is contained in:
@@ -34,6 +34,9 @@
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.jet.plugin.ktSignature.KotlinSignatureInJavaMarkerUpdater</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.jet.plugin.configuration.ui.AbsentJdkAnnotationsNotifications</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<application-components>
|
||||
@@ -310,7 +313,6 @@
|
||||
|
||||
<contentBasedClassFileProcessor implementation="org.jetbrains.jet.plugin.libraries.JetContentBasedFileSubstitutor"/>
|
||||
|
||||
<editorNotificationProvider implementation="org.jetbrains.jet.plugin.configuration.ui.AbsentJdkAnnotationsNotifications"/>
|
||||
<editorNotificationProvider implementation="org.jetbrains.jet.plugin.quickfix.IncorrectSourceRootNameNotification"/>
|
||||
<editorNotificationProvider implementation="org.jetbrains.jet.plugin.versions.UnsupportedAbiVersionNotificationPanelProvider"/>
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.jet.plugin.configuration.ui.notifications.ConfigureKotlinNotification
|
||||
import com.intellij.notification.NotificationsManager
|
||||
import com.intellij.notification.Notification
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import org.jetbrains.jet.plugin.configuration.ui.notifications.AbsentSdkAnnotationsNotification
|
||||
import org.jetbrains.jet.plugin.configuration.ui.notifications.*
|
||||
|
||||
object ConfigureKotlinNotificationManager: KotlinSingleNotificationManager<ConfigureKotlinNotification> {
|
||||
fun notify(project: Project) {
|
||||
@@ -27,6 +30,12 @@ object ConfigureKotlinNotificationManager: KotlinSingleNotificationManager<Confi
|
||||
}
|
||||
}
|
||||
|
||||
object AbsentSdkAnnotationsNotificationManager: KotlinSingleNotificationManager<AbsentSdkAnnotationsNotification> {
|
||||
fun notify(project: Project, sdks: Collection<Sdk>) {
|
||||
notify(project, AbsentSdkAnnotationsNotification(sdks, getNotificationTitle(sdks), getNotificationString(sdks)))
|
||||
}
|
||||
}
|
||||
|
||||
trait KotlinSingleNotificationManager<T: Notification> {
|
||||
fun notify(project: Project, notification: T) {
|
||||
val notificationsManager = NotificationsManager.getNotificationsManager()
|
||||
|
||||
+87
-59
@@ -16,83 +16,111 @@
|
||||
|
||||
package org.jetbrains.jet.plugin.configuration.ui;
|
||||
|
||||
import com.intellij.openapi.compiler.CompilerManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.ProjectTopics;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.AbstractProjectComponent;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ModuleRootAdapter;
|
||||
import com.intellij.openapi.roots.ModuleRootEvent;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.ui.EditorNotificationPanel;
|
||||
import com.intellij.ui.EditorNotifications;
|
||||
import com.intellij.openapi.vfs.*;
|
||||
import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.plugin.JetFileType;
|
||||
import org.jetbrains.jet.plugin.configuration.AbsentSdkAnnotationsNotificationManager;
|
||||
import org.jetbrains.jet.plugin.configuration.ConfigureKotlinInProjectUtils;
|
||||
import org.jetbrains.jet.plugin.configuration.ui.notifications.NotificationsPackage;
|
||||
import org.jetbrains.jet.plugin.framework.KotlinFrameworkDetector;
|
||||
import org.jetbrains.jet.plugin.versions.KotlinRuntimeLibraryUtil;
|
||||
|
||||
public class AbsentJdkAnnotationsNotifications extends EditorNotifications.Provider<EditorNotificationPanel> {
|
||||
private static final Key<EditorNotificationPanel> KEY = Key.create("add.kotlin.jdk.annotations");
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
private final Project project;
|
||||
public class AbsentJdkAnnotationsNotifications extends AbstractProjectComponent {
|
||||
|
||||
public AbsentJdkAnnotationsNotifications(Project project) {
|
||||
this.project = project;
|
||||
protected AbsentJdkAnnotationsNotifications(Project project) {
|
||||
super(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public EditorNotificationPanel createNotificationPanel(VirtualFile file, FileEditor fileEditor) {
|
||||
if (file.getFileType() != JetFileType.INSTANCE) return null;
|
||||
if (CompilerManager.getInstance(project).isExcludedFromCompilation(file)) return null;
|
||||
|
||||
final Module module = ModuleUtil.findModuleForFile(file, project);
|
||||
if (module == null) return null;
|
||||
|
||||
GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false);
|
||||
if (JavaPsiFacade.getInstance(project).findClass("jet.JetObject", scope) == null) return null;
|
||||
|
||||
Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
|
||||
if (sdk == null) return null;
|
||||
|
||||
return createMissingSdkAnnotationsPanelIfNeeded(module, sdk);
|
||||
}
|
||||
|
||||
private EditorNotificationPanel createMissingSdkAnnotationsPanelIfNeeded(final Module module, @NotNull Sdk sdk) {
|
||||
final boolean isAndroidSdk = isAndroidSdk(sdk);
|
||||
if (KotlinRuntimeLibraryUtil.jdkAnnotationsArePresent(module)) {
|
||||
if (!isAndroidSdk || KotlinRuntimeLibraryUtil.androidSdkAnnotationsArePresent(module)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
EditorNotificationPanel panel = new EditorNotificationPanel();
|
||||
String sdkKind = isAndroidSdk ? "Android SDK" : "JDK";
|
||||
panel.setText("Kotlin external annotations for " + sdkKind + " are not set for '" + sdk.getName() + "'.");
|
||||
panel.createActionLabel("Set up Kotlin " + sdkKind + " annotations", new Runnable() {
|
||||
public void projectOpened() {
|
||||
super.projectOpened();
|
||||
MessageBusConnection connection = myProject.getMessageBus().connect();
|
||||
connection.subscribe(DumbService.DUMB_MODE, new DumbService.DumbModeListener() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!KotlinRuntimeLibraryUtil.jdkAnnotationsArePresent(module)) {
|
||||
KotlinRuntimeLibraryUtil.addJdkAnnotations(module);
|
||||
}
|
||||
if (isAndroidSdk && !KotlinRuntimeLibraryUtil.androidSdkAnnotationsArePresent(module)) {
|
||||
KotlinRuntimeLibraryUtil.addAndroidSdkAnnotations(module);
|
||||
}
|
||||
public void enteredDumbMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitDumbMode() {
|
||||
showNotificationIfNeeded();
|
||||
}
|
||||
});
|
||||
connection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
|
||||
@Override
|
||||
public void rootsChanged(ModuleRootEvent event) {
|
||||
showNotificationIfNeeded();
|
||||
}
|
||||
});
|
||||
|
||||
return panel;
|
||||
connection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkVirtualFileListenerAdapter(new VirtualFileAdapter() {
|
||||
@Override
|
||||
public void fileDeleted(VirtualFileEvent event) {
|
||||
showNotificationIfNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileCreated(VirtualFileEvent event) {
|
||||
showNotificationIfNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileMoved(VirtualFileMoveEvent event) {
|
||||
showNotificationIfNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileCopied(VirtualFileCopyEvent event) {
|
||||
showNotificationIfNeeded();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<EditorNotificationPanel> getKey() {
|
||||
return KEY;
|
||||
public void showNotificationIfNeeded() {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Collection<Sdk> sdks = collectSdksWithoutAnnotations();
|
||||
if (sdks.isEmpty()) return;
|
||||
AbsentSdkAnnotationsNotificationManager.instance$.notify(myProject, sdks);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isAndroidSdk(@NotNull Sdk sdk) {
|
||||
return sdk.getSdkType().getName().equals("Android SDK");
|
||||
@NotNull
|
||||
private Collection<Sdk> collectSdksWithoutAnnotations() {
|
||||
Set<Sdk> sdks = Sets.newHashSet();
|
||||
for (Module module : ConfigureKotlinInProjectUtils.getModulesWithKotlinFiles(myProject)) {
|
||||
if (KotlinFrameworkDetector.isJavaKotlinModule(module)) {
|
||||
Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
|
||||
if (sdk != null && !isAnnotationsArePresent(sdk)) {
|
||||
sdks.add(sdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sdks;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAnnotationsArePresent(@NotNull Sdk sdk) {
|
||||
if (!KotlinRuntimeLibraryUtil.jdkAnnotationsArePresent(sdk)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isAndroidSdk = NotificationsPackage.isAndroidSdk(sdk);
|
||||
return !(isAndroidSdk && !KotlinRuntimeLibraryUtil.androidSdkAnnotationsArePresent(sdk));
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2010-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.configuration.ui.notifications
|
||||
|
||||
import com.intellij.notification.Notification
|
||||
import com.intellij.notification.NotificationListener
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import org.jetbrains.jet.plugin.versions.KotlinRuntimeLibraryUtil
|
||||
import javax.swing.event.HyperlinkEvent
|
||||
|
||||
private val GROUP_ID = "Configure Kotlin: absent sdk annotations"
|
||||
|
||||
public class AbsentSdkAnnotationsNotification(sdks: Collection<Sdk>, val title: String, val text: String) :
|
||||
Notification(
|
||||
GROUP_ID,
|
||||
title, text,
|
||||
NotificationType.WARNING,
|
||||
AbsentAnnotationsListener(sdks) // Workaround for KT-4086
|
||||
)
|
||||
{
|
||||
override fun equals(obj: Any?) = obj is AbsentSdkAnnotationsNotification && text == obj.text && title == obj.title
|
||||
|
||||
override fun hashCode(): Int = 31 * title.hashCode() + text.hashCode()
|
||||
}
|
||||
|
||||
fun isAndroidSdk(sdk: Sdk) = sdk.getSdkType().getName() == "Android SDK"
|
||||
|
||||
fun getNotificationTitle(sdks: Collection<Sdk>) = "Kotlin external annotations for ${getSdkKind(sdks)} are not set for " + getSdkName(sdks)
|
||||
|
||||
fun getNotificationString(sdks: Collection<Sdk>) = "<a href=\"configure\">Set up Kotlin ${getSdkKind(sdks)} annotations</a>"
|
||||
|
||||
private class AbsentAnnotationsListener(val sdks: Collection<Sdk>): NotificationListener {
|
||||
override fun hyperlinkUpdate(notification: Notification, event: HyperlinkEvent) {
|
||||
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
if (event.getDescription() == "configure") {
|
||||
for (sdk in sdks) {
|
||||
if (!KotlinRuntimeLibraryUtil.jdkAnnotationsArePresent(sdk)) {
|
||||
KotlinRuntimeLibraryUtil.addJdkAnnotations(sdk);
|
||||
}
|
||||
if (isAndroidSdk(sdk) && !KotlinRuntimeLibraryUtil.androidSdkAnnotationsArePresent(sdk)) {
|
||||
KotlinRuntimeLibraryUtil.addAndroidSdkAnnotations(sdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
notification.expire()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSdkKind(sdks: Collection<Sdk>) = if (sdks.size() > 1) "SDK" else if (isAndroidSdk(sdks.first())) "Android SDK" else "JDK"
|
||||
|
||||
private fun getSdkName(sdks: Collection<Sdk>) = if (sdks.size() > 1) "several SDKs" else "'${sdks.first().getName()}'"
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ import org.jetbrains.jet.utils.PathUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.util.PathUtil.getLocalFile;
|
||||
@@ -95,19 +94,15 @@ public class KotlinRuntimeLibraryUtil {
|
||||
return badRoots;
|
||||
}
|
||||
|
||||
public static void addJdkAnnotations(@NotNull Module module) {
|
||||
addAnnotations(module, PathUtil.getKotlinPathsForIdeaPlugin().getJdkAnnotationsPath());
|
||||
public static void addJdkAnnotations(@NotNull Sdk sdk) {
|
||||
addAnnotations(sdk, PathUtil.getKotlinPathsForIdeaPlugin().getJdkAnnotationsPath());
|
||||
}
|
||||
|
||||
public static void addAndroidSdkAnnotations(@NotNull Module module) {
|
||||
addAnnotations(module, PathUtil.getKotlinPathsForIdeaPlugin().getAndroidSdkAnnotationsPath());
|
||||
public static void addAndroidSdkAnnotations(@NotNull Sdk sdk) {
|
||||
addAnnotations(sdk, PathUtil.getKotlinPathsForIdeaPlugin().getAndroidSdkAnnotationsPath());
|
||||
}
|
||||
|
||||
private static void addAnnotations(@NotNull Module module, @NotNull File annotationsPath) {
|
||||
Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
|
||||
if (sdk == null) {
|
||||
return;
|
||||
}
|
||||
private static void addAnnotations(@NotNull Sdk sdk, @NotNull File annotationsPath) {
|
||||
if (annotationsPath.exists()) {
|
||||
VirtualFile jdkAnnotationsJar = LocalFileSystem.getInstance().findFileByIoFile(annotationsPath);
|
||||
if (jdkAnnotationsJar != null) {
|
||||
@@ -119,17 +114,15 @@ public class KotlinRuntimeLibraryUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean jdkAnnotationsArePresent(@NotNull Module module) {
|
||||
return areAnnotationsPresent(module, PathUtil.JDK_ANNOTATIONS_JAR);
|
||||
public static boolean jdkAnnotationsArePresent(@NotNull Sdk sdk) {
|
||||
return areAnnotationsPresent(sdk, PathUtil.JDK_ANNOTATIONS_JAR);
|
||||
}
|
||||
|
||||
public static boolean androidSdkAnnotationsArePresent(@NotNull Module module) {
|
||||
return areAnnotationsPresent(module, PathUtil.ANDROID_SDK_ANNOTATIONS_JAR);
|
||||
public static boolean androidSdkAnnotationsArePresent(@NotNull Sdk sdk) {
|
||||
return areAnnotationsPresent(sdk, PathUtil.ANDROID_SDK_ANNOTATIONS_JAR);
|
||||
}
|
||||
|
||||
private static boolean areAnnotationsPresent(@NotNull Module module, @NotNull final String jarFileName) {
|
||||
Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
|
||||
if (sdk == null) return false;
|
||||
private static boolean areAnnotationsPresent(@NotNull Sdk sdk, @NotNull final String jarFileName) {
|
||||
return ContainerUtil.exists(sdk.getRootProvider().getFiles(AnnotationOrderRootType.getInstance()),
|
||||
new Condition<VirtualFile>() {
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user