Refactor: move script module info related code to a separate file
This commit is contained in:
committed by
Ilya Chernikov
parent
a8416035b1
commit
bc0ccd4861
@@ -19,35 +19,24 @@ package org.jetbrains.kotlin.idea.caches.resolve
|
|||||||
import com.intellij.openapi.module.Module
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.openapi.module.impl.scopes.LibraryScopeBase
|
import com.intellij.openapi.module.impl.scopes.LibraryScopeBase
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
|
||||||
import com.intellij.openapi.projectRoots.Sdk
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
import com.intellij.openapi.roots.*
|
import com.intellij.openapi.roots.*
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||||
import com.intellij.openapi.roots.libraries.Library
|
import com.intellij.openapi.roots.libraries.Library
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.search.NonClasspathDirectoriesScope
|
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
|
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies
|
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider
|
|
||||||
import org.jetbrains.kotlin.utils.alwaysNull
|
import org.jetbrains.kotlin.utils.alwaysNull
|
||||||
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
||||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
|
||||||
import java.io.File
|
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private val LIBRARY_NAME_PREFIX: String = "library "
|
private val LIBRARY_NAME_PREFIX: String = "library "
|
||||||
private val SCRIPT_NAME_PREFIX: String = "script "
|
|
||||||
|
|
||||||
// TODO used reflection to be compatible with IDEA from both 143 and 144 branches,
|
// TODO used reflection to be compatible with IDEA from both 143 and 144 branches,
|
||||||
// TODO switch to directly using when "since-build" will be >= 144.3357.4
|
// TODO switch to directly using when "since-build" will be >= 144.3357.4
|
||||||
@@ -291,94 +280,6 @@ internal object NotUnderContentRootModuleInfo : IdeaModuleInfo {
|
|||||||
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
|
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
|
|
||||||
override fun equals(other: Any?) = other is ScriptModuleSearchScope && scriptFile == other.scriptFile && super.equals(other)
|
|
||||||
|
|
||||||
override fun hashCode() = scriptFile.hashCode() * 73 * super.hashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
data class ScriptModuleInfo(val project: Project, val scriptFile: VirtualFile,
|
|
||||||
val scriptDefinition: KotlinScriptDefinition) : IdeaModuleInfo {
|
|
||||||
override val moduleOrigin: ModuleOrigin
|
|
||||||
get() = ModuleOrigin.OTHER
|
|
||||||
|
|
||||||
val externalDependencies by lazy {
|
|
||||||
KotlinScriptExternalImportsProvider.getInstance(project)?.getExternalImports(scriptFile)?.makeComparable()
|
|
||||||
}
|
|
||||||
|
|
||||||
override val name: Name = Name.special("<$SCRIPT_NAME_PREFIX${scriptDefinition.name}>")
|
|
||||||
|
|
||||||
override fun contentScope() = GlobalSearchScope.fileScope(project, scriptFile)
|
|
||||||
|
|
||||||
override fun dependencies(): List<IdeaModuleInfo> {
|
|
||||||
return listOf(
|
|
||||||
this,
|
|
||||||
ScriptDependenciesModuleInfo(project, externalDependencies)
|
|
||||||
) + sdkDependencies(externalDependencies, project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sdkDependencies(scriptDependencies: KotlinScriptExternalDependencies?, project: Project): List<SdkInfo>
|
|
||||||
= findJdk(scriptDependencies, project)?.let { SdkInfo(project, it) }.singletonOrEmptyList()
|
|
||||||
|
|
||||||
fun findJdk(dependencies: KotlinScriptExternalDependencies?, project: Project): Sdk? {
|
|
||||||
val jdkTable = ProjectJdkTable.getInstance()
|
|
||||||
val allJdks = jdkTable.allJdks
|
|
||||||
// workaround for mismatched gradle wrapper and plugin version
|
|
||||||
val javaHome = try { dependencies?.javaHome } catch (e: Throwable) { null }
|
|
||||||
|
|
||||||
return allJdks.find { javaHome != null && it.homePath == javaHome } ?:
|
|
||||||
ProjectRootManager.getInstance(project).projectSdk ?:
|
|
||||||
allJdks.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <T> tryGet(body: () -> T): T? {
|
|
||||||
return try {
|
|
||||||
body()
|
|
||||||
}
|
|
||||||
catch (e: Throwable) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class ComparableScriptDependencies(
|
|
||||||
override val javaHome: String?,
|
|
||||||
override val classpath: Iterable<File>,
|
|
||||||
override val imports: Iterable<String>,
|
|
||||||
override val sources: Iterable<File>,
|
|
||||||
override val scripts: Iterable<File>
|
|
||||||
): KotlinScriptExternalDependencies
|
|
||||||
|
|
||||||
fun KotlinScriptExternalDependencies.makeComparable() = ComparableScriptDependencies(
|
|
||||||
tryGet { javaHome }, classpath, imports, sources, tryGet { scripts } ?: listOf()
|
|
||||||
)
|
|
||||||
|
|
||||||
class ScriptDependenciesModuleInfo(val project: Project, val dependencies: ComparableScriptDependencies?): IdeaModuleInfo {
|
|
||||||
override fun dependencies() = listOf(this) + sdkDependencies(dependencies, project)
|
|
||||||
|
|
||||||
override val name = Name.special("<Script dependencies>")
|
|
||||||
|
|
||||||
override fun contentScope(): GlobalSearchScope {
|
|
||||||
if (dependencies == null) {
|
|
||||||
// we do not know which scripts these dependencies are
|
|
||||||
return KotlinSourceFilterScope.libraryClassFiles(
|
|
||||||
KotlinScriptConfigurationManager.getInstance(project).getAllScriptsClasspathScope(), project
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val classpath = KotlinScriptConfigurationManager.toVfsRoots(dependencies.classpath)
|
|
||||||
// TODO: this is not very efficient because KotlinSourceFilterScope already checks if the files are in scripts classpath
|
|
||||||
return KotlinSourceFilterScope.libraryClassFiles(NonClasspathDirectoriesScope(classpath), project)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: intentionally not taking dependencies into account
|
|
||||||
// otherwise there is no way to implement getModuleInfo
|
|
||||||
override fun hashCode() = project.hashCode()
|
|
||||||
override fun equals(other: Any?): Boolean = other is ScriptDependenciesModuleInfo && this.project == other.project
|
|
||||||
|
|
||||||
override val moduleOrigin: ModuleOrigin
|
|
||||||
get() = ModuleOrigin.LIBRARY
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LibraryWithoutSourceScope(project: Project, private val library: Library) :
|
private class LibraryWithoutSourceScope(project: Project, private val library: Library) :
|
||||||
LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
|
LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.caches.resolve
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||||
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.psi.search.NonClasspathDirectoriesScope
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
|
||||||
|
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||||
|
import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies
|
||||||
|
import org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider
|
||||||
|
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
private val SCRIPT_NAME_PREFIX: String = "script "
|
||||||
|
|
||||||
|
class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
|
||||||
|
override fun equals(other: Any?) = other is ScriptModuleSearchScope && scriptFile == other.scriptFile && super.equals(other)
|
||||||
|
|
||||||
|
override fun hashCode() = scriptFile.hashCode() * 73 * super.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ScriptModuleInfo(val project: Project, val scriptFile: VirtualFile,
|
||||||
|
val scriptDefinition: KotlinScriptDefinition) : IdeaModuleInfo {
|
||||||
|
override val moduleOrigin: ModuleOrigin
|
||||||
|
get() = ModuleOrigin.OTHER
|
||||||
|
|
||||||
|
val externalDependencies by lazy {
|
||||||
|
KotlinScriptExternalImportsProvider.getInstance(project)?.getExternalImports(scriptFile)?.makeComparable()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val name: Name = Name.special("<$SCRIPT_NAME_PREFIX${scriptDefinition.name}>")
|
||||||
|
|
||||||
|
override fun contentScope() = GlobalSearchScope.fileScope(project, scriptFile)
|
||||||
|
|
||||||
|
override fun dependencies(): List<IdeaModuleInfo> {
|
||||||
|
return listOf(
|
||||||
|
this,
|
||||||
|
ScriptDependenciesModuleInfo(project, externalDependencies)
|
||||||
|
) + sdkDependencies(externalDependencies, project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sdkDependencies(scriptDependencies: KotlinScriptExternalDependencies?, project: Project): List<SdkInfo>
|
||||||
|
= findJdk(scriptDependencies, project)?.let { SdkInfo(project, it) }.singletonOrEmptyList()
|
||||||
|
|
||||||
|
fun findJdk(dependencies: KotlinScriptExternalDependencies?, project: Project): Sdk? {
|
||||||
|
val jdkTable = ProjectJdkTable.getInstance()
|
||||||
|
val allJdks = jdkTable.allJdks
|
||||||
|
// workaround for mismatched gradle wrapper and plugin version
|
||||||
|
val javaHome = try { dependencies?.javaHome } catch (e: Throwable) { null }
|
||||||
|
|
||||||
|
return allJdks.find { javaHome != null && it.homePath == javaHome } ?:
|
||||||
|
ProjectRootManager.getInstance(project).projectSdk ?:
|
||||||
|
allJdks.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T> tryGet(body: () -> T): T? {
|
||||||
|
return try {
|
||||||
|
body()
|
||||||
|
}
|
||||||
|
catch (e: Throwable) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ComparableScriptDependencies(
|
||||||
|
override val javaHome: String?,
|
||||||
|
override val classpath: Iterable<File>,
|
||||||
|
override val imports: Iterable<String>,
|
||||||
|
override val sources: Iterable<File>,
|
||||||
|
override val scripts: Iterable<File>
|
||||||
|
): KotlinScriptExternalDependencies
|
||||||
|
|
||||||
|
fun KotlinScriptExternalDependencies.makeComparable() = ComparableScriptDependencies(
|
||||||
|
tryGet { javaHome }, classpath, imports, sources, tryGet { scripts } ?: listOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
class ScriptDependenciesModuleInfo(val project: Project, val dependencies: ComparableScriptDependencies?): IdeaModuleInfo {
|
||||||
|
override fun dependencies() = listOf(this) + sdkDependencies(dependencies, project)
|
||||||
|
|
||||||
|
override val name = Name.special("<Script dependencies>")
|
||||||
|
|
||||||
|
override fun contentScope(): GlobalSearchScope {
|
||||||
|
if (dependencies == null) {
|
||||||
|
// we do not know which scripts these dependencies are
|
||||||
|
return KotlinSourceFilterScope.libraryClassFiles(
|
||||||
|
KotlinScriptConfigurationManager.getInstance(project).getAllScriptsClasspathScope(), project
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val classpath = KotlinScriptConfigurationManager.toVfsRoots(dependencies.classpath)
|
||||||
|
// TODO: this is not very efficient because KotlinSourceFilterScope already checks if the files are in scripts classpath
|
||||||
|
return KotlinSourceFilterScope.libraryClassFiles(NonClasspathDirectoriesScope(classpath), project)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: intentionally not taking dependencies into account
|
||||||
|
// otherwise there is no way to implement getModuleInfo
|
||||||
|
override fun hashCode() = project.hashCode()
|
||||||
|
override fun equals(other: Any?): Boolean = other is ScriptDependenciesModuleInfo && this.project == other.project
|
||||||
|
|
||||||
|
override val moduleOrigin: ModuleOrigin
|
||||||
|
get() = ModuleOrigin.LIBRARY
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user