diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 00df0658049..a7049fcc191 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -29,6 +29,12 @@ + + + org.jetbrains.jet.plugin.project.K2JSModuleComponent + + + diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java index c66cdc96710..0b6e3213ad5 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java @@ -26,7 +26,6 @@ import com.intellij.openapi.compiler.CompileScope; import com.intellij.openapi.compiler.TranslatingCompiler; import com.intellij.openapi.compiler.ex.CompileContextEx; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.JavaSdkType; import com.intellij.openapi.projectRoots.JdkUtil; import com.intellij.openapi.projectRoots.Sdk; @@ -61,8 +60,8 @@ public class JetCompiler implements TranslatingCompiler { if (!(virtualFile.getFileType() instanceof JetFileType)) { return false; } - Project project = compileContext.getProject(); - if (project != null && JsModuleDetector.isJsProject(project)) { + Module module = compileContext.getModuleByFile(virtualFile); + if (module != null && JsModuleDetector.isJsModule(module)) { return false; } return true; diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java index dc8589e46fe..20b7f6831f6 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java @@ -51,11 +51,11 @@ public final class K2JSCompiler implements TranslatingCompiler { if (!(file.getFileType() instanceof JetFileType)) { return false; } - Project project = context.getProject(); - if (project == null) { + Module module = context.getModuleByFile(file); + if (module == null) { return false; } - return JsModuleDetector.isJsProject(project); + return JsModuleDetector.isJsModule(module); } @Override @@ -142,14 +142,14 @@ public final class K2JSCompiler implements TranslatingCompiler { } private static void addLibLocationAndTarget(@NotNull Project project, @NotNull ArrayList args) { - Pair data = JsModuleDetector.getLibLocationAndTargetForProject(project); - if (data.first != null) { + Pair libLocationAndTarget = JsModuleDetector.getLibLocationAndTargetForProject(project); + if (libLocationAndTarget.first != null) { args.add("-libzip"); - args.add(data.first); + args.add(libLocationAndTarget.first); } - if (data.second != null) { + if (libLocationAndTarget.second != null) { args.add("-target"); - args.add(data.second); + args.add(libLocationAndTarget.second); } } diff --git a/idea/src/org/jetbrains/jet/plugin/k2jsrun/K2JSRunnerUtils.java b/idea/src/org/jetbrains/jet/plugin/k2jsrun/K2JSRunnerUtils.java index b0b73a5dfdc..65ff421aa7b 100644 --- a/idea/src/org/jetbrains/jet/plugin/k2jsrun/K2JSRunnerUtils.java +++ b/idea/src/org/jetbrains/jet/plugin/k2jsrun/K2JSRunnerUtils.java @@ -69,6 +69,7 @@ public final class K2JSRunnerUtils { @NotNull public static Module getJsModule(@NotNull Project project) { + //TODO Should not be there, we should know what module we are in Module[] modules = ModuleManager.getInstance(project).getModules(); if (modules.length != 1) { throw new UnsupportedOperationException("Kotlin to JavaScript translator temporarily does not support multiple modules."); diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java index 5173e6d91c7..f89015e9dd7 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java @@ -71,7 +71,7 @@ public class JetSourceNavigationHelper { } final Project project = declaration.getProject(); final List libraryFiles = findAllSourceFilesWhichContainIdentifier(declaration); - BindingContext bindingContext = AnalyzerFacadeProvider.getAnalyzerFacadeForProject(project).analyzeFiles( + BindingContext bindingContext = AnalyzerFacadeProvider.getAnalyzerFacadeForProject().analyzeFiles( project, libraryFiles, Collections.emptyList(), diff --git a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeProvider.java b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeProvider.java index 84ec04b4ce5..4d75740f9a2 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/project/AnalyzerFacadeProvider.java @@ -16,31 +16,64 @@ package org.jetbrains.jet.plugin.project; -import com.intellij.openapi.project.Project; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.analyzer.AnalyzerFacade; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM; -import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS; /** * @author Pavel Talanov */ public final class AnalyzerFacadeProvider { + private final static Logger LOG = Logger.getInstance(AnalyzerFacade.class); + private AnalyzerFacadeProvider() { } @NotNull public static AnalyzerFacade getAnalyzerFacadeForFile(@NotNull JetFile file) { - return getAnalyzerFacadeForProject(file.getProject()); + VirtualFile virtualFile = file.getVirtualFile(); + if (virtualFile == null) { + logErrorIfNotTests("No virtual file for " + file.getName() + " with text:\n" + file.getText()); + return getDefaultAnalyzerFacade(); + } + Module moduleForFile = ProjectFileIndex.SERVICE.getInstance(file.getProject()).getModuleForFile(virtualFile); + if (moduleForFile == null) { + logErrorIfNotTests("File " + virtualFile.getPath() + " is not under any module. Cannot determine which facade to use."); + return getDefaultAnalyzerFacade(); + } + return getAnalyzerFacadeForModule(moduleForFile); + } + + private static void logErrorIfNotTests(@NotNull String message) { + if (!ApplicationManager.getApplication().isUnitTestMode()) { + LOG.error(message); + } } @NotNull - public static AnalyzerFacade getAnalyzerFacadeForProject(@NotNull Project project) { - if (JsModuleDetector.isJsProject(project)) { + private static AnalyzerFacade getDefaultAnalyzerFacade() { + LOG.info("Using default analyzer facade"); + return AnalyzerFacadeForJVM.INSTANCE; + } + + @NotNull + private static AnalyzerFacade getAnalyzerFacadeForModule(@NotNull Module module) { + if (JsModuleDetector.isJsModule(module)) { return JSAnalyzerFacadeForIDEA.INSTANCE; } return AnalyzerFacadeForJVM.INSTANCE; } + + //TODO should remove all calls to this method + @NotNull + public static AnalyzerFacade getAnalyzerFacadeForProject() { + return AnalyzerFacadeForJVM.INSTANCE; + } } diff --git a/idea/src/org/jetbrains/jet/plugin/project/JsModuleDetector.java b/idea/src/org/jetbrains/jet/plugin/project/JsModuleDetector.java index dbf3e862692..7cf1ce91429 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/JsModuleDetector.java +++ b/idea/src/org/jetbrains/jet/plugin/project/JsModuleDetector.java @@ -16,77 +16,47 @@ package org.jetbrains.jet.plugin.project; -import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.io.InputStream; - /** * @author Pavel Talanov *

* This class has utility functions to determine whether the project (or module) is js project. */ public final class JsModuleDetector { - - public static final String INDICATION_FILE_NAME = ".kotlin-js"; - private JsModuleDetector() { } - public static boolean isJsProject(@NotNull Project project) { - return findIndicationFileInContentRoots(project) != null; + public static boolean isJsModule(@NotNull Module module) { + return K2JSModuleComponent.getInstance(module).isJavaScriptModule(); + } + + @NotNull + public static Pair getLibLocationAndTargetForProject(@NotNull Project project) { + Module module = getJSModule(project); + if (module == null) { + return Pair.empty(); + } + K2JSModuleComponent jsModuleComponent = K2JSModuleComponent.getInstance(module); + String pathToJavaScriptLibrary = jsModuleComponent.getPathToJavaScriptLibrary(); + String basePath = ModuleRootManager.getInstance(module).getContentRoots()[0].getPath(); + return Pair.create(basePath + pathToJavaScriptLibrary, jsModuleComponent.getEcmaVersion().toString()); } @Nullable - public static VirtualFile findIndicationFileInContentRoots(@NotNull Project project) { - VirtualFile[] roots = ProjectRootManager.getInstance(project).getContentRoots(); - for (VirtualFile root : roots) { - for (VirtualFile child : root.getChildren()) { - if (child.getName().equals(INDICATION_FILE_NAME)) { - return child; - } + private static Module getJSModule(@NotNull Project project) { + Module[] modules = ModuleManager.getInstance(project).getModules(); + for (Module module : modules) { + if (isJsModule(module)) { + return module; } } return null; } - - //TODO: refactor - @NotNull - public static Pair getLibLocationAndTargetForProject(@NotNull Project project) { - VirtualFile indicationFile = findIndicationFileInContentRoots(project); - Logger logger = Logger.getInstance(JsModuleDetector.class); - if (indicationFile == null) { - logger.error("Indication file not found for project " + project.getName()); - return Pair.empty(); - } - - try { - InputStream stream = indicationFile.getInputStream(); - String text = FileUtil.loadTextAndClose(stream); - if (text.isEmpty()) { - logger.error("Indication file is corrupted for project " + project.getName()); - return Pair.empty(); - } - String[] lines = text.split("\n"); - if (lines.length == 0) { - logger.error("Indication file " + indicationFile.getPath() + "is empty"); - return Pair.empty(); - } - String pathToLibFile = lines[0]; - String version = lines.length >= 2 ? lines[1] : null; - String pathToIndicationFileLocation = indicationFile.getParent().getPath(); - return new Pair(pathToIndicationFileLocation + "/" + pathToLibFile, version); - } - catch (IOException e) { - logger.error("Could not open file " + indicationFile.getPath()); - return Pair.empty(); - } - } } diff --git a/idea/src/org/jetbrains/jet/plugin/project/K2JSModuleComponent.java b/idea/src/org/jetbrains/jet/plugin/project/K2JSModuleComponent.java new file mode 100644 index 00000000000..1ef482548c6 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/project/K2JSModuleComponent.java @@ -0,0 +1,127 @@ +/* + * 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.project; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleComponent; +import com.intellij.openapi.roots.impl.storage.ClasspathStorage; +import com.intellij.util.xmlb.XmlSerializerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.config.EcmaVersion; + +/** + * @author Pavel Talanov + */ +@State( + name = "K2JSModule", + storages = @Storage( + id = ClasspathStorage.DEFAULT_STORAGE, + file = "$MODULE_FILE$" + ) +) +public final class K2JSModuleComponent implements ModuleComponent, PersistentStateComponent { + + @NotNull + public static K2JSModuleComponent getInstance(@NotNull Module module) { + return module.getComponent(K2JSModuleComponent.class); + } + + private boolean isJavaScriptModule; + + @Nullable + private String pathToJavaScriptLibrary; + + @NotNull + private EcmaVersion ecmaVersion; + + @NotNull + public EcmaVersion getEcmaVersion() { + return ecmaVersion; + } + + public K2JSModuleComponent() { + this.isJavaScriptModule = false; + this.pathToJavaScriptLibrary = null; + this.ecmaVersion = EcmaVersion.defaultVersion(); + } + + public void setEcmaVersion(@NotNull EcmaVersion ecmaVersion) { + this.ecmaVersion = ecmaVersion; + } + + public boolean isJavaScriptModule() { + return isJavaScriptModule; + } + + public void setJavaScriptModule(boolean javaScriptModule) { + isJavaScriptModule = javaScriptModule; + } + + @Nullable + public String getPathToJavaScriptLibrary() { + return pathToJavaScriptLibrary; + } + + public void setPathToJavaScriptLibrary(@Nullable String pathToJavaScriptLibrary) { + this.pathToJavaScriptLibrary = pathToJavaScriptLibrary; + } + + @Override + public void projectOpened() { + //do nothing + } + + @Override + public void projectClosed() { + //do nothing + } + + @Override + public void moduleAdded() { + //do nothing + } + + @Override + public void initComponent() { + //do nothing + } + + @Override + public void disposeComponent() { + //do nothing + } + + @NotNull + @Override + public String getComponentName() { + return "Kotlin to JavaScript module configuration"; + } + + @Override + public K2JSModuleComponent getState() { + return this; + } + + @Override + public void loadState(K2JSModuleComponent state) { + XmlSerializerUtil.copyBean(state, this); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java index 0b873b1259c..25a5b52de47 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java @@ -56,7 +56,7 @@ import javax.swing.*; import java.io.File; import java.io.IOException; -import static org.jetbrains.jet.plugin.project.JsModuleDetector.isJsProject; +import static org.jetbrains.jet.plugin.project.JsModuleDetector.isJsModule; public class ConfigureKotlinLibraryNotificationProvider implements EditorNotifications.Provider { private static final Key KEY = Key.create("configure.kotlin.library"); @@ -86,7 +86,7 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific if (isMavenModule(module)) return null; - if (isJsProject(myProject)) return null; + if (isJsModule(module)) return null; GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false); if (JavaPsiFacade.getInstance(myProject).findClass("jet.JetObject", scope) == null) { @@ -178,7 +178,7 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific } private void setUpJSModule(@NotNull Module module) { - JsModuleSetUp.doSetUpModule(module.getProject(), new Runnable() { + JsModuleSetUp.doSetUpModule(module, new Runnable() { @Override public void run() { updateNotifications(); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/JsModuleSetUp.java b/idea/src/org/jetbrains/jet/plugin/quickfix/JsModuleSetUp.java index b2bc6a2b8fb..388c6dd3ab1 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/JsModuleSetUp.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/JsModuleSetUp.java @@ -20,14 +20,11 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.plugin.k2jsrun.K2JSRunnerUtils; -import org.jetbrains.jet.plugin.project.JsModuleDetector; +import org.jetbrains.jet.plugin.project.K2JSModuleComponent; import org.jetbrains.jet.utils.PathUtil; import java.io.File; @@ -45,13 +42,13 @@ public final class JsModuleSetUp { private JsModuleSetUp() { } - public static void doSetUpModule(@Nullable Project project, @NotNull Runnable continuation) { - if (project == null) { - notifyFailure("Internal error: Project not found."); + public static void doSetUpModule(@Nullable Module module, @NotNull Runnable continuation) { + if (module == null) { + notifyFailure("Internal error: Module not found."); return; } - File rootDir = getRootDir(project); + File rootDir = getRootDir(module); if (!rootDir.isDirectory()) { notifyFailure("Internal error: Broken content root."); return; @@ -59,19 +56,11 @@ public final class JsModuleSetUp { if (!copyJsLibFiles(rootDir)) return; - File file = new File(rootDir, JsModuleDetector.INDICATION_FILE_NAME); - if (file.exists()) { - notifyInfo("File " + file.getName() + " already exists."); - // If the notification in the editor did not disappear due to - // slow file system events, this will remove the notification - // when the user clicks for the second time - refreshRootDir(project, continuation); - return; - } + K2JSModuleComponent jsModuleComponent = K2JSModuleComponent.getInstance(module); + jsModuleComponent.setJavaScriptModule(true); + jsModuleComponent.setPathToJavaScriptLibrary("/lib/" + PathUtil.JS_LIB_JAR_NAME); - createIndicationFile(file); - - refreshRootDir(project, continuation); + refreshRootDir(module, continuation); } private static boolean copyJsLibFiles(@NotNull File rootDir) { @@ -85,19 +74,10 @@ public final class JsModuleSetUp { return doCopyJsLibFiles(Arrays.asList(jsLibJarPath, jsLibJsPath), rootDir); } - private static void refreshRootDir(@NotNull Project project, @NotNull Runnable continuation) { + private static void refreshRootDir(@NotNull Module project, @NotNull Runnable continuation) { getContentRoot(project).refresh(true, true, continuation); } - private static void createIndicationFile(@NotNull File file) { - try { - FileUtil.writeToFile(file, "lib/" + PathUtil.JS_LIB_JAR_NAME); - } - catch (IOException e) { - notifyFailure("Failed to write file " + file.getName()); - } - } - private static boolean doCopyJsLibFiles(@NotNull List files, @NotNull File rootDir) { try { File lib = new File(rootDir, "lib"); @@ -113,14 +93,13 @@ public final class JsModuleSetUp { } @NotNull - private static File getRootDir(@NotNull Project project) { - VirtualFile contentRoot = getContentRoot(project); + private static File getRootDir(@NotNull Module module) { + VirtualFile contentRoot = getContentRoot(module); return new File(contentRoot.getPath()); } @NotNull - private static VirtualFile getContentRoot(@NotNull Project project) { - Module module = K2JSRunnerUtils.getJsModule(project); + private static VirtualFile getContentRoot(@NotNull Module module) { return ModuleRootManager.getInstance(module).getContentRoots()[0]; } @@ -129,10 +108,4 @@ public final class JsModuleSetUp { message, NotificationType.ERROR)); } - - public static void notifyInfo(@NotNull String message) { - Notifications.Bus.notify(new Notification("Set Up Kotlin to JavaScript Module", "Information", - message, - NotificationType.INFORMATION)); - } } diff --git a/idea/src/org/jetbrains/jet/plugin/run/JetRunConfigurationProducer.java b/idea/src/org/jetbrains/jet/plugin/run/JetRunConfigurationProducer.java index b6c72cbb6e3..f237d4942f5 100644 --- a/idea/src/org/jetbrains/jet/plugin/run/JetRunConfigurationProducer.java +++ b/idea/src/org/jetbrains/jet/plugin/run/JetRunConfigurationProducer.java @@ -59,7 +59,7 @@ public class JetRunConfigurationProducer extends RuntimeConfigurationProducer im return null; } - if (JsModuleDetector.isJsProject(module.getProject())) { + if (JsModuleDetector.isJsModule(module)) { return null; }