Refactoring: extract separate manager to update script configurations of gradle scripts
The main goal is to extract logic that is related to Gradle scripts only to separate place. Configurations for gradle scripts are updated during import simultaneously, there is no need to manage script configuration update for one file Note that for Gradle less than 6.0 The DefaultScriptingSupport is used
This commit is contained in:
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsCache
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsStorage
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsStorage.Companion.ScriptClassRoots
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
|
||||
internal class GradleClassRootsCache(
|
||||
project: Project,
|
||||
configuration: Configuration?,
|
||||
override val fileToConfiguration: (VirtualFile) -> ScriptCompilationConfigurationWrapper?
|
||||
) : ScriptClassRootsCache(project, extractRoots(configuration)) {
|
||||
|
||||
override val rootsCacheKey = ScriptClassRootsStorage.Companion.Key("gradle")
|
||||
|
||||
override fun getScriptSdk(file: VirtualFile): Sdk? {
|
||||
return firstScriptSdk
|
||||
}
|
||||
|
||||
override val firstScriptSdk: Sdk? = configuration?.let { getScriptSdk(configuration.context.javaHome) }
|
||||
|
||||
// TODO what should we do if no configuration is loaded yet
|
||||
override fun contains(file: VirtualFile): Boolean = true
|
||||
|
||||
companion object {
|
||||
fun extractRoots(configuration: Configuration?): ScriptClassRoots {
|
||||
if (configuration == null) {
|
||||
return ScriptClassRootsStorage.EMPTY
|
||||
}
|
||||
val scriptSdk = getScriptSdk(configuration.context.javaHome)
|
||||
if (scriptSdk != null && !scriptSdk.isAlreadyIndexed(configuration.context.project)) {
|
||||
return ScriptClassRootsStorage.Companion.ScriptClassRoots(
|
||||
configuration.classFilePath,
|
||||
configuration.sourcePath,
|
||||
setOf(scriptSdk)
|
||||
)
|
||||
}
|
||||
return ScriptClassRoots(
|
||||
configuration.classFilePath,
|
||||
configuration.sourcePath,
|
||||
getScriptSdk(configuration.context.javaHome)?.let { setOf(it) } ?: setOf()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-14
@@ -34,15 +34,14 @@ fun runPartialGradleImport(project: Project) {
|
||||
)
|
||||
}
|
||||
|
||||
private val kotlinDslNotificationGroupId = "Gradle Kotlin DSL Scripts"
|
||||
private const val kotlinDslNotificationGroupId = "Gradle Kotlin DSL Scripts"
|
||||
private var Project.notificationPanel: ScriptConfigurationChangedNotification?
|
||||
by UserDataProperty<Project, ScriptConfigurationChangedNotification>(Key.create("load.script.configuration.panel"))
|
||||
|
||||
|
||||
fun showNotificationForProjectImport(project: Project, callback: () -> Unit) {
|
||||
fun showNotificationForProjectImport(project: Project) {
|
||||
val existingPanel = project.notificationPanel
|
||||
if (existingPanel != null) {
|
||||
existingPanel.update(callback)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,7 +52,7 @@ fun showNotificationForProjectImport(project: Project, callback: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
val notification = ScriptConfigurationChangedNotification(project, callback)
|
||||
val notification = ScriptConfigurationChangedNotification(project)
|
||||
project.notificationPanel = notification
|
||||
notification.notify(project)
|
||||
}
|
||||
@@ -64,7 +63,7 @@ fun hideNotificationForProjectImport(project: Project): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
private class ScriptConfigurationChangedNotification(val project: Project, onClick: () -> Unit) :
|
||||
private class ScriptConfigurationChangedNotification(val project: Project) :
|
||||
Notification(
|
||||
kotlinDslNotificationGroupId,
|
||||
KotlinIcons.LOAD_SCRIPT_CONFIGURATION,
|
||||
@@ -76,9 +75,7 @@ private class ScriptConfigurationChangedNotification(val project: Project, onCli
|
||||
) {
|
||||
|
||||
init {
|
||||
//NotificationGroup.findRegisteredGroup(s) ?: NotificationGroup.balloonGroup(s)
|
||||
|
||||
addAction(LoadConfigurationAction(onClick))
|
||||
addAction(LoadConfigurationAction())
|
||||
addAction(NotificationAction.createSimple(KotlinIdeaGradleBundle.message("action.label.enable.auto.import")) {
|
||||
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
|
||||
val projectSettings = gradleSettings.getLinkedProjectsSettings()
|
||||
@@ -87,22 +84,20 @@ private class ScriptConfigurationChangedNotification(val project: Project, onCli
|
||||
if (projectSettings != null) {
|
||||
projectSettings.isUseAutoImport = true
|
||||
}
|
||||
runPartialGradleImport(project)
|
||||
})
|
||||
}
|
||||
|
||||
fun update(onClick: () -> Unit) {
|
||||
actions.filterIsInstance<LoadConfigurationAction>().forEach { it.onClick = onClick }
|
||||
}
|
||||
|
||||
override fun expire() {
|
||||
super.expire()
|
||||
|
||||
project.notificationPanel = null
|
||||
}
|
||||
|
||||
private class LoadConfigurationAction(var onClick: () -> Unit) : AnAction(KotlinIdeaGradleBundle.message("action.label.import.project")) {
|
||||
private class LoadConfigurationAction : AnAction(KotlinIdeaGradleBundle.message("action.label.import.project")) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
onClick()
|
||||
val project = e.project ?: return
|
||||
runPartialGradleImport(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-16
@@ -8,7 +8,6 @@
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import com.intellij.diff.util.DiffUtil
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
@@ -46,17 +45,16 @@ fun runPartialGradleImport(project: Project) {
|
||||
)
|
||||
}
|
||||
|
||||
private var Project.notificationPanel: Boolean?
|
||||
by UserDataProperty<Project, Boolean>(Key.create("load.script.configuration.panel"))
|
||||
private var Project.shouldShowLoadConfiguraionsAction: Boolean?
|
||||
by UserDataProperty<Project, Boolean>(Key.create("load.script.configuration.action"))
|
||||
|
||||
|
||||
fun showNotificationForProjectImport(project: Project, callback: () -> Unit) {
|
||||
project.notificationPanel = true
|
||||
(ActionManager.getInstance().getAction("LoadConfigurationAction") as LoadConfigurationAction).onClick = callback
|
||||
fun showNotificationForProjectImport(project: Project) {
|
||||
project.shouldShowLoadConfiguraionsAction = true
|
||||
}
|
||||
|
||||
fun hideNotificationForProjectImport(project: Project): Boolean {
|
||||
project.notificationPanel = false
|
||||
project.shouldShowLoadConfiguraionsAction = false
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -65,11 +63,9 @@ class LoadConfigurationAction : AnAction(
|
||||
KotlinIdeaGradleBundle.message("action.description.load.script.configurations"),
|
||||
KotlinIcons.LOAD_SCRIPT_CONFIGURATION
|
||||
) {
|
||||
|
||||
var onClick = {}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
onClick()
|
||||
val project = e.project ?: return
|
||||
runPartialGradleImport(project)
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
@@ -79,24 +75,28 @@ class LoadConfigurationAction : AnAction(
|
||||
}
|
||||
|
||||
private fun ensureValidActionVisibility(e: AnActionEvent) {
|
||||
if (e.project?.notificationPanel != true) {
|
||||
if (e.project?.shouldShowLoadConfiguraionsAction != true) {
|
||||
e.presentation.isVisible = false
|
||||
return
|
||||
}
|
||||
val editor = e.getData(CommonDataKeys.EDITOR) ?: return
|
||||
when {
|
||||
DiffUtil.isDiffEditor(editor) -> e.presentation.isVisible = false
|
||||
!editor.isScriptEditor() -> e.presentation.isVisible = false
|
||||
if (DiffUtil.isDiffEditor(editor)) {
|
||||
e.presentation.isVisible = false
|
||||
return
|
||||
}
|
||||
e.presentation.isVisible = editor.isScriptEditor()
|
||||
}
|
||||
|
||||
private fun Editor.isScriptEditor(): Boolean {
|
||||
val project = project ?: return false
|
||||
|
||||
val documentManager = FileDocumentManager.getInstance()
|
||||
val virtualFile = documentManager.getFile(document)
|
||||
if (virtualFile is LightVirtualFileBase) return false
|
||||
if (virtualFile == null || !virtualFile.isValid) return false
|
||||
|
||||
val project = project ?: return false
|
||||
|
||||
// todo only for gradle script
|
||||
return virtualFile.findScriptDefinition(project) != null
|
||||
}
|
||||
}
|
||||
-6
@@ -78,12 +78,6 @@ class GradleScriptConfigurationLoader(project: Project) : DefaultScriptConfigura
|
||||
}
|
||||
}
|
||||
|
||||
val gradleVersion = getGradleVersion(project)
|
||||
if (gradleVersion != null && kotlinDslScriptsModelImportSupported(gradleVersion)) {
|
||||
runPartialGradleImport(project)
|
||||
return true
|
||||
}
|
||||
|
||||
val result = getConfigurationThroughScriptingApi(ktFile, vFile, scriptDefinition)
|
||||
context.saveNewConfiguration(vFile, result)
|
||||
return true
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupport
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupportHelper
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsCache
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsIndexer
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.GradleKtsContext
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.createGradleKtsContextIfPossible
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.toScriptConfiguration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import java.nio.file.Paths
|
||||
|
||||
|
||||
internal data class ConfigurationData(
|
||||
val templateClasspath: List<String>,
|
||||
val models: List<KotlinDslScriptModel>
|
||||
)
|
||||
|
||||
internal class Configuration(val context: GradleKtsContext, data: ConfigurationData) {
|
||||
val scripts = data.models.associateBy { it.file }
|
||||
val sourcePath = data.models.flatMapTo(mutableSetOf()) { it.sourcePath }
|
||||
|
||||
val classFilePath: MutableSet<String> = mutableSetOf()
|
||||
|
||||
init {
|
||||
classFilePath.addAll(data.templateClasspath)
|
||||
data.models.flatMapTo(classFilePath) { it.classPath }
|
||||
}
|
||||
|
||||
fun scriptModel(file: VirtualFile): KotlinDslScriptModel? {
|
||||
return scripts[FileUtil.toSystemDependentName(file.path)]
|
||||
}
|
||||
}
|
||||
|
||||
class GradleScriptingSupport(val project: Project) : ScriptingSupport() {
|
||||
@Volatile
|
||||
private var configuration: Configuration? = null
|
||||
|
||||
private val rootsIndexer = ScriptClassRootsIndexer(project)
|
||||
|
||||
init {
|
||||
if (isKotlinDslScriptsModelImportSupported(project)) {
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
val data = KotlinDslScriptModels.read(project)
|
||||
val gradleKtsContext = createGradleKtsContextIfPossible(project)
|
||||
if (data != null && gradleKtsContext != null) {
|
||||
val newConfiguration = Configuration(
|
||||
gradleKtsContext,
|
||||
data
|
||||
)
|
||||
|
||||
configuration = newConfiguration
|
||||
configurationChangedCallback(newConfiguration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun recreateRootsCache(): ScriptClassRootsCache {
|
||||
return GradleClassRootsCache(project, configuration) {
|
||||
configuration?.let { conf ->
|
||||
val model = conf.scriptModel(it)
|
||||
model?.toScriptConfiguration(conf.context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun replace(context: GradleKtsContext, models: List<KotlinDslScriptModel>) {
|
||||
if (models.isEmpty()) return
|
||||
|
||||
val anyScript = VfsUtil.findFile(Paths.get(models.first().file), true)!!
|
||||
|
||||
val definition = anyScript.findScriptDefinition(context.project) ?: return
|
||||
val templateClasspath = definition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()
|
||||
?.templateClasspath?.map { it.path } ?: return
|
||||
|
||||
val data = ConfigurationData(templateClasspath, models)
|
||||
KotlinDslScriptModels.write(project, data)
|
||||
|
||||
val newConfiguration = Configuration(context, data)
|
||||
configuration = newConfiguration
|
||||
|
||||
configurationChangedCallback(newConfiguration)
|
||||
}
|
||||
|
||||
private fun configurationChangedCallback(newConfiguration: Configuration) {
|
||||
rootsIndexer.transaction {
|
||||
if (classpathRoots.hasNotCachedRoots(GradleClassRootsCache.extractRoots(newConfiguration))) {
|
||||
rootsIndexer.markNewRoot()
|
||||
}
|
||||
|
||||
clearClassRootsCaches(project)
|
||||
|
||||
ScriptingSupportHelper.updateHighlighting(project) {
|
||||
configuration?.scriptModel(it) != null
|
||||
}
|
||||
}
|
||||
|
||||
hideNotificationForProjectImport(project)
|
||||
}
|
||||
|
||||
fun updateNotification(file: KtFile) {
|
||||
val vFile = file.originalFile.virtualFile
|
||||
val scriptModel = configuration?.scriptModel(vFile) ?: return
|
||||
|
||||
if (scriptModel.inputs.isUpToDate(project, vFile)) {
|
||||
hideNotificationForProjectImport(project)
|
||||
} else {
|
||||
showNotificationForProjectImport(project)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isRelated(file: VirtualFile): Boolean {
|
||||
if (isGradleKotlinScript(file)) {
|
||||
return isKotlinDslScriptsModelImportSupported(project)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isKotlinDslScriptsModelImportSupported(project: Project): Boolean {
|
||||
val gradleVersion = getGradleVersion(project)
|
||||
return gradleVersion != null && kotlinDslScriptsModelImportSupported(gradleVersion)
|
||||
}
|
||||
|
||||
override fun clearCaches() {
|
||||
// todo should clear up to date
|
||||
}
|
||||
|
||||
override fun hasCachedConfiguration(file: KtFile): Boolean =
|
||||
configuration?.scriptModel(file.originalFile.virtualFile) != null
|
||||
|
||||
override fun getOrLoadConfiguration(virtualFile: VirtualFile, preloadedKtFile: KtFile?): ScriptCompilationConfigurationWrapper? {
|
||||
val configuration = configuration
|
||||
if (configuration == null) {
|
||||
// todo: show notification "Import gradle project"
|
||||
return null
|
||||
} else {
|
||||
return classpathRoots.getScriptConfiguration(virtualFile)
|
||||
}
|
||||
}
|
||||
|
||||
override val updater: ScriptConfigurationUpdater
|
||||
get() = object : ScriptConfigurationUpdater {
|
||||
override fun ensureUpToDatedConfigurationSuggested(file: KtFile) {
|
||||
// do nothing for gradle scripts
|
||||
}
|
||||
|
||||
// unused symbol inspection should not initiate loading
|
||||
override fun ensureConfigurationUpToDate(files: List<KtFile>): Boolean = true
|
||||
|
||||
override fun suggestToUpdateConfigurationIfOutOfDate(file: KtFile) {
|
||||
updateNotification(file)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): GradleScriptingSupport {
|
||||
return SCRIPTING_SUPPORT.getPoint(project).extensionList.firstIsInstance()
|
||||
}
|
||||
}
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationNotificationFactory
|
||||
|
||||
class GradleScriptConfigurationNotificationFactory : ScriptConfigurationNotificationFactory {
|
||||
override fun showNotification(file: VirtualFile, project: Project, onClick: () -> Unit): Boolean {
|
||||
if (isGradleKotlinScript(file) && isGradleImportCanBeUsed(project)) {
|
||||
showNotificationForProjectImport(project, onClick)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hideNotification(file: VirtualFile, project: Project): Boolean {
|
||||
return hideNotificationForProjectImport(project)
|
||||
}
|
||||
|
||||
private fun isGradleImportCanBeUsed(project: Project): Boolean {
|
||||
val gradleVersion = getGradleVersion(project)
|
||||
if (gradleVersion != null && kotlinDslScriptsModelImportSupported(gradleVersion)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -5,9 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle.importing
|
||||
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.GradleKotlinScriptConfigurationInputs
|
||||
|
||||
data class KotlinDslScriptModel(
|
||||
val file: String,
|
||||
val inputsTimeStamp: Long,
|
||||
val inputs: GradleKotlinScriptConfigurationInputs,
|
||||
val classPath: List<String>,
|
||||
val sourcePath: List<String>,
|
||||
val imports: List<String>,
|
||||
|
||||
+55
-57
@@ -7,16 +7,14 @@ package org.jetbrains.kotlin.idea.scripting.gradle.importing
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.gradle.tooling.model.kotlin.dsl.EditorReportSeverity
|
||||
import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
|
||||
import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationSnapshot
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptInputsWatcher
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptingSupport
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.getGradleScriptInputsStamp
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
@@ -24,6 +22,8 @@ import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition
|
||||
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
|
||||
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
|
||||
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
@@ -40,8 +40,9 @@ fun processScriptModel(
|
||||
"Couldn't get KotlinDslScriptsModel for $projectName:\n${model.message}\n${model.stackTrace}"
|
||||
)
|
||||
} else {
|
||||
val models = model.toListOfScriptModels()
|
||||
resolverCtx.externalSystemTaskId.findProject()?.kotlinDslModels?.addAll(
|
||||
val project = resolverCtx.externalSystemTaskId.findProject() ?: return
|
||||
val models = model.toListOfScriptModels(project)
|
||||
project.kotlinDslModels.addAll(
|
||||
models
|
||||
)
|
||||
if (models.containsErrors()) {
|
||||
@@ -58,7 +59,7 @@ private fun Collection<KotlinDslScriptModel>.containsErrors(): Boolean {
|
||||
return any { it.messages.any { it.severity == KotlinDslScriptModel.Severity.ERROR } }
|
||||
}
|
||||
|
||||
private fun KotlinDslScriptsModel.toListOfScriptModels(): List<KotlinDslScriptModel> =
|
||||
private fun KotlinDslScriptsModel.toListOfScriptModels(project: Project): List<KotlinDslScriptModel> =
|
||||
scriptModels.map { (file, model) ->
|
||||
val messages = mutableListOf<KotlinDslScriptModel.Message>()
|
||||
|
||||
@@ -91,10 +92,14 @@ private fun KotlinDslScriptsModel.toListOfScriptModels(): List<KotlinDslScriptMo
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: NPE
|
||||
val virtualFile = VfsUtil.findFile(file.toPath(), true)!!
|
||||
|
||||
// todo(KT-34440): take inputs snapshot before starting import
|
||||
KotlinDslScriptModel(
|
||||
file.absolutePath,
|
||||
System.currentTimeMillis(),
|
||||
// TODO: NPE
|
||||
getGradleScriptInputsStamp(project, virtualFile)!!,
|
||||
model.classPath.map { it.absolutePath },
|
||||
model.sourcePath.map { it.absolutePath },
|
||||
model.implicitImports,
|
||||
@@ -102,69 +107,62 @@ private fun KotlinDslScriptsModel.toListOfScriptModels(): List<KotlinDslScriptMo
|
||||
)
|
||||
}
|
||||
|
||||
fun createGradleKtsContextIfPossible(project: Project): GradleKtsContext? {
|
||||
val gradleProjectSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
|
||||
.getLinkedProjectsSettings()
|
||||
.filterIsInstance<GradleProjectSettings>().firstOrNull() ?: return null
|
||||
|
||||
val gradleExeSettings = ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
|
||||
project,
|
||||
gradleProjectSettings.externalProjectPath,
|
||||
GradleConstants.SYSTEM_ID
|
||||
)
|
||||
|
||||
val javaHome = gradleExeSettings.javaHome?.let { File(it)}
|
||||
|
||||
return GradleKtsContext(project, javaHome)
|
||||
}
|
||||
|
||||
class GradleKtsContext(val project: Project, val javaHome: File?)
|
||||
|
||||
fun KotlinDslScriptModel.toScriptConfiguration(context: GradleKtsContext): ScriptCompilationConfigurationWrapper? {
|
||||
val scriptFile = File(file)
|
||||
val virtualFile = VfsUtil.findFile(scriptFile.toPath(), true)!!
|
||||
|
||||
val definition = virtualFile.findScriptDefinition(context.project) ?: return null
|
||||
|
||||
return ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(
|
||||
VirtualFileScriptSource(virtualFile),
|
||||
definition.compilationConfiguration.with {
|
||||
if (context.javaHome != null) {
|
||||
jvm.jdkHome(context.javaHome)
|
||||
}
|
||||
defaultImports(imports)
|
||||
dependencies(JvmDependency(classPath.map { File(it) }))
|
||||
ide.dependenciesSources(JvmDependency(sourcePath.map { File(it) }))
|
||||
}.adjustByDefinition(definition)
|
||||
)
|
||||
}
|
||||
|
||||
fun saveScriptModels(
|
||||
project: Project,
|
||||
task: ExternalSystemTaskId,
|
||||
javaHomeStr: String?,
|
||||
models: List<KotlinDslScriptModel>
|
||||
) {
|
||||
val scriptConfigurations = mutableListOf<Pair<VirtualFile, ScriptConfigurationSnapshot>>()
|
||||
|
||||
val errorReporter = KotlinGradleDslErrorReporter(project, task)
|
||||
|
||||
val javaHome = javaHomeStr?.let { File(it) }
|
||||
val context = GradleKtsContext(project, javaHome)
|
||||
|
||||
models.forEach { model ->
|
||||
val scriptFile = File(model.file)
|
||||
val virtualFile = VfsUtil.findFile(scriptFile.toPath(), true)!!
|
||||
|
||||
val inputs = getGradleScriptInputsStamp(
|
||||
project,
|
||||
virtualFile,
|
||||
givenTimeStamp = model.inputsTimeStamp
|
||||
)
|
||||
|
||||
val definition = virtualFile.findScriptDefinition(project) ?: return@forEach
|
||||
|
||||
val configuration =
|
||||
definition.compilationConfiguration.with {
|
||||
if (javaHome != null) {
|
||||
jvm.jdkHome(javaHome)
|
||||
}
|
||||
defaultImports(model.imports)
|
||||
dependencies(JvmDependency(model.classPath.map {
|
||||
File(
|
||||
it
|
||||
)
|
||||
}))
|
||||
ide.dependenciesSources(JvmDependency(model.sourcePath.map {
|
||||
File(
|
||||
it
|
||||
)
|
||||
}))
|
||||
}.adjustByDefinition(definition)
|
||||
|
||||
scriptConfigurations.add(
|
||||
Pair(
|
||||
virtualFile,
|
||||
ScriptConfigurationSnapshot(
|
||||
inputs
|
||||
?: CachedConfigurationInputs.OutOfDate,
|
||||
listOf(),
|
||||
ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(
|
||||
VirtualFileScriptSource(virtualFile),
|
||||
configuration,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
errorReporter.reportError(scriptFile, model)
|
||||
errorReporter.reportError(File(model.file), model)
|
||||
}
|
||||
|
||||
project.service<GradleScriptInputsWatcher>().saveGradleProjectRootsAfterImport(
|
||||
scriptConfigurations.map { it.first.parent.path }.toSet()
|
||||
models.map { File(it.file).parent }.toSet()
|
||||
)
|
||||
|
||||
project.service<ScriptConfigurationManager>().saveCompilationConfigurationAfterImport(scriptConfigurations)
|
||||
GradleScriptingSupport.getInstance(project).replace(context, models)
|
||||
project.service<GradleScriptInputsWatcher>().clearState()
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.newvfs.FileAttribute
|
||||
import org.jetbrains.kotlin.idea.core.util.readString
|
||||
import org.jetbrains.kotlin.idea.core.util.writeString
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
internal object KotlinDslScriptModels {
|
||||
private val attribute = FileAttribute("kotlin-dsl-script-models", 2, false)
|
||||
|
||||
fun read(project: Project): ConfigurationData? {
|
||||
return attribute.readAttribute(project.projectFile ?: return null)?.use {
|
||||
readKotlinDslScriptModels(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun write(project: Project, configuration: ConfigurationData) {
|
||||
attribute.writeAttribute(project.projectFile ?: return).use {
|
||||
writeKotlinDslScriptModels(it, configuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun writeKotlinDslScriptModels(output: DataOutputStream, data: ConfigurationData) {
|
||||
val strings = StringsPool.writer(output)
|
||||
strings.addStrings(data.templateClasspath)
|
||||
val scriptModels = data.models
|
||||
scriptModels.forEach {
|
||||
strings.addString(it.file)
|
||||
strings.addStrings(it.classPath)
|
||||
strings.addStrings(it.sourcePath)
|
||||
strings.addStrings(it.imports)
|
||||
}
|
||||
strings.writeHeader()
|
||||
strings.writeStringIds(data.templateClasspath)
|
||||
output.writeList(scriptModels) {
|
||||
strings.writeStringId(it.file)
|
||||
output.writeString(it.inputs.sections)
|
||||
output.writeLong(it.inputs.inputsTS)
|
||||
strings.writeStringIds(it.classPath)
|
||||
strings.writeStringIds(it.sourcePath)
|
||||
strings.writeStringIds(it.imports)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun readKotlinDslScriptModels(input: DataInputStream): ConfigurationData {
|
||||
val strings = StringsPool.reader(input)
|
||||
|
||||
val templateClasspath = strings.readStrings()
|
||||
|
||||
val models = input.readList {
|
||||
KotlinDslScriptModel(
|
||||
strings.readString(),
|
||||
GradleKotlinScriptConfigurationInputs(input.readString(), input.readLong()),
|
||||
strings.readStrings(),
|
||||
strings.readStrings(),
|
||||
strings.readStrings(),
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
|
||||
return ConfigurationData(templateClasspath, models)
|
||||
}
|
||||
|
||||
private object StringsPool {
|
||||
fun writer(output: DataOutputStream) = Writer(output)
|
||||
|
||||
class Writer(val output: DataOutputStream) {
|
||||
var freeze = false
|
||||
val ids = mutableMapOf<String, Int>()
|
||||
|
||||
fun getStringId(string: String) = ids.getOrPut(string) {
|
||||
check(!freeze)
|
||||
ids.size
|
||||
}
|
||||
|
||||
fun addString(string: String) {
|
||||
getStringId(string)
|
||||
}
|
||||
|
||||
fun addStrings(list: Collection<String>) {
|
||||
list.forEach { addString(it) }
|
||||
}
|
||||
|
||||
fun writeHeader() {
|
||||
freeze = true
|
||||
|
||||
output.writeInt(ids.size)
|
||||
|
||||
// sort for optimal performance and compression
|
||||
ids.keys.sorted().forEachIndexed { index, s ->
|
||||
ids[s] = index
|
||||
output.writeString(s)
|
||||
}
|
||||
}
|
||||
|
||||
fun writeStringId(it: String) {
|
||||
output.writeInt(getStringId(it))
|
||||
}
|
||||
|
||||
fun writeStringIds(strings: List<String>) {
|
||||
output.writeInt(strings.size)
|
||||
strings.forEach {
|
||||
writeStringId(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reader(input: DataInputStream): Reader {
|
||||
val strings = input.readList { input.readString() }
|
||||
return Reader(input, strings)
|
||||
}
|
||||
|
||||
class Reader(val input: DataInputStream, val strings: List<String>) {
|
||||
fun getString(id: Int) = strings[id]
|
||||
|
||||
fun readString() = getString(input.readInt())
|
||||
|
||||
fun readStrings(): List<String> = input.readList { readString() }
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> DataOutputStream.writeList(list: Collection<T>, write: (T) -> Unit) {
|
||||
writeInt(list.size)
|
||||
list.forEach { write(it) }
|
||||
}
|
||||
|
||||
private inline fun <T> DataInputStream.readList(read: () -> T): List<T> {
|
||||
val n = readInt()
|
||||
val result = ArrayList<T>(n)
|
||||
repeat(n) {
|
||||
result.add(read())
|
||||
}
|
||||
return result
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class KotlinDslScriptModelsTest {
|
||||
@Test
|
||||
fun write() {
|
||||
val buffer = ByteArrayOutputStream()
|
||||
|
||||
val conf = ConfigurationData(
|
||||
listOf("a", "b", "c"),
|
||||
listOf(
|
||||
KotlinDslScriptModel(
|
||||
"a",
|
||||
GradleKotlinScriptConfigurationInputs("b", 1),
|
||||
listOf("c", "a", "b"),
|
||||
listOf("b", "c", "a"),
|
||||
listOf("i", "c", "b"),
|
||||
listOf()
|
||||
),
|
||||
KotlinDslScriptModel(
|
||||
"a",
|
||||
GradleKotlinScriptConfigurationInputs("b", 1),
|
||||
listOf("c", "a", "b"),
|
||||
listOf("b", "c", "a"),
|
||||
listOf("i", "c", "b"),
|
||||
listOf()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
writeKotlinDslScriptModels(DataOutputStream(buffer), conf)
|
||||
|
||||
val restored = readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())))
|
||||
|
||||
assertEquals(conf.toString(), restored.toString())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user