Try to approximate "build.gradle.kts" script dependencies on first access

This is a hacky solution to prevent whole file being highlighted as unresolved while we wait for gradle response
This commit is contained in:
Pavel V. Talanov
2017-10-12 19:06:03 +03:00
parent 1cdac78b32
commit 320c5f6f3a
7 changed files with 79 additions and 13 deletions
@@ -45,7 +45,7 @@ data class ScriptModuleInfo(
override val moduleOrigin: ModuleOrigin override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER get() = ModuleOrigin.OTHER
val externalDependencies: ScriptDependencies? val externalDependencies: ScriptDependencies
get() = ScriptDependenciesManager.getInstance(project).getScriptDependencies(scriptFile) get() = ScriptDependenciesManager.getInstance(project).getScriptDependencies(scriptFile)
override val name: Name = Name.special("<script ${scriptFile.name} ${scriptDefinition.name}>") override val name: Name = Name.special("<script ${scriptFile.name} ${scriptDefinition.name}>")
@@ -58,6 +58,8 @@ class ScriptDependenciesCache(private val project: Project) {
NonClasspathDirectoriesScope(allLibrarySources) NonClasspathDirectoriesScope(allLibrarySources)
} }
fun <T> inspectCache(body: (Map<String, ScriptDependencies>) -> T): T = cacheLock.read { body(cache) }
private fun onChange(file: VirtualFile?) { private fun onChange(file: VirtualFile?) {
this::allScriptsClasspath.clearValue() this::allScriptsClasspath.clearValue()
this::allScriptsClasspathScope.clearValue() this::allScriptsClasspathScope.clearValue()
@@ -51,7 +51,7 @@ class ScriptDependenciesManager internal constructor(
} }
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> = toVfsRoots(cacheUpdater.getCurrentDependencies(file).classpath) fun getScriptClasspath(file: VirtualFile): List<VirtualFile> = toVfsRoots(cacheUpdater.getCurrentDependencies(file).classpath)
fun getScriptDependencies(file: VirtualFile) = cacheUpdater.getCurrentDependencies(file) fun getScriptDependencies(file: VirtualFile): ScriptDependencies = cacheUpdater.getCurrentDependencies(file)
private fun reloadScriptDefinitions() { private fun reloadScriptDefinitions() {
val def = makeScriptDefsFromTemplatesProviderExtensions( val def = makeScriptDefsFromTemplatesProviderExtensions(
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.core.script
import com.intellij.openapi.application.Application import com.intellij.openapi.application.Application
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.project.isProjectOrWorkspaceFile import com.intellij.openapi.project.isProjectOrWorkspaceFile
@@ -35,11 +34,13 @@ import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.asCoroutineDispatcher import kotlinx.coroutines.experimental.asCoroutineDispatcher
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.idea.core.util.EDT import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.idea.core.util.cancelOnDisposal import org.jetbrains.kotlin.idea.core.util.cancelOnDisposal
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.script.* import org.jetbrains.kotlin.script.*
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
@@ -77,20 +78,33 @@ internal class ScriptDependenciesUpdater(
fun getCurrentDependencies(file: VirtualFile): ScriptDependencies { fun getCurrentDependencies(file: VirtualFile): ScriptDependencies {
cache[file]?.let { return it } cache[file]?.let { return it }
tryLoadingFromDisk(file) val loaded = tryLoadingFromDisk(file)
if (!loaded) {
tryUsingDefault(file)
}
updateCache(listOf(file)) updateCache(listOf(file))
return cache[file] ?: ScriptDependencies.Empty return cache[file] ?: ScriptDependencies.Empty
} }
private fun tryLoadingFromDisk(file: VirtualFile) { private fun tryUsingDefault(file: VirtualFile) {
ScriptDependenciesFileAttribute.read(file)?.let { deserialized -> val defaults = DefaultScriptDependenciesProvider.getInstances(project).firstNotNullResult { it.defaultDependenciesFor(file) } ?: return
val rootsChanged = cache.hasNotCachedRoots(deserialized) saveToCache(defaults, file)
cache.save(file, deserialized) }
if (rootsChanged) {
notifyRootsChanged() private fun tryLoadingFromDisk(file: VirtualFile): Boolean {
} val deserializedDependencies = ScriptDependenciesFileAttribute.read(file) ?: return false
saveToCache(deserializedDependencies, file)
return true
}
private fun saveToCache(deserialized: ScriptDependencies, file: VirtualFile) {
val rootsChanged = cache.hasNotCachedRoots(deserialized)
cache.save(file, deserialized)
if (rootsChanged) {
notifyRootsChanged()
} }
} }
@@ -281,3 +295,13 @@ private object TimeStamps {
@set: TestOnly @set: TestOnly
var Application.isScriptDependenciesUpdaterDisabled by NotNullableUserDataProperty(Key.create("SCRIPT_DEPENDENCIES_UPDATER_DISABLED"), false) var Application.isScriptDependenciesUpdaterDisabled by NotNullableUserDataProperty(Key.create("SCRIPT_DEPENDENCIES_UPDATER_DISABLED"), false)
interface DefaultScriptDependenciesProvider {
fun defaultDependenciesFor(scriptFile: VirtualFile): ScriptDependencies?
companion object : ProjectExtensionDescriptor<DefaultScriptDependenciesProvider>(
"org.jetbrains.kotlin.defaultScriptDependenciesProvider",
DefaultScriptDependenciesProvider::class.java
)
}
@@ -20,7 +20,9 @@ import com.intellij.execution.configurations.CommandLineTokenizer
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.gradle.tooling.ProjectConnection import org.gradle.tooling.ProjectConnection
import org.jetbrains.kotlin.gradle.kdsl.DEFAULT_SCRIPT_NAME
import org.jetbrains.kotlin.lexer.KotlinLexer import org.jetbrains.kotlin.lexer.KotlinLexer
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.script.ScriptTemplatesProvider import org.jetbrains.kotlin.script.ScriptTemplatesProvider
@@ -28,6 +30,7 @@ import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
import java.io.File import java.io.File
import java.util.* import java.util.*
import kotlin.script.experimental.dependencies.ScriptDependencies
abstract class AbstractGradleScriptTemplatesProvider( abstract class AbstractGradleScriptTemplatesProvider(
project: Project, override val id: String, private val templateClass: String, private val dependencySelector: Regex project: Project, override val id: String, private val templateClass: String, private val dependencySelector: Regex
@@ -165,3 +168,36 @@ fun topLevelSectionCodeTextTokens(script: CharSequence, sectionIdentifier: Strin
TopLevelSectionTokensEnumerator(script, sectionIdentifier).asSequence() TopLevelSectionTokensEnumerator(script, sectionIdentifier).asSequence()
.filter { it.tokenType !in KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET } .filter { it.tokenType !in KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET }
.map { it.tokenSequence } .map { it.tokenSequence }
private const val KOTLIN_BUILD_FILE_NAME = DEFAULT_SCRIPT_NAME
class GradleScriptDefaultDependenciesProvider(
private val scriptDependenciesCache: ScriptDependenciesCache
) : DefaultScriptDependenciesProvider {
override fun defaultDependenciesFor(scriptFile: VirtualFile): ScriptDependencies? {
if (scriptFile.name != KOTLIN_BUILD_FILE_NAME) return null
return previouslyAnalyzedScriptsCombinedDependencies().takeUnless { it.classpath.isEmpty() }
}
private fun previouslyAnalyzedScriptsCombinedDependencies(): ScriptDependencies {
val sources = mutableListOf<File>()
val binaries = mutableListOf<File>()
val imports = mutableListOf<String>()
scriptDependenciesCache.inspectCache { cache ->
cache.entries.filter { it.key.endsWith(KOTLIN_BUILD_FILE_NAME) }
.map { it.value }.forEach {
sources += it.sources
binaries += it.classpath
imports += it.imports
}
}
return ScriptDependencies(
classpath = binaries.distinct(),
sources = sources.distinct(),
imports = imports.distinct()
)
}
}
+3
View File
@@ -51,5 +51,8 @@
interface="com.intellij.ide.util.projectWizard.ModuleBuilder"/> interface="com.intellij.ide.util.projectWizard.ModuleBuilder"/>
<extensionPoint name="gradleFrameworkSupport" <extensionPoint name="gradleFrameworkSupport"
interface="org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleFrameworkSupportProvider"/> interface="org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleFrameworkSupportProvider"/>
<extensionPoint name="defaultScriptDependenciesProvider"
interface="org.jetbrains.kotlin.idea.core.script.DefaultScriptDependenciesProvider"
area="IDEA_PROJECT"/>
</extensionPoints> </extensionPoints>
</idea-plugin> </idea-plugin>
+1
View File
@@ -49,6 +49,7 @@
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.allopen.ide.AllOpenGradleProjectImportHandler"/> <gradleProjectImportHandler implementation="org.jetbrains.kotlin.allopen.ide.AllOpenGradleProjectImportHandler"/>
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.noarg.ide.NoArgGradleProjectImportHandler"/> <gradleProjectImportHandler implementation="org.jetbrains.kotlin.noarg.ide.NoArgGradleProjectImportHandler"/>
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.samWithReceiver.ide.SamWithReceiverGradleProjectImportHandler"/> <gradleProjectImportHandler implementation="org.jetbrains.kotlin.samWithReceiver.ide.SamWithReceiverGradleProjectImportHandler"/>
<defaultScriptDependenciesProvider implementation="org.jetbrains.kotlin.idea.core.script.GradleScriptDefaultDependenciesProvider"/>
<projectConfigurator implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleModuleConfigurator"/> <projectConfigurator implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleModuleConfigurator"/>
<projectConfigurator implementation="org.jetbrains.kotlin.idea.configuration.KotlinJsGradleModuleConfigurator"/> <projectConfigurator implementation="org.jetbrains.kotlin.idea.configuration.KotlinJsGradleModuleConfigurator"/>