UnsupportedAbiVersionNotificationPanelProvider: J2K

This commit is contained in:
Dmitry Jemerov
2015-11-23 15:11:07 +01:00
parent e20fdac9df
commit b1c5d3644e
@@ -14,279 +14,226 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.versions; package org.jetbrains.kotlin.idea.versions
import com.google.common.base.Predicate; import com.google.common.base.Predicate
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2
import com.intellij.ProjectTopics; import com.intellij.ProjectTopics
import com.intellij.icons.AllIcons; import com.intellij.icons.AllIcons
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.compiler.CompilerManager
import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.module.Module; import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.IndexNotReadyException
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootAdapter; import com.intellij.openapi.roots.ModuleRootAdapter
import com.intellij.openapi.roots.ModuleRootEvent; import com.intellij.openapi.roots.ModuleRootEvent
import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.ui.popup.ListPopup
import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Key
import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.Ref
import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.EditorNotificationPanel; import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotifications; import com.intellij.ui.EditorNotifications
import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.messages.MessageBusConnection
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.configuration.*
import org.jetbrains.kotlin.idea.KotlinFileType; import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider
import org.jetbrains.kotlin.idea.configuration.ConfigureKotlinInProjectUtilsKt; import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider
import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider;
import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider;
import javax.swing.*; import javax.swing.*
import java.awt.*; import java.awt.*
import java.text.MessageFormat; import java.text.MessageFormat
import java.util.Collection; import java.util.Collections
import java.util.Collections; import java.util.HashSet
import java.util.HashSet;
public class UnsupportedAbiVersionNotificationPanelProvider extends EditorNotifications.Provider<EditorNotificationPanel> { class UnsupportedAbiVersionNotificationPanelProvider(private val project: Project) : EditorNotifications.Provider<EditorNotificationPanel>() {
private static final Key<EditorNotificationPanel> KEY = Key.create("unsupported.abi.version");
private final Project project; init {
val connection = project.messageBus.connect()
public UnsupportedAbiVersionNotificationPanelProvider(@NotNull Project project) { connection.subscribe(ProjectTopics.PROJECT_ROOTS, object : ModuleRootAdapter() {
this.project = project; override fun rootsChanged(event: ModuleRootEvent?) {
MessageBusConnection connection = project.getMessageBus().connect(); updateNotifications()
connection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
@Override
public void rootsChanged(ModuleRootEvent event) {
updateNotifications();
} }
}); })
connection.subscribe(DumbService.DUMB_MODE, new DumbService.DumbModeListener() { connection.subscribe(DumbService.DUMB_MODE, object : DumbService.DumbModeListener {
@Override override fun enteredDumbMode() {
public void enteredDumbMode() {}
@Override
public void exitDumbMode() {
updateNotifications();
} }
});
override fun exitDumbMode() {
updateNotifications()
}
})
} }
@Nullable private fun doCreate(badRoots: Collection<VirtualFile>): EditorNotificationPanel {
public static EditorNotificationPanel checkAndCreate(@NotNull Project project) { val answer = ErrorNotificationPanel()
SuppressNotificationState state = ServiceManager.getService(project, SuppressNotificationState.class).getState();
if (state != null && state.isSuppressed) { val kotlinLibraries = findAllUsedLibraries(project).keySet()
return null; val badRuntimeLibraries = Collections2.filter(kotlinLibraries) { library ->
assert(library != null) { "library should be non null" }
val runtimeJar = getLocalJar(JavaRuntimePresentationProvider.getRuntimeJar(library))
val jsLibJar = getLocalJar(JSLibraryStdPresentationProvider.getJsStdLibJar(library))
badRoots.contains(runtimeJar) || badRoots.contains(jsLibJar)
} }
Collection<VirtualFile> badRoots = collectBadRoots(project);
if (!badRoots.isEmpty()) {
return new UnsupportedAbiVersionNotificationPanelProvider(project).doCreate(badRoots);
}
return null;
}
private EditorNotificationPanel doCreate(final Collection<VirtualFile> badRoots) {
EditorNotificationPanel answer = new ErrorNotificationPanel();
Collection<Library> kotlinLibraries = KotlinRuntimeLibraryUtilKt.findAllUsedLibraries(project).keySet();
final Collection<Library> badRuntimeLibraries = Collections2.filter(kotlinLibraries, new Predicate<Library>() {
@Override
public boolean apply(@Nullable Library library) {
assert library != null : "library should be non null";
VirtualFile runtimeJar = KotlinRuntimeLibraryUtilKt.getLocalJar(JavaRuntimePresentationProvider.getRuntimeJar(library));
VirtualFile jsLibJar = KotlinRuntimeLibraryUtilKt.getLocalJar(JSLibraryStdPresentationProvider.getJsStdLibJar(library));
return badRoots.contains(runtimeJar) || badRoots.contains(jsLibJar);
}
});
if (!badRuntimeLibraries.isEmpty()) { if (!badRuntimeLibraries.isEmpty()) {
int otherBadRootsCount = badRoots.size() - badRuntimeLibraries.size(); val otherBadRootsCount = badRoots.size - badRuntimeLibraries.size
String text = MessageFormat.format("<html><b>{0,choice,0#|1#|1<Some }Kotlin runtime librar{0,choice,0#|1#y|1<ies}</b>" + val text = MessageFormat.format("<html><b>{0,choice,0#|1#|1<Some }Kotlin runtime librar{0,choice,0#|1#y|1<ies}</b>" + "{1,choice,0#|1# and one other jar|1< and {1} other jars} " + "{1,choice,0#has|0<have} an unsupported format</html>",
"{1,choice,0#|1# and one other jar|1< and {1} other jars} " + badRuntimeLibraries.size,
"{1,choice,0#has|0<have} an unsupported format</html>", otherBadRootsCount)
badRuntimeLibraries.size(),
otherBadRootsCount);
String actionLabelText = MessageFormat.format("Update {0,choice,0#|1#|1<all }Kotlin runtime librar{0,choice,0#|1#y|1<ies} ", val actionLabelText = MessageFormat.format("Update {0,choice,0#|1#|1<all }Kotlin runtime librar{0,choice,0#|1#y|1<ies} ",
badRuntimeLibraries.size()); badRuntimeLibraries.size)
answer.setText(text); answer.setText(text)
answer.createActionLabel(actionLabelText, new Runnable() { answer.createActionLabel(actionLabelText) { updateLibraries(project, badRuntimeLibraries) }
@Override
public void run() {
KotlinRuntimeLibraryUtilKt.updateLibraries(project, badRuntimeLibraries);
}
});
if (otherBadRootsCount > 0) { if (otherBadRootsCount > 0) {
createShowPathsActionLabel(answer, "Show all"); createShowPathsActionLabel(answer, "Show all")
} }
} }
else if (badRoots.size() == 1) { else if (badRoots.size == 1) {
final VirtualFile root = badRoots.iterator().next(); val root = badRoots.iterator().next()
String presentableName = root.getPresentableName(); val presentableName = root.presentableName
answer.setText("<html>Kotlin library <b>'" + presentableName + "'</b> " + answer.setText("<html>Kotlin library <b>'$presentableName'</b> has an unsupported format. Please update the library or the plugin</html>")
"has an unsupported format. Please update the library or the plugin</html>");
answer.createActionLabel("Go to " + presentableName, new Runnable() { answer.createActionLabel("Go to " + presentableName) { navigateToLibraryRoot(project, root) }
@Override
public void run() {
navigateToLibraryRoot(project, root);
}
});
} }
else { else {
answer.setText("Some Kotlin libraries attached to this project have unsupported format. Please update the libraries or the plugin"); answer.setText("Some Kotlin libraries attached to this project have unsupported format. Please update the libraries or the plugin")
createShowPathsActionLabel(answer, "Show paths"); createShowPathsActionLabel(answer, "Show paths")
} }
return answer; return answer
} }
private static void navigateToLibraryRoot(Project project, @NotNull VirtualFile root) { private fun createShowPathsActionLabel(answer: EditorNotificationPanel, labelText: String) {
new OpenFileDescriptor(project, root).navigate(true); val label = Ref<Component>(null)
val action = Runnable {
DumbService.getInstance(project).tryRunReadActionInSmartMode({
val badRoots = collectBadRoots(project)
assert(!badRoots.isEmpty()) { "This action should only be called when bad roots are present" }
val listPopupModel = LibraryRootsPopupModel("Unsupported format", project, badRoots)
val popup = JBPopupFactory.getInstance().createListPopup(listPopupModel)
popup.showUnderneathOf(label.get())
null
}, "Can't show all paths during index update")
}
label.set(answer.createActionLabel(labelText, action))
} }
private void createShowPathsActionLabel(EditorNotificationPanel answer, String labelText) { override fun getKey(): Key<EditorNotificationPanel> {
final Ref<Component> label = new Ref<Component>(null); return KEY
Runnable action = new Runnable() {
@Override
public void run() {
DumbService.getInstance(project).tryRunReadActionInSmartMode(new Computable<Object>() {
@Override
public Object compute() {
Collection<VirtualFile> badRoots = collectBadRoots(project);
assert !badRoots.isEmpty() : "This action should only be called when bad roots are present";
LibraryRootsPopupModel listPopupModel = new LibraryRootsPopupModel("Unsupported format", project, badRoots);
ListPopup popup = JBPopupFactory.getInstance().createListPopup(listPopupModel);
popup.showUnderneathOf(label.get());
return null;
}
}, "Can't show all paths during index update");
}
};
label.set(answer.createActionLabel(labelText, action));
} }
@NotNull override fun createNotificationPanel(file: VirtualFile, fileEditor: FileEditor): EditorNotificationPanel? {
@Override
public Key<EditorNotificationPanel> getKey() {
return KEY;
}
@Nullable
@Override
public EditorNotificationPanel createNotificationPanel(@NotNull VirtualFile file, @NotNull FileEditor fileEditor) {
try { try {
if (DumbService.isDumb(project)) return null; if (DumbService.isDumb(project)) return null
if (ApplicationManager.getApplication().isUnitTestMode()) return null; if (ApplicationManager.getApplication().isUnitTestMode) return null
if (file.getFileType() != KotlinFileType.INSTANCE) return null; if (file.fileType !== KotlinFileType.INSTANCE) return null
if (CompilerManager.getInstance(project).isExcludedFromCompilation(file)) return null; if (CompilerManager.getInstance(project).isExcludedFromCompilation(file)) return null
Module module = ModuleUtilCore.findModuleForFile(file, project); val module = ModuleUtilCore.findModuleForFile(file, project) ?: return null
if (module == null) return null;
if (!ConfigureKotlinInProjectUtilsKt.isModuleConfigured(module)) return null; if (!isModuleConfigured(module)) return null
return checkAndCreate(project); return checkAndCreate(project)
} }
catch (ProcessCanceledException e) { catch (e: ProcessCanceledException) {
// Ignore // Ignore
} }
catch (IndexNotReadyException e) { catch (e: IndexNotReadyException) {
DumbService.getInstance(project).runWhenSmart(updateNotifications); DumbService.getInstance(project).runWhenSmart(updateNotifications)
} }
return null; return null
} }
private static class LibraryRootsPopupModel extends BaseListPopupStep<VirtualFile> { private class LibraryRootsPopupModel(title: String, private val project: Project, roots: Collection<VirtualFile>) : BaseListPopupStep<VirtualFile>(title, roots.toArray<VirtualFile>(arrayOfNulls<VirtualFile>(roots.size))) {
private final Project project;
public LibraryRootsPopupModel(@NotNull String title, @NotNull Project project, @NotNull Collection<VirtualFile> roots) { override fun getTextFor(root: VirtualFile): String {
super(title, roots.toArray(new VirtualFile[roots.size()])); val relativePath = VfsUtilCore.getRelativePath(root, project.baseDir, '/')
this.project = project; return relativePath ?: root.path
} }
@NotNull override fun getIconFor(aValue: VirtualFile): Icon? {
@Override if (aValue.isDirectory) {
public String getTextFor(VirtualFile root) { return AllIcons.Nodes.Folder
String relativePath = VfsUtilCore.getRelativePath(root, project.getBaseDir(), '/');
return relativePath != null ? relativePath : root.getPath();
}
@Override
public Icon getIconFor(VirtualFile aValue) {
if (aValue.isDirectory()) {
return AllIcons.Nodes.Folder;
} }
return AllIcons.FileTypes.Archive; return AllIcons.FileTypes.Archive
} }
@Override override fun onChosen(selectedValue: VirtualFile?, finalChoice: Boolean): PopupStep<Any>? {
public PopupStep onChosen(VirtualFile selectedValue, boolean finalChoice) { navigateToLibraryRoot(project, selectedValue)
navigateToLibraryRoot(project, selectedValue); return PopupStep.FINAL_CHOICE
return FINAL_CHOICE;
} }
@Override override fun isSpeedSearchEnabled(): Boolean {
public boolean isSpeedSearchEnabled() { return true
return true;
} }
} }
private static class ErrorNotificationPanel extends EditorNotificationPanel { private class ErrorNotificationPanel : EditorNotificationPanel() {
public ErrorNotificationPanel() { init {
myLabel.setIcon(AllIcons.General.Error); myLabel.icon = AllIcons.General.Error
} }
} }
private final Runnable updateNotifications = new Runnable() { private val updateNotifications = Runnable { updateNotifications() }
@Override
public void run() {
updateNotifications();
}
};
private void updateNotifications() { private fun updateNotifications() {
ApplicationManager.getApplication().invokeLater(new Runnable() { ApplicationManager.getApplication().invokeLater {
@Override if (!project.isDisposed) {
public void run() { EditorNotifications.getInstance(project).updateAllNotifications()
if (!project.isDisposed()) {
EditorNotifications.getInstance(project).updateAllNotifications();
}
} }
}); }
} }
@NotNull companion object {
private static Collection<VirtualFile> collectBadRoots(@NotNull Project project) { private val KEY = Key.create<EditorNotificationPanel>("unsupported.abi.version")
Collection<VirtualFile> badJVMRoots = KotlinRuntimeLibraryUtilKt.getLibraryRootsWithAbiIncompatibleKotlinClasses(project);
Collection<VirtualFile> badJSRoots = KotlinRuntimeLibraryUtilKt.getLibraryRootsWithAbiIncompatibleForKotlinJs(project);
if (badJVMRoots.isEmpty() && badJSRoots.isEmpty()) return Collections.emptyList(); fun checkAndCreate(project: Project): EditorNotificationPanel? {
val state = ServiceManager.getService(project, SuppressNotificationState::class.java).state
if (state != null && state.isSuppressed) {
return null
}
Collection<VirtualFile> badRoots = new HashSet<VirtualFile>(); val badRoots = collectBadRoots(project)
badRoots.addAll(badJVMRoots); if (!badRoots.isEmpty()) {
badRoots.addAll(badJSRoots); return UnsupportedAbiVersionNotificationPanelProvider(project).doCreate(badRoots)
}
return badRoots; return null
}
private fun navigateToLibraryRoot(project: Project, root: VirtualFile) {
OpenFileDescriptor(project, root).navigate(true)
}
private fun collectBadRoots(project: Project): Collection<VirtualFile> {
val badJVMRoots = getLibraryRootsWithAbiIncompatibleKotlinClasses(project)
val badJSRoots = getLibraryRootsWithAbiIncompatibleForKotlinJs(project)
if (badJVMRoots.isEmpty() && badJSRoots.isEmpty()) return emptyList()
val badRoots = HashSet<VirtualFile>()
badRoots.addAll(badJVMRoots)
badRoots.addAll(badJSRoots)
return badRoots
}
} }
} }