diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 4daa746906a..498d91b9c10 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -78,6 +78,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java b/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java index ecd90784c69..dd7fae4c3f4 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java @@ -17,8 +17,14 @@ package org.jetbrains.jet.plugin; import com.google.common.collect.Lists; +import com.intellij.openapi.compiler.CompilerManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.ModuleDescriptor; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; @@ -77,4 +83,24 @@ public class JetPluginUtil { } return libraryScope == ((NamespaceDescriptor) declaration).getMemberScope(); } + + @Nullable + public static Module getModuleForKotlinFile(@NotNull final VirtualFile file, @NotNull final Project project) { + 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 || isMavenModule(module)) return null; + + return module; + } + + private static boolean isMavenModule(@NotNull final Module module) { + for (final VirtualFile root : ModuleRootManager.getInstance(module).getContentRoots()) { + if (root.findChild("pom.xml") != null) { + return true; + } + } + return false; + } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java index b5b1ab1fe9c..516a7f8a423 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java @@ -20,13 +20,11 @@ package org.jetbrains.jet.plugin.quickfix; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.fileChooser.FileChooserFactory; import com.intellij.openapi.fileChooser.FileTextField; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; @@ -55,7 +53,7 @@ import com.intellij.ui.EditorNotificationPanel; import com.intellij.ui.EditorNotifications; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.plugin.JetFileType; +import org.jetbrains.jet.plugin.JetPluginUtil; import org.jetbrains.jet.utils.PathUtil; import javax.swing.*; @@ -83,15 +81,9 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific @Override public EditorNotificationPanel createNotificationPanel(VirtualFile file) { try { - if (file.getFileType() != JetFileType.INSTANCE) return null; - - if (CompilerManager.getInstance(myProject).isExcludedFromCompilation(file)) return null; - - final Module module = ModuleUtil.findModuleForFile(file, myProject); + final Module module = JetPluginUtil.getModuleForKotlinFile(file, myProject); if (module == null) return null; - if (isMavenModule(module)) return null; - if (isJsModule(module)) return null; GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false); @@ -251,14 +243,6 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific }); } - private static boolean isMavenModule(@NotNull Module module) { - for (VirtualFile root : ModuleRootManager.getInstance(module).getContentRoots()) { - if (root.findChild("pom.xml") != null) return true; - } - - return false; - } - private static class ChoosePathDialog extends DialogWrapper { private final Project myProject; private TextFieldWithBrowseButton myPathField; diff --git a/idea/src/org/jetbrains/jet/plugin/sdk/KotlinSdkNotConfiguredNotificationProvider.java b/idea/src/org/jetbrains/jet/plugin/sdk/KotlinSdkNotConfiguredNotificationProvider.java new file mode 100644 index 00000000000..b04ac9280a6 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/sdk/KotlinSdkNotConfiguredNotificationProvider.java @@ -0,0 +1,110 @@ +/* + * Copyright 2010-2012 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.sdk; + +import com.intellij.ProjectTopics; +import com.intellij.ide.util.frameworkSupport.AddFrameworkSupportDialog; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.IndexNotReadyException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleRootEvent; +import com.intellij.openapi.roots.ModuleRootListener; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.search.FilenameIndex; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.EditorNotificationPanel; +import com.intellij.ui.EditorNotifications; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.plugin.JetPluginUtil; + +import java.io.File; +import java.util.Collection; + +/** + * @author Maxim.Manuylov + * Date: 19.05.12 + */ +public class KotlinSdkNotConfiguredNotificationProvider implements EditorNotifications.Provider { + @NotNull private static final Key KEY = Key.create("configure.kotlin.sdk"); + + @NotNull private final Project myProject; + + public KotlinSdkNotConfiguredNotificationProvider(@NotNull final Project project, @NotNull final EditorNotifications notifications) { + myProject = project; + project.getMessageBus().connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() { + @Override + public void beforeRootsChange(final ModuleRootEvent event) {} + + @Override + public void rootsChanged(final ModuleRootEvent event) { + notifications.updateAllNotifications(); + } + }); + } + + @NotNull + @Override + public Key getKey() { + return KEY; + } + + @Nullable + @Override + public EditorNotificationPanel createNotificationPanel(@NotNull final VirtualFile file) { + try { + final Module module = JetPluginUtil.getModuleForKotlinFile(file, myProject); + if (module == null) return null; + + final GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false); + if (!containsKotlinCompilerJar(FilenameIndex.getVirtualFilesByName(myProject, KotlinSdkUtil.KOTLIN_COMPILER_JAR, scope))) { + return createNotificationPanel(module); + } + } + catch (final ProcessCanceledException ignore) {} + catch (final IndexNotReadyException ignore) {} + + return null; + } + + private static boolean containsKotlinCompilerJar(@NotNull final Collection jars) { + for (final VirtualFile jar : jars) { + if (KotlinSdkUtil.isKotlinCompilerJar(new File(jar.getPath()))) { + return true; + } + } + return false; + } + + @NotNull + private static EditorNotificationPanel createNotificationPanel(@NotNull final Module module) { + final EditorNotificationPanel panel = new EditorNotificationPanel(); + panel.setText("Kotlin SDK is not configured for module \"" + module.getName() + "\""); + panel.createActionLabel("Configure Kotlin SDK", new Runnable() { + @Override + public void run() { + final AddFrameworkSupportDialog dialog = AddFrameworkSupportDialog.createDialog(module); + if (dialog != null) { + dialog.show(); + } + } + }); + return panel; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/sdk/KotlinSdkUtil.java b/idea/src/org/jetbrains/jet/plugin/sdk/KotlinSdkUtil.java index 897b016a0fc..6fb28a52980 100644 --- a/idea/src/org/jetbrains/jet/plugin/sdk/KotlinSdkUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/sdk/KotlinSdkUtil.java @@ -25,6 +25,9 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; /** * @author Maxim.Manuylov @@ -39,7 +42,11 @@ public class KotlinSdkUtil { return new KotlinSdkProperties(""); } }; - @NotNull private static final String KOTLIN_COMPILER_JAR = "kotlin-compiler.jar"; + @NotNull public static final String KOTLIN_COMPILER_JAR = "kotlin-compiler.jar"; + @NotNull private static final String[] KOTLIN_COMPILER_JAR_ENTRY_NAMES = { + "org/jetbrains/jet/cli/KotlinCompiler.class", + "org/jetbrains/jet/cli/jvm/K2JVMCompiler.class" + }; private KotlinSdkUtil() {} @@ -48,7 +55,7 @@ public class KotlinSdkUtil { } private static boolean isSDKHome(@NotNull final File dir) { - return dir.isDirectory() && new File(new File(dir, "lib"), KOTLIN_COMPILER_JAR).isFile(); + return dir.isDirectory() && isKotlinCompilerJar(getKotlinCompilerJar(dir)); } @Nullable @@ -57,14 +64,36 @@ public class KotlinSdkUtil { return FileUtil.loadFile(new File(sdkHomePath, "build.txt")).trim(); } catch (final IOException e) { - return null; + try { + return getJarImplementationVersion(getKotlinCompilerJar(new File(sdkHomePath))); + } + catch (final IOException e1) { + return null; + } } } + @Nullable + private static String getJarImplementationVersion(@NotNull final File jar) throws IOException { + final JarFile jarFile = new JarFile(jar); + try { + final Manifest manifest = jarFile.getManifest(); + return manifest == null ? null : manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); + } + finally { + jarFile.close(); + } + } + + @NotNull + private static File getKotlinCompilerJar(@NotNull final File sdkHome) { + return new File(new File(sdkHome, "lib"), KOTLIN_COMPILER_JAR); + } + @Nullable public static String detectSDKVersion(@NotNull final List jars) { for (final VirtualFile jar : jars) { - if (jar.getName().equals(KOTLIN_COMPILER_JAR)) { + if (jar.getName().equals(KOTLIN_COMPILER_JAR) && isKotlinCompilerJar(new File(jar.getPath()))) { final VirtualFile libDir = jar.getParent(); if (libDir != null) { final VirtualFile sdkHomeDir = libDir.getParent(); @@ -77,6 +106,30 @@ public class KotlinSdkUtil { return null; } + public static boolean isKotlinCompilerJar(@NotNull final File jar) { + try { + return doIsKotlinCompilerJar(jar); + } + catch (final IOException e) { + return false; + } + } + + private static boolean doIsKotlinCompilerJar(@NotNull final File jar) throws IOException { + final JarFile jarFile = new JarFile(jar); + try { + for (final String entryName : KOTLIN_COMPILER_JAR_ENTRY_NAMES) { + if (jarFile.getJarEntry(entryName) != null) { + return true; + } + } + return false; + } + finally { + jarFile.close(); + } + } + @NotNull public static String getSDKName(@NotNull final String version) { return "Kotlin " + version;