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
get() = ModuleOrigin.OTHER
val externalDependencies: ScriptDependencies?
val externalDependencies: ScriptDependencies
get() = ScriptDependenciesManager.getInstance(project).getScriptDependencies(scriptFile)
override val name: Name = Name.special("<script ${scriptFile.name} ${scriptDefinition.name}>")
@@ -58,6 +58,8 @@ class ScriptDependenciesCache(private val project: Project) {
NonClasspathDirectoriesScope(allLibrarySources)
}
fun <T> inspectCache(body: (Map<String, ScriptDependencies>) -> T): T = cacheLock.read { body(cache) }
private fun onChange(file: VirtualFile?) {
this::allScriptsClasspath.clearValue()
this::allScriptsClasspathScope.clearValue()
@@ -51,7 +51,7 @@ class ScriptDependenciesManager internal constructor(
}
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() {
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.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.isProjectOrWorkspaceFile
@@ -35,11 +34,13 @@ import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.asCoroutineDispatcher
import kotlinx.coroutines.experimental.launch
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.cancelOnDisposal
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.script.*
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
@@ -77,20 +78,33 @@ internal class ScriptDependenciesUpdater(
fun getCurrentDependencies(file: VirtualFile): ScriptDependencies {
cache[file]?.let { return it }
tryLoadingFromDisk(file)
val loaded = tryLoadingFromDisk(file)
if (!loaded) {
tryUsingDefault(file)
}
updateCache(listOf(file))
return cache[file] ?: ScriptDependencies.Empty
}
private fun tryLoadingFromDisk(file: VirtualFile) {
ScriptDependenciesFileAttribute.read(file)?.let { deserialized ->
val rootsChanged = cache.hasNotCachedRoots(deserialized)
cache.save(file, deserialized)
if (rootsChanged) {
notifyRootsChanged()
}
private fun tryUsingDefault(file: VirtualFile) {
val defaults = DefaultScriptDependenciesProvider.getInstances(project).firstNotNullResult { it.defaultDependenciesFor(file) } ?: return
saveToCache(defaults, file)
}
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()
}
}
@@ -280,4 +294,14 @@ private object TimeStamps {
}
@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.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
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.KtTokens
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 java.io.File
import java.util.*
import kotlin.script.experimental.dependencies.ScriptDependencies
abstract class AbstractGradleScriptTemplatesProvider(
project: Project, override val id: String, private val templateClass: String, private val dependencySelector: Regex
@@ -164,4 +167,37 @@ class TopLevelSectionTokensEnumerator(script: CharSequence, identifier: String)
fun topLevelSectionCodeTextTokens(script: CharSequence, sectionIdentifier: String): Sequence<CharSequence> =
TopLevelSectionTokensEnumerator(script, sectionIdentifier).asSequence()
.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"/>
<extensionPoint name="gradleFrameworkSupport"
interface="org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleFrameworkSupportProvider"/>
<extensionPoint name="defaultScriptDependenciesProvider"
interface="org.jetbrains.kotlin.idea.core.script.DefaultScriptDependenciesProvider"
area="IDEA_PROJECT"/>
</extensionPoints>
</idea-plugin>
+1
View File
@@ -49,6 +49,7 @@
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.allopen.ide.AllOpenGradleProjectImportHandler"/>
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.noarg.ide.NoArgGradleProjectImportHandler"/>
<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.KotlinJsGradleModuleConfigurator"/>