diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt index 553019fd322..b5665458e5e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt @@ -23,7 +23,6 @@ import com.intellij.execution.lineMarker.RunLineMarkerContributor import com.intellij.execution.testframework.TestIconMapper import com.intellij.execution.testframework.sm.runner.states.TestStateInfo import com.intellij.icons.AllIcons -import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.FileUtil import com.intellij.psi.PsiElement @@ -34,8 +33,10 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.idea.js.getJsOutputFilePath +import org.jetbrains.kotlin.idea.js.jsOrJsImpl +import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath import org.jetbrains.kotlin.idea.project.TargetPlatformDetector +import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.idea.util.string.joinWithEscape import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.name.FqName @@ -45,6 +46,7 @@ import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import org.jetbrains.kotlin.resolve.TargetPlatform.Common import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform import javax.swing.Icon @@ -108,8 +110,8 @@ class KotlinTestRunLineMarkerContributor : RunLineMarkerContributor() { private fun getJavaScriptTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? { if (!descriptor.isTest()) return null - val module = ModuleUtilCore.findModuleForPsiElement(declaration) ?: return null - val testFilePath = getJsOutputFilePath(module, true, false) ?: return null + val module = declaration.module?.jsOrJsImpl() ?: return null + val testFilePath = module.jsTestOutputFilePath ?: return null val locations = ArrayList() @@ -146,7 +148,7 @@ class KotlinTestRunLineMarkerContributor : RunLineMarkerContributor() { val platform = TargetPlatformDetector.getPlatform(declaration.containingKtFile) val icon = when (platform) { is JvmPlatform -> getJavaTestIcon(declaration) - is JsPlatform -> getJavaScriptTestIcon(declaration, descriptor) + is JsPlatform, is Common -> getJavaScriptTestIcon(declaration, descriptor) else -> return null } ?: return null return RunLineMarkerContributor.Info(icon, { "Run Test" }, ExecutorAction.getActions()) diff --git a/idea/src/org/jetbrains/kotlin/idea/js/jsUtils.kt b/idea/src/org/jetbrains/kotlin/idea/js/jsUtils.kt index 05635873ac2..1514a80529b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/js/jsUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/js/jsUtils.kt @@ -19,14 +19,23 @@ package org.jetbrains.kotlin.idea.js import com.intellij.openapi.module.Module import com.intellij.openapi.roots.CompilerModuleExtension import org.jetbrains.jps.util.JpsPathUtil -import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils +import org.jetbrains.kotlin.idea.facet.KotlinFacet +import org.jetbrains.kotlin.idea.facet.implementingModules +import org.jetbrains.kotlin.idea.project.TargetPlatformDetector +import org.jetbrains.kotlin.js.resolve.JsPlatform +import org.jetbrains.kotlin.resolve.TargetPlatform -fun getJsOutputFilePath(module: Module, isTests: Boolean, isMeta: Boolean): String? { - val compilerExtension = CompilerModuleExtension.getInstance(module) - val outputDir = (if (isTests) compilerExtension?.compilerOutputUrlForTests else compilerExtension?.compilerOutputUrl) - ?: return null - val extension = if (isMeta) KotlinJavascriptMetadataUtils.META_JS_SUFFIX else KotlinJavascriptMetadataUtils.JS_EXT - return JpsPathUtil.urlToPath("$outputDir/${module.name}${suffix(isTests)}$extension") -} +val Module.jsTestOutputFilePath: String? + get() { + (KotlinFacet.get(this)?.configuration?.settings?.testOutputPath)?.let { return it } -private fun suffix(isTests: Boolean) = if (isTests) "_test" else "" \ No newline at end of file + val compilerExtension = CompilerModuleExtension.getInstance(this) + val outputDir = compilerExtension?.compilerOutputUrlForTests ?: return null + return JpsPathUtil.urlToPath("$outputDir/${name}_test.js") + } + +fun Module.jsOrJsImpl() = when (TargetPlatformDetector.getPlatform(this)) { + is TargetPlatform.Common -> implementingModules.firstOrNull { TargetPlatformDetector.getPlatform(it) is JsPlatform } + is JsPlatform -> this + else -> null +} \ No newline at end of file diff --git a/ultimate/src/org/jetbrains/kotlin/idea/js/jsModuleUtils.kt b/ultimate/src/org/jetbrains/kotlin/idea/js/jsModuleUtils.kt deleted file mode 100644 index df617d7747e..00000000000 --- a/ultimate/src/org/jetbrains/kotlin/idea/js/jsModuleUtils.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2010-2017 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.kotlin.idea.js - -import com.intellij.openapi.module.Module -import com.intellij.openapi.roots.CompilerModuleExtension -import com.intellij.openapi.roots.ModuleRootManager -import org.jetbrains.kotlin.utils.addIfNotNull -import java.util.* - -private fun addSingleModulePaths(target: Module, result: MutableList) { - val compilerExtension = CompilerModuleExtension.getInstance(target) ?: return - result.addIfNotNull(compilerExtension.compilerOutputPath?.path) - result.addIfNotNull(compilerExtension.compilerOutputPathForTests?.let { "${it.path}/lib" }) -} - -fun getJsClasspath(module: Module): List { - val result = ArrayList() - ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule { - addSingleModulePaths(it, result) - true - } - return result -} diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt index c221c00a862..573cc44d6f0 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt +++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt @@ -33,10 +33,12 @@ import com.intellij.util.SmartList import com.intellij.util.containers.SmartHashSet import com.jetbrains.nodejs.mocha.MochaUtil import com.jetbrains.nodejs.mocha.execution.* -import org.jetbrains.kotlin.idea.js.getJsOutputFilePath +import org.jetbrains.kotlin.idea.js.jsOrJsImpl +import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath import org.jetbrains.kotlin.idea.nodejs.getNodeJsEnvironmentVars import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.util.projectStructure.getModuleDir +import org.jetbrains.kotlin.idea.util.projectStructure.module import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtNamedDeclaration @@ -74,12 +76,21 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() { // Copied from MochaRunConfigurationProducer.isActiveFor() private fun isActiveFor(element: PsiElement, context: ConfigurationContext): Boolean { - val file = PsiUtilCore.getVirtualFile(element) ?: return false - if (isTestRunnerPackageAvailableFor(element.project, file)) return true + val module = element.module + val jsModule = module?.jsOrJsImpl() ?: return false + val file = if (jsModule != module) { + jsModule.moduleFile + } + else { + PsiUtilCore.getVirtualFile(element) + } ?: return false + val project = module.project + + if (isTestRunnerPackageAvailableFor(project, file)) return true if (context.getOriginalConfiguration(MochaConfigurationType.getInstance()) is MochaRunConfiguration) return true - val roots = collectMochaTestRoots(element.project) + val roots = collectMochaTestRoots(project) if (roots.isEmpty()) return false val dirs = SmartHashSet() @@ -113,10 +124,9 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() { } private fun createTestElementRunInfo(element: PsiElement, originalSettings: MochaRunSettings): TestElementInfo? { - val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return null - if (TargetPlatformDetector.getPlatform(module) !is JsPlatform) return null + val module = element.module?.jsOrJsImpl() ?: return null val project = module.project - val testFilePath = getJsOutputFilePath(module, true, false) ?: return null + val testFilePath = module.jsTestOutputFilePath ?: return null val settings = if (originalSettings.workingDir.isBlank()) { val workingDir = FileUtil.toSystemDependentName(project.baseDir.path) originalSettings.builder().setWorkingDir(workingDir).build() diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/nodeJsUtil.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/nodeJsUtil.kt index c352b188078..33caaa89fa5 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/nodeJsUtil.kt +++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/nodeJsUtil.kt @@ -17,17 +17,59 @@ package org.jetbrains.kotlin.idea.nodejs import com.intellij.execution.configuration.EnvironmentVariablesData +import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.CompilerModuleExtension +import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.util.io.FileUtil -import org.jetbrains.kotlin.idea.js.getJsClasspath +import com.intellij.util.PathUtil +import org.jetbrains.kotlin.idea.KotlinPluginUtil +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.plugins.gradle.model.ExternalProject +import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataCache +import org.jetbrains.plugins.gradle.util.GradleConstants import java.io.File +import java.util.* const val NODE_PATH_VAR = "NODE_PATH" fun Module.getNodeJsEnvironmentVars(): EnvironmentVariablesData { - val nodeJsClasspath = getJsClasspath(this).joinToString(File.pathSeparator) { + val nodeJsClasspath = getNodeJsClasspath(this).joinToString(File.pathSeparator) { val basePath = project.basePath ?: return@joinToString it FileUtil.getRelativePath(basePath, it, '/') ?: it } return EnvironmentVariablesData.create(mapOf(NODE_PATH_VAR to nodeJsClasspath), true) +} + +private fun addSingleModulePaths(target: Module, result: MutableList) { + val compilerExtension = CompilerModuleExtension.getInstance(target) ?: return + result.addIfNotNull(compilerExtension.compilerOutputPath?.path) + result.addIfNotNull(compilerExtension.compilerOutputPathForTests?.let { "${it.path}/lib" }) +} + +private fun ExternalProject.findProjectById(id: String): ExternalProject? { + if (this.id == id) return this + for (childProject in childProjects.values) { + childProject.findProjectById(id)?.let { return it } + } + return null +} + +private fun getNodeJsClasspath(module: Module): List { + if (KotlinPluginUtil.isGradleModule(module)) { + val gradleProjectPath = ExternalSystemModulePropertyManager.getInstance(module).getRootProjectPath() + val projectCache = ExternalProjectDataCache.getInstance(module.project) + val rootProject = projectCache.getRootExternalProject(GradleConstants.SYSTEM_ID, File(gradleProjectPath)) ?: return listOf() + val externalProjectId = ExternalSystemApiUtil.getExternalProjectId(module) ?: return listOf() + val buildDir = rootProject.findProjectById(externalProjectId)?.buildDir ?: return listOf() + return listOfNotNull(PathUtil.toSystemIndependentName(buildDir.absolutePath) + "/nodejs_modules") + } + + val result = ArrayList() + ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule { + addSingleModulePaths(it, result) + true + } + return result } \ No newline at end of file diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt index c34c26e06cf..006638e182d 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt +++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt @@ -23,9 +23,12 @@ import com.intellij.openapi.util.Ref import com.intellij.openapi.util.io.FileUtil import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.idea.js.getJsOutputFilePath +import org.jetbrains.kotlin.idea.facet.implementingModules +import org.jetbrains.kotlin.idea.js.jsOrJsImpl +import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath import org.jetbrains.kotlin.idea.nodejs.getNodeJsEnvironmentVars import org.jetbrains.kotlin.idea.project.TargetPlatformDetector +import org.jetbrains.kotlin.resolve.TargetPlatform.Common import org.jetbrains.kotlin.idea.util.projectStructure.getModuleDir import org.jetbrains.kotlin.idea.util.projectStructure.module import org.jetbrains.kotlin.js.resolve.JsPlatform @@ -37,8 +40,8 @@ class KotlinProtractorRunConfigurationProducer : context: ConfigurationContext ): Boolean { val contextPsi = context.psiLocation ?: return false - val module = contextPsi.module ?: return false - val testFilePath = getJsOutputFilePath(module, isTests = true, isMeta = false) ?: return false + val jsModule = contextPsi.module?.jsOrJsImpl() ?: return false + val testFilePath = jsModule.jsTestOutputFilePath ?: return false return configuration.runSettings.testFileSystemDependentPath == FileUtil.toSystemDependentName(testFilePath) } @@ -50,13 +53,13 @@ class KotlinProtractorRunConfigurationProducer : val element = context.psiLocation ?: return false val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return false if (element !is PsiDirectory || module.getModuleDir() != element.virtualFile.path) return false - if (TargetPlatformDetector.getPlatform(module) !is JsPlatform) return false - val testFilePath = getJsOutputFilePath(module, true, false) ?: return false + val jsModule = module.jsOrJsImpl() ?: return false + val testFilePath = jsModule.jsTestOutputFilePath ?: return false sourceElement.set(element) configuration.runSettings = configuration.runSettings.copy( testFilePath = testFilePath, - envData = module.getNodeJsEnvironmentVars() + envData = jsModule.getNodeJsEnvironmentVars() ) configuration.name = configuration.suggestedName() return true