From 6e26af40fcf27124dd2fcff45c285d8e3e40971a Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Sep 2019 03:21:30 +0200 Subject: [PATCH 01/31] Add task for fetching Gradle's runtime dependencies --- server/src/main/resources/classpathFinder.gradle | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/server/src/main/resources/classpathFinder.gradle b/server/src/main/resources/classpathFinder.gradle index 1457c3420..ecc244c06 100644 --- a/server/src/main/resources/classpathFinder.gradle +++ b/server/src/main/resources/classpathFinder.gradle @@ -1,6 +1,6 @@ allprojects { project -> - task kotlinLSPDeps { - task -> doLast { + task kotlinProjectDeps { task -> + doLast { System.out.println "" System.out.println "gradle-version $gradleVersion" System.out.println "kotlin-lsp-project ${project.name}" @@ -41,7 +41,7 @@ allprojects { project -> }.each { it.resolve().each { def inspected = it.inspect() - + if (inspected.endsWith("jar")) { if (!inspected.contains("zip!")) { System.out.println "kotlin-lsp-gradle $it" @@ -61,4 +61,10 @@ allprojects { project -> } } } + + task kotlinGradleDeps { + doLast { + System.out.println "kotlin-lsp-gradle $gradle.gradleHomeDir/lib" + } + } } From 58718e3ff127c71fe5bb6e84af273ce482e9fe94 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Sep 2019 03:34:20 +0200 Subject: [PATCH 02/31] Add Gradle runtime libraries for Kotlin DSL projects --- .../kt/classpath/GradleClassPathResolver.kt | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt index d176f4a1a..8b6bb5a8a 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt @@ -12,12 +12,12 @@ import java.nio.file.FileSystems import java.nio.file.Files import java.nio.file.Path -internal class GradleClassPathResolver(private val path: Path) : ClassPathResolver { +internal class GradleClassPathResolver(private val path: Path, private val includeGradleLibs: Boolean): ClassPathResolver { override val resolverType: String = "Gradle" override val classpath: Set get() { val projectDirectory = path.getParent() - return readDependenciesViaGradleCLI(projectDirectory) - .orEmpty() + val tasks = listOf("kotlinProjectDeps") + (if (includeGradleLibs) listOf("kotlinGradleDeps") else emptySet()) + return readDependenciesViaGradleCLI(projectDirectory, tasks) .apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") } } @@ -25,7 +25,7 @@ internal class GradleClassPathResolver(private val path: Path) : ClassPathResolv /** Create a Gradle resolver if a file is a pom. */ fun maybeCreate(file: Path): GradleClassPathResolver? = file.takeIf { file.endsWith("build.gradle") || file.endsWith("build.gradle.kts") } - ?.let { GradleClassPathResolver(it) } + ?.let { GradleClassPathResolver(it, file.endsWith(".kts")) } } } @@ -58,17 +58,18 @@ private fun getGradleCommand(workspace: Path): Path { } } -private fun readDependenciesViaGradleCLI(projectDirectory: Path): Set? { +private fun readDependenciesViaGradleCLI(projectDirectory: Path, gradleTasks: List): Set { LOG.info("Resolving dependencies for '{}' through Gradle's CLI...", projectDirectory.fileName) - val config = createTemporaryGradleFile(deleteOnExit = false) + val tmpFile = createTemporaryGradleFile(deleteOnExit = false).toPath() val gradle = getGradleCommand(projectDirectory) - val cmd = "$gradle -I ${config.absolutePath} kotlinLSPDeps --console=plain" - LOG.debug(" -- executing {}", cmd) - val dependencies = findGradleCLIDependencies(cmd, projectDirectory) - config.delete() + val dependencies = gradleTasks.flatMap { queryGradleCLIDependencies(gradle, tmpFile, it, projectDirectory).orEmpty() }.toSet() + Files.delete(tmpFile) return dependencies } +private fun queryGradleCLIDependencies(gradle: Path, tmpFile: Path, task: String, projectDirectory: Path): Set? = + findGradleCLIDependencies("$gradle -I ${tmpFile.toAbsolutePath()} $task --console=plain", projectDirectory) + private fun findGradleCLIDependencies(command: String, projectDirectory: Path): Set? { val result = execAndReadStdout(command, projectDirectory) LOG.debug(result) From 2c4e02af095b7b9c6a75fa9301c051828bb1f4e4 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Sep 2019 04:25:17 +0200 Subject: [PATCH 03/31] List all Gradle dependency JARs in classpath finder --- server/src/main/resources/classpathFinder.gradle | 8 +++++--- .../javacs/kt/classpath/GradleClassPathResolver.kt | 14 ++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/server/src/main/resources/classpathFinder.gradle b/server/src/main/resources/classpathFinder.gradle index ecc244c06..7dbef3d1e 100644 --- a/server/src/main/resources/classpathFinder.gradle +++ b/server/src/main/resources/classpathFinder.gradle @@ -1,5 +1,5 @@ allprojects { project -> - task kotlinProjectDeps { task -> + task kotlinLSPProjectDeps { task -> doLast { System.out.println "" System.out.println "gradle-version $gradleVersion" @@ -62,9 +62,11 @@ allprojects { project -> } } - task kotlinGradleDeps { + task kotlinLSPGradleDeps { doLast { - System.out.println "kotlin-lsp-gradle $gradle.gradleHomeDir/lib" + fileTree("$gradle.gradleHomeDir/lib") + .findAll { it.toString().endsWith '.jar' } + .forEach { System.out.println "kotlin-lsp-gradle $it" } } } } diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt index 8b6bb5a8a..04acf6004 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt @@ -8,15 +8,15 @@ import org.javacs.kt.util.KotlinLSException import org.javacs.kt.util.isOSWindows import org.javacs.kt.util.findCommandOnPath import java.io.File -import java.nio.file.FileSystems import java.nio.file.Files import java.nio.file.Path +import java.nio.file.Paths internal class GradleClassPathResolver(private val path: Path, private val includeGradleLibs: Boolean): ClassPathResolver { override val resolverType: String = "Gradle" override val classpath: Set get() { val projectDirectory = path.getParent() - val tasks = listOf("kotlinProjectDeps") + (if (includeGradleLibs) listOf("kotlinGradleDeps") else emptySet()) + val tasks = listOf("kotlinLSPProjectDeps") + (if (includeGradleLibs) listOf("kotlinLSPGradleDeps") else emptySet()) return readDependenciesViaGradleCLI(projectDirectory, tasks) .apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") } } @@ -25,7 +25,7 @@ internal class GradleClassPathResolver(private val path: Path, private val inclu /** Create a Gradle resolver if a file is a pom. */ fun maybeCreate(file: Path): GradleClassPathResolver? = file.takeIf { file.endsWith("build.gradle") || file.endsWith("build.gradle.kts") } - ?.let { GradleClassPathResolver(it, file.endsWith(".kts")) } + ?.let { GradleClassPathResolver(it, file.toString().endsWith(".kts")) } } } @@ -59,7 +59,7 @@ private fun getGradleCommand(workspace: Path): Path { } private fun readDependenciesViaGradleCLI(projectDirectory: Path, gradleTasks: List): Set { - LOG.info("Resolving dependencies for '{}' through Gradle's CLI...", projectDirectory.fileName) + LOG.info("Resolving dependencies for '{}' through Gradle's CLI using tasks {}...", projectDirectory.fileName, gradleTasks) val tmpFile = createTemporaryGradleFile(deleteOnExit = false).toPath() val gradle = getGradleCommand(projectDirectory) val dependencies = gradleTasks.flatMap { queryGradleCLIDependencies(gradle, tmpFile, it, projectDirectory).orEmpty() }.toSet() @@ -69,6 +69,7 @@ private fun readDependenciesViaGradleCLI(projectDirectory: Path, gradleTasks: Li private fun queryGradleCLIDependencies(gradle: Path, tmpFile: Path, task: String, projectDirectory: Path): Set? = findGradleCLIDependencies("$gradle -I ${tmpFile.toAbsolutePath()} $task --console=plain", projectDirectory) + ?.also { LOG.debug("Classpath for task {}", it) } private fun findGradleCLIDependencies(command: String, projectDirectory: Path): Set? { val result = execAndReadStdout(command, projectDirectory) @@ -76,11 +77,12 @@ private fun findGradleCLIDependencies(command: String, projectDirectory: Path): return parseGradleCLIDependencies(result) } -private val artifactPattern by lazy { "kotlin-lsp-gradle (.+)(\r?\n)".toRegex() } +private val artifactPattern by lazy { "kotlin-lsp-gradle (.+)(?:\r?\n)".toRegex() } private fun parseGradleCLIDependencies(output: String): Set? { + LOG.debug(output) val artifacts = artifactPattern.findAll(output) - .mapNotNull { FileSystems.getDefault().getPath(it.groups[1]?.value) } + .mapNotNull { Paths.get(it.groups[1]?.value) } .filterNotNull() return artifacts.toSet() } From 68ccda0159b6e47ee10add79a263b0b92fceac91 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Sep 2019 18:20:22 +0200 Subject: [PATCH 04/31] Load build script template in Compiler --- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 9c7fcdd57..697011bd7 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots +import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration as KotlinCompilerConfiguration import org.jetbrains.kotlin.config.JVMConfigurationKeys @@ -33,26 +34,29 @@ import org.jetbrains.kotlin.resolve.calls.components.InferenceSession import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import org.jetbrains.kotlin.resolve.scopes.LexicalScope -import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys -import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider -import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDependenciesProvider import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar -import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition +import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition +import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices import org.jetbrains.kotlin.util.KotlinFrontEndException +import org.jetbrains.kotlin.utils.PathUtil import java.io.Closeable +import java.io.File import java.nio.file.Path import java.nio.file.Paths +import java.net.URLClassLoader import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock import org.javacs.kt.util.KotlinLSException import org.javacs.kt.util.KotlinNullableNotNullManager import org.javacs.kt.util.LoggingMessageCollector +private val GRADLE_DSL_DEPENDENCY_PATTERN = Regex("^gradle-(?:kotlin-dsl|core).*\\.jar$") + /** * Kotlin compiler APIs used to parse, analyze and compile * files and expressions. @@ -74,8 +78,28 @@ private class CompilationEnvironment( put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME) put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector) add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) - add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) addJvmClasspathRoots(classPath.map { it.toFile() }) + + // Setup script templates + val scriptDefinitions: List + + if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { + LOG.info("Configuring Kotlin DSL script templates...") + val scriptTemplates = listOf( + // "org.gradle.kotlin.dsl.KotlinInitScript", + // "org.gradle.kotlin.dsl.KotlinSettingsScript", + "org.gradle.kotlin.dsl.KotlinBuildScript" + ) + // Load templates + val baseClassLoader = CompilationEnvironment::class.java.classLoader + val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray(), baseClassLoader) + // TODO: Use org.jetbrains.kotlin.scripting.definitions.ScriptDefinition instead of + // KotlinScriptDefinition since the latter will be deprecated soon. + scriptDefinitions = scriptTemplates.map { KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin) } + } else { + scriptDefinitions = listOf(StandardScriptDefinition) + } + addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) }, configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES ) @@ -155,7 +179,7 @@ class Compiler(classPath: Set) : Closeable { compileEnvironment.updateConfiguration(config) } - fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtFile { + fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtFile { assert(!content.contains('\r')) val new = psiFileFactory.createFileFromText(file.toString(), KotlinLanguage.INSTANCE, content, true, false) as KtFile @@ -164,13 +188,13 @@ class Compiler(classPath: Set) : Closeable { return new } - fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtExpression { + fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtExpression { val property = parseDeclaration("val x = $content", file) as KtProperty return property.initializer!! } - fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtDeclaration = + fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtDeclaration = parseDeclaration(content, file) private fun parseDeclaration(content: String, file: Path): KtDeclaration { From b48aca43bffd252c725e5ba427a5577a0a79676b Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 00:12:53 +0200 Subject: [PATCH 05/31] Create separate task for finding Kotlin DSL libs Include Gradle API, Kotlin DSL jar and generated extensions. --- server/src/main/resources/classpathFinder.gradle | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/src/main/resources/classpathFinder.gradle b/server/src/main/resources/classpathFinder.gradle index 7dbef3d1e..fd1892a64 100644 --- a/server/src/main/resources/classpathFinder.gradle +++ b/server/src/main/resources/classpathFinder.gradle @@ -62,11 +62,20 @@ allprojects { project -> } } - task kotlinLSPGradleDeps { + task kotlinLSPAllGradleDeps { doLast { fileTree("$gradle.gradleHomeDir/lib") .findAll { it.toString().endsWith '.jar' } .forEach { System.out.println "kotlin-lsp-gradle $it" } } } + + task kotlinLSPKotlinDSLDeps { + def pattern = ~/^(?:gradle-(?:kotlin-dsl|core)|kotlin-(?:compiler|stdlib)).*\.jar/ + doLast { + (fileTree("$gradle.gradleHomeDir/lib") + fileTree("$gradle.gradleUserHomeDir/caches/$gradle.gradleVersion/generated-gradle-jars")) + .findAll { it.name =~ pattern } + .forEach { System.out.println "kotlin-lsp-gradle $it.name" } + } + } } From 260db736efbe571230007e10d4a7758eb15b87a8 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 00:35:30 +0200 Subject: [PATCH 06/31] Use new Gradle tasks in classpath resolver Also omit the base classloader when loading the Kotlin script template classes at runtime. --- server/src/main/kotlin/org/javacs/kt/Compiler.kt | 7 ++++--- .../org/javacs/kt/classpath/GradleClassPathResolver.kt | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 697011bd7..2c493099f 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -85,14 +85,15 @@ private class CompilationEnvironment( if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { LOG.info("Configuring Kotlin DSL script templates...") + val scriptTemplates = listOf( // "org.gradle.kotlin.dsl.KotlinInitScript", // "org.gradle.kotlin.dsl.KotlinSettingsScript", "org.gradle.kotlin.dsl.KotlinBuildScript" ) - // Load templates - val baseClassLoader = CompilationEnvironment::class.java.classLoader - val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray(), baseClassLoader) + + // Load template classes + val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) // TODO: Use org.jetbrains.kotlin.scripting.definitions.ScriptDefinition instead of // KotlinScriptDefinition since the latter will be deprecated soon. scriptDefinitions = scriptTemplates.map { KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin) } diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt index 04acf6004..03e8c1ffd 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt @@ -12,11 +12,11 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -internal class GradleClassPathResolver(private val path: Path, private val includeGradleLibs: Boolean): ClassPathResolver { +internal class GradleClassPathResolver(private val path: Path, private val includeKotlinDSL: Boolean): ClassPathResolver { override val resolverType: String = "Gradle" override val classpath: Set get() { val projectDirectory = path.getParent() - val tasks = listOf("kotlinLSPProjectDeps") + (if (includeGradleLibs) listOf("kotlinLSPGradleDeps") else emptySet()) + val tasks = listOf("kotlinLSPProjectDeps") + (if (includeKotlinDSL) listOf("kotlinLSPKotlinDSLDeps") else emptySet()) return readDependenciesViaGradleCLI(projectDirectory, tasks) .apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") } } @@ -25,7 +25,7 @@ internal class GradleClassPathResolver(private val path: Path, private val inclu /** Create a Gradle resolver if a file is a pom. */ fun maybeCreate(file: Path): GradleClassPathResolver? = file.takeIf { file.endsWith("build.gradle") || file.endsWith("build.gradle.kts") } - ?.let { GradleClassPathResolver(it, file.toString().endsWith(".kts")) } + ?.let { GradleClassPathResolver(it, includeKotlinDSL = file.toString().endsWith(".kts")) } } } From 1f1c3e2ba782011165b3d1afed4c85e0494a711f Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 00:59:14 +0200 Subject: [PATCH 07/31] Catch class loader exceptions in Compiler --- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 2c493099f..97d44d313 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -81,7 +81,7 @@ private class CompilationEnvironment( addJvmClasspathRoots(classPath.map { it.toFile() }) // Setup script templates - val scriptDefinitions: List + var scriptDefinitions: List = listOf(StandardScriptDefinition) if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { LOG.info("Configuring Kotlin DSL script templates...") @@ -92,13 +92,16 @@ private class CompilationEnvironment( "org.gradle.kotlin.dsl.KotlinBuildScript" ) - // Load template classes - val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) - // TODO: Use org.jetbrains.kotlin.scripting.definitions.ScriptDefinition instead of - // KotlinScriptDefinition since the latter will be deprecated soon. - scriptDefinitions = scriptTemplates.map { KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin) } - } else { - scriptDefinitions = listOf(StandardScriptDefinition) + try { + // Load template classes + val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) + // TODO: Use org.jetbrains.kotlin.scripting.definitions.ScriptDefinition instead of + // KotlinScriptDefinition since the latter will be deprecated soon. + scriptDefinitions = scriptTemplates.map { KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin) } + } catch (e: Exception) { + LOG.error("Error while loading script template classes") + LOG.printStackTrace(e) + } } addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) }, From f9cb99e785f259cdc85c90b9e64ee8445b8d6d30 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 01:02:56 +0200 Subject: [PATCH 08/31] Print entire path in kotlinLSPKotlinDSLDeps --- server/src/main/resources/classpathFinder.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/resources/classpathFinder.gradle b/server/src/main/resources/classpathFinder.gradle index fd1892a64..6134a1afe 100644 --- a/server/src/main/resources/classpathFinder.gradle +++ b/server/src/main/resources/classpathFinder.gradle @@ -75,7 +75,7 @@ allprojects { project -> doLast { (fileTree("$gradle.gradleHomeDir/lib") + fileTree("$gradle.gradleUserHomeDir/caches/$gradle.gradleVersion/generated-gradle-jars")) .findAll { it.name =~ pattern } - .forEach { System.out.println "kotlin-lsp-gradle $it.name" } + .forEach { System.out.println "kotlin-lsp-gradle $it" } } } } From 2fef42d68f927412c5c2bf8038b2fac212c73c6d Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 01:08:16 +0200 Subject: [PATCH 09/31] Run all Gradle tasks in a single execution --- .../org/javacs/kt/classpath/GradleClassPathResolver.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt index 03e8c1ffd..43d4ce084 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt @@ -62,15 +62,14 @@ private fun readDependenciesViaGradleCLI(projectDirectory: Path, gradleTasks: Li LOG.info("Resolving dependencies for '{}' through Gradle's CLI using tasks {}...", projectDirectory.fileName, gradleTasks) val tmpFile = createTemporaryGradleFile(deleteOnExit = false).toPath() val gradle = getGradleCommand(projectDirectory) - val dependencies = gradleTasks.flatMap { queryGradleCLIDependencies(gradle, tmpFile, it, projectDirectory).orEmpty() }.toSet() + val command = "$gradle -I ${tmpFile.toAbsolutePath()} ${gradleTasks.joinToString(separator = " ")} --console=plain" + val dependencies = findGradleCLIDependencies(command, projectDirectory) + ?.also { LOG.debug("Classpath for task {}", it) } + .orEmpty() Files.delete(tmpFile) return dependencies } -private fun queryGradleCLIDependencies(gradle: Path, tmpFile: Path, task: String, projectDirectory: Path): Set? = - findGradleCLIDependencies("$gradle -I ${tmpFile.toAbsolutePath()} $task --console=plain", projectDirectory) - ?.also { LOG.debug("Classpath for task {}", it) } - private fun findGradleCLIDependencies(command: String, projectDirectory: Path): Set? { val result = execAndReadStdout(command, projectDirectory) LOG.debug(result) From 8f48d8c0bacf0305d34d89e3b5fd610a828db882 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 01:52:49 +0200 Subject: [PATCH 10/31] List Kotlin DSL accessors and split into two classpath finders Print dynamically generated Kotlin DSL accessors (e.g. the 'compile' configuration method) when resolving the classpath of Kotlin DSL projects. Also move the Kotlin DSL classpath resolution logic into a separate script. --- .../resources/kotlinDSLClassPathFinder.gradle | 18 ++++++++++++++++ ...r.gradle => projectClassPathFinder.gradle} | 9 -------- .../kt/classpath/GradleClassPathResolver.kt | 21 ++++++++++++------- 3 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 server/src/main/resources/kotlinDSLClassPathFinder.gradle rename server/src/main/resources/{classpathFinder.gradle => projectClassPathFinder.gradle} (85%) diff --git a/server/src/main/resources/kotlinDSLClassPathFinder.gradle b/server/src/main/resources/kotlinDSLClassPathFinder.gradle new file mode 100644 index 000000000..90072c87a --- /dev/null +++ b/server/src/main/resources/kotlinDSLClassPathFinder.gradle @@ -0,0 +1,18 @@ +import org.gradle.kotlin.dsl.accessors.AccessorsClassPathKt +import org.gradle.internal.classpath.ClassPath + +allprojects { project -> + task kotlinLSPKotlinDSLDeps { + def pattern = ~/^(?:gradle-(?:kotlin-dsl|core)|kotlin-(?:compiler|stdlib)).*\.jar/ + doLast { + (fileTree("$gradle.gradleHomeDir/lib") + fileTree("$gradle.gradleUserHomeDir/caches/$gradle.gradleVersion/generated-gradle-jars")) + .findAll { it.name =~ pattern } + .forEach { System.out.println "kotlin-lsp-gradle $it" } + + // List dynamically generated Kotlin DSL accessors (e.g. the 'compile' configuration method) + def accessors = AccessorsClassPathKt.projectAccessorsClassPath(project, ClassPath.EMPTY) + accessors.bin.asFiles + .forEach { System.out.println "kotlin-lsp-gradle $it" } + } + } +} diff --git a/server/src/main/resources/classpathFinder.gradle b/server/src/main/resources/projectClassPathFinder.gradle similarity index 85% rename from server/src/main/resources/classpathFinder.gradle rename to server/src/main/resources/projectClassPathFinder.gradle index 6134a1afe..6e4d9a0bc 100644 --- a/server/src/main/resources/classpathFinder.gradle +++ b/server/src/main/resources/projectClassPathFinder.gradle @@ -69,13 +69,4 @@ allprojects { project -> .forEach { System.out.println "kotlin-lsp-gradle $it" } } } - - task kotlinLSPKotlinDSLDeps { - def pattern = ~/^(?:gradle-(?:kotlin-dsl|core)|kotlin-(?:compiler|stdlib)).*\.jar/ - doLast { - (fileTree("$gradle.gradleHomeDir/lib") + fileTree("$gradle.gradleUserHomeDir/caches/$gradle.gradleVersion/generated-gradle-jars")) - .findAll { it.name =~ pattern } - .forEach { System.out.println "kotlin-lsp-gradle $it" } - } - } } diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt index 43d4ce084..593ff5671 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt @@ -16,8 +16,10 @@ internal class GradleClassPathResolver(private val path: Path, private val inclu override val resolverType: String = "Gradle" override val classpath: Set get() { val projectDirectory = path.getParent() - val tasks = listOf("kotlinLSPProjectDeps") + (if (includeKotlinDSL) listOf("kotlinLSPKotlinDSLDeps") else emptySet()) - return readDependenciesViaGradleCLI(projectDirectory, tasks) + val scripts = listOf("projectClassPathFinder.gradle") + listOf("kotlinDSLClassPathFinder.gradle").takeIf { includeKotlinDSL }.orEmpty() + val tasks = listOf("kotlinLSPProjectDeps") + listOf("kotlinLSPKotlinDSLDeps").takeIf { includeKotlinDSL }.orEmpty() + + return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks) .apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") } } @@ -29,7 +31,7 @@ internal class GradleClassPathResolver(private val path: Path, private val inclu } } -private fun createTemporaryGradleFile(deleteOnExit: Boolean = false): File { +private fun gradleScriptToTempFile(scriptName: String, deleteOnExit: Boolean = false): File { val config = File.createTempFile("classpath", ".gradle") if (deleteOnExit) { config.deleteOnExit() @@ -38,7 +40,7 @@ private fun createTemporaryGradleFile(deleteOnExit: Boolean = false): File { LOG.debug("Creating temporary gradle file {}", config.absolutePath) config.bufferedWriter().use { configWriter -> - ClassLoader.getSystemResourceAsStream("classpathFinder.gradle").bufferedReader().use { configReader -> + ClassLoader.getSystemResourceAsStream(scriptName).bufferedReader().use { configReader -> configReader.copyTo(configWriter) } } @@ -58,15 +60,18 @@ private fun getGradleCommand(workspace: Path): Path { } } -private fun readDependenciesViaGradleCLI(projectDirectory: Path, gradleTasks: List): Set { +private fun readDependenciesViaGradleCLI(projectDirectory: Path, gradleScripts: List, gradleTasks: List): Set { LOG.info("Resolving dependencies for '{}' through Gradle's CLI using tasks {}...", projectDirectory.fileName, gradleTasks) - val tmpFile = createTemporaryGradleFile(deleteOnExit = false).toPath() + + val tmpScripts = gradleScripts.map { gradleScriptToTempFile(it, deleteOnExit = false).toPath().toAbsolutePath() } val gradle = getGradleCommand(projectDirectory) - val command = "$gradle -I ${tmpFile.toAbsolutePath()} ${gradleTasks.joinToString(separator = " ")} --console=plain" + + val command = "$gradle ${tmpScripts.map { "-I $it" }.joinToString(" ")} ${gradleTasks.joinToString(" ")} --console=plain" val dependencies = findGradleCLIDependencies(command, projectDirectory) ?.also { LOG.debug("Classpath for task {}", it) } .orEmpty() - Files.delete(tmpFile) + + tmpScripts.forEach(Files::delete) return dependencies } From b4a1467593456438a9bdbb27cbc592801243ac1d Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 02:01:34 +0200 Subject: [PATCH 11/31] Add Gradle's base-services to the Kotlin DSL classpath --- server/src/main/resources/kotlinDSLClassPathFinder.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/resources/kotlinDSLClassPathFinder.gradle b/server/src/main/resources/kotlinDSLClassPathFinder.gradle index 90072c87a..92d25ff9f 100644 --- a/server/src/main/resources/kotlinDSLClassPathFinder.gradle +++ b/server/src/main/resources/kotlinDSLClassPathFinder.gradle @@ -3,7 +3,7 @@ import org.gradle.internal.classpath.ClassPath allprojects { project -> task kotlinLSPKotlinDSLDeps { - def pattern = ~/^(?:gradle-(?:kotlin-dsl|core)|kotlin-(?:compiler|stdlib)).*\.jar/ + def pattern = ~/^(?:gradle-(?:kotlin-dsl|base-services|core)|kotlin-(?:compiler|stdlib)).*\.jar/ doLast { (fileTree("$gradle.gradleHomeDir/lib") + fileTree("$gradle.gradleUserHomeDir/caches/$gradle.gradleVersion/generated-gradle-jars")) .findAll { it.name =~ pattern } From 66c850d8e621f7ce6f5cfedebeafed0ec9578a05 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 02:35:17 +0200 Subject: [PATCH 12/31] Separate classpath from build script classpath --- .../resources/kotlinDSLClassPathFinder.gradle | 3 ++- .../javacs/kt/classpath/ClassPathResolver.kt | 15 +++++++++++++++ .../kt/classpath/GradleClassPathResolver.kt | 17 ++++++++++++++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/server/src/main/resources/kotlinDSLClassPathFinder.gradle b/server/src/main/resources/kotlinDSLClassPathFinder.gradle index 92d25ff9f..f1df027f6 100644 --- a/server/src/main/resources/kotlinDSLClassPathFinder.gradle +++ b/server/src/main/resources/kotlinDSLClassPathFinder.gradle @@ -3,7 +3,8 @@ import org.gradle.internal.classpath.ClassPath allprojects { project -> task kotlinLSPKotlinDSLDeps { - def pattern = ~/^(?:gradle-(?:kotlin-dsl|base-services|core)|kotlin-(?:compiler|stdlib)).*\.jar/ + // def pattern = ~/^(?:gradle-(?:kotlin-dsl|base-services|plugins|core)|kotlin-(?:compiler|stdlib)).*\.jar/ + def pattern = ~/^.*\.jar/ doLast { (fileTree("$gradle.gradleHomeDir/lib") + fileTree("$gradle.gradleUserHomeDir/caches/$gradle.gradleVersion/generated-gradle-jars")) .findAll { it.name =~ pattern } diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/ClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/ClassPathResolver.kt index f6c4f4409..2353f30c0 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/ClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/ClassPathResolver.kt @@ -6,6 +6,7 @@ import java.nio.file.Path /** A source for creating class paths */ interface ClassPathResolver { val resolverType: String + val classpath: Set // may throw exceptions val classpathOrEmpty: Set // does not throw exceptions get() = try { @@ -15,6 +16,16 @@ interface ClassPathResolver { emptySet() } + val buildScriptClasspath: Set + get() = emptySet() + val buildScriptClasspathOrEmpty: Set + get() = try { + buildScriptClasspath + } catch (e: Exception) { + LOG.warn("Could not resolve buildscript classpath using {}: {}", resolverType, e.message) + emptySet() + } + companion object { /** A default empty classpath implementation */ val empty = object : ClassPathResolver { @@ -39,10 +50,14 @@ internal class UnionClassPathResolver(val lhs: ClassPathResolver, val rhs: Class override val resolverType: String get() = "(${lhs.resolverType} + ${rhs.resolverType})" override val classpath get() = lhs.classpath + rhs.classpath override val classpathOrEmpty get() = lhs.classpathOrEmpty + rhs.classpathOrEmpty + override val buildScriptClasspath get() = lhs.buildScriptClasspath + rhs.buildScriptClasspath + override val buildScriptClasspathOrEmpty get() = lhs.buildScriptClasspathOrEmpty + rhs.buildScriptClasspathOrEmpty } internal class FirstNonEmptyClassPathResolver(val lhs: ClassPathResolver, val rhs: ClassPathResolver) : ClassPathResolver { override val resolverType: String get() = "(${lhs.resolverType} or ${rhs.resolverType})" override val classpath get() = lhs.classpath.takeIf { it.isNotEmpty() } ?: rhs.classpath override val classpathOrEmpty get() = lhs.classpathOrEmpty.takeIf { it.isNotEmpty() } ?: rhs.classpathOrEmpty + override val buildScriptClasspath get() = lhs.buildScriptClasspath.takeIf { it.isNotEmpty() } ?: rhs.buildScriptClasspath + override val buildScriptClasspathOrEmpty get() = lhs.buildScriptClasspathOrEmpty.takeIf { it.isNotEmpty() } ?: rhs.buildScriptClasspathOrEmpty } diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt index 593ff5671..a0e06f519 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt @@ -14,14 +14,25 @@ import java.nio.file.Paths internal class GradleClassPathResolver(private val path: Path, private val includeKotlinDSL: Boolean): ClassPathResolver { override val resolverType: String = "Gradle" + private val projectDirectory: Path get() = path.getParent() override val classpath: Set get() { - val projectDirectory = path.getParent() - val scripts = listOf("projectClassPathFinder.gradle") + listOf("kotlinDSLClassPathFinder.gradle").takeIf { includeKotlinDSL }.orEmpty() - val tasks = listOf("kotlinLSPProjectDeps") + listOf("kotlinLSPKotlinDSLDeps").takeIf { includeKotlinDSL }.orEmpty() + val scripts = listOf("projectClassPathFinder.gradle") + val tasks = listOf("kotlinLSPProjectDeps") return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks) .apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") } } + override val buildScriptClasspath: Set get() { + return if (includeKotlinDSL) { + val scripts = listOf("kotlinDSLClassPathFinder.gradle") + val tasks = listOf("kotlinLSPKotlinDSLDeps") + + return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks) + .apply { if (isNotEmpty()) LOG.info("Successfully resolved build script dependencies for '${projectDirectory.fileName}' using Gradle") } + } else { + emptySet() + } + } companion object { /** Create a Gradle resolver if a file is a pom. */ From a2078c9693297265ca9e9da09e1a0cd31298e5d5 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 03:35:27 +0200 Subject: [PATCH 13/31] Add compilation kinds Use a separate class path for build scripts at the compiler level. --- .../main/kotlin/org/javacs/kt/CompiledFile.kt | 18 +++--- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 48 +++++++++++----- .../main/kotlin/org/javacs/kt/SourcePath.kt | 56 +++++++++++-------- 3 files changed, 78 insertions(+), 44 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt b/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt index e42c5a069..e1696d45b 100644 --- a/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt +++ b/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt @@ -15,15 +15,17 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.types.KotlinType +import java.nio.file.Path class CompiledFile( - val content: String, - val parse: KtFile, - val compile: BindingContext, - val container: ComponentProvider, - val sourcePath: Collection, - val classPath: CompilerClassPath) { - + val content: String, + val parse: KtFile, + val compile: BindingContext, + val container: ComponentProvider, + val sourcePath: Collection, + val classPath: CompilerClassPath, + val kind: CompilationKind = CompilationKind.DEFAULT +) { /** * Find the type of the expression at `cursor` */ @@ -38,7 +40,7 @@ class CompiledFile( bindingContextOf(expression, scopeWithImports).getType(expression) fun bindingContextOf(expression: KtExpression, scopeWithImports: LexicalScope): BindingContext = - classPath.compiler.compileExpression(expression, scopeWithImports, sourcePath).first + classPath.compiler.compileExpression(expression, scopeWithImports, sourcePath, kind).first private fun expandForType(cursor: Int, surroundingExpr: KtExpression): KtExpression { val dotParent = surroundingExpr.parent as? KtDotQualifiedExpression diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 97d44d313..87f5033d7 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -152,18 +152,31 @@ private class CompilationEnvironment( } } +/** + * Determines the compilation environment used + * by the compiler (and thus the class path). + */ +enum class CompilationKind { + /** Uses the default class path. */ + DEFAULT, + /** Uses the Kotlin DSL class path if available. */ + BUILD_SCRIPT +} + /** * Incrementally compiles files and expressions. * The basic strategy for compiling one file at-a-time is outlined in OneFilePerformance. */ -class Compiler(classPath: Set) : Closeable { +class Compiler(classPath: Set, buildScriptClassPath: Set = emptySet()) : Closeable { private var closed = false private val localFileSystem: VirtualFileSystem - private val compileEnvironment = CompilationEnvironment(classPath) + + private val defaultCompileEnvironment = CompilationEnvironment(classPath) + private val buildScriptCompileEnvironment = buildScriptClassPath.takeIf { it.isNotEmpty() }?.let(::CompilationEnvironment) private val compileLock = ReentrantLock() // TODO: Lock at file-level val psiFileFactory - get() = PsiFileFactory.getInstance(compileEnvironment.environment.project) + get() = PsiFileFactory.getInstance(defaultCompileEnvironment.environment.project) companion object { init { @@ -180,10 +193,11 @@ class Compiler(classPath: Set) : Closeable { * configuration (which is a class from this project). */ fun updateConfiguration(config: CompilerConfiguration) { - compileEnvironment.updateConfiguration(config) + defaultCompileEnvironment.updateConfiguration(config) + buildScriptCompileEnvironment?.updateConfiguration(config) } - fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtFile { + fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtFile { assert(!content.contains('\r')) val new = psiFileFactory.createFileFromText(file.toString(), KotlinLanguage.INSTANCE, content, true, false) as KtFile @@ -192,13 +206,13 @@ class Compiler(classPath: Set) : Closeable { return new } - fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtExpression { + fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtExpression { val property = parseDeclaration("val x = $content", file) as KtProperty return property.initializer!! } - fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtDeclaration = + fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtDeclaration = parseDeclaration(content, file) private fun parseDeclaration(content: String, file: Path): KtDeclaration { @@ -219,12 +233,17 @@ class Compiler(classPath: Set) : Closeable { else return onlyDeclaration } - fun compileFile(file: KtFile, sourcePath: Collection): Pair = - compileFiles(listOf(file), sourcePath) + private fun compileEnvironmentFor(kind: CompilationKind): CompilationEnvironment = when (kind) { + CompilationKind.DEFAULT -> defaultCompileEnvironment + CompilationKind.BUILD_SCRIPT -> buildScriptCompileEnvironment ?: defaultCompileEnvironment + } + + fun compileFile(file: KtFile, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair = + compileFiles(listOf(file), sourcePath, kind) - fun compileFiles(files: Collection, sourcePath: Collection): Pair { + fun compileFiles(files: Collection, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair { compileLock.withLock { - val (container, trace) = compileEnvironment.createContainer(sourcePath) + val (container, trace) = compileEnvironmentFor(kind).createContainer(sourcePath) val topDownAnalyzer = container.get() topDownAnalyzer.analyzeDeclarations(TopLevelDeclarations, files) @@ -232,11 +251,11 @@ class Compiler(classPath: Set) : Closeable { } } - fun compileExpression(expression: KtExpression, scopeWithImports: LexicalScope, sourcePath: Collection): Pair { + fun compileExpression(expression: KtExpression, scopeWithImports: LexicalScope, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair { try { // Use same lock as 'compileFile' to avoid concurrency issues such as #42 compileLock.withLock { - val (container, trace) = compileEnvironment.createContainer(sourcePath) + val (container, trace) = compileEnvironmentFor(kind).createContainer(sourcePath) val incrementalCompiler = container.get() incrementalCompiler.getTypeInfo( scopeWithImports, @@ -255,7 +274,8 @@ class Compiler(classPath: Set) : Closeable { override fun close() { if (!closed) { - compileEnvironment.close() + defaultCompileEnvironment.close() + buildScriptCompileEnvironment?.close() closed = true } else { LOG.warn("Compiler is already closed!") diff --git a/server/src/main/kotlin/org/javacs/kt/SourcePath.kt b/server/src/main/kotlin/org/javacs/kt/SourcePath.kt index 5a1279e4a..3c30b7364 100644 --- a/server/src/main/kotlin/org/javacs/kt/SourcePath.kt +++ b/server/src/main/kotlin/org/javacs/kt/SourcePath.kt @@ -17,15 +17,19 @@ class SourcePath( private val files = mutableMapOf() private inner class SourceFile( - val uri: URI, - var content: String, - val path: Path? = uri.filePath, - var parsed: KtFile? = null, - var compiledFile: KtFile? = null, - var compiledContext: BindingContext? = null, - var compiledContainer: ComponentProvider? = null, - val isTemporary: Boolean = false // A temporary source file will not be returned by .all() + val uri: URI, + var content: String, + val path: Path? = uri.filePath, + var parsed: KtFile? = null, + var compiledFile: KtFile? = null, + var compiledContext: BindingContext? = null, + var compiledContainer: ComponentProvider? = null, + val isTemporary: Boolean = false // A temporary source file will not be returned by .all() ) { + val kind: CompilationKind = + if (path?.fileName?.toString()?.endsWith(".gradle.kts") ?: false) CompilationKind.BUILD_SCRIPT + else CompilationKind.DEFAULT + fun put(newContent: String) { content = newContent } @@ -54,7 +58,7 @@ class SourcePath( if (parsed?.text != compiledFile?.text) { LOG.debug("Compiling {}", path?.fileName) - val (context, container) = cp.compiler.compileFile(parsed!!, allIncludingThis()) + val (context, container) = cp.compiler.compileFile(parsed!!, allIncludingThis(), kind) compiledContext = context compiledContainer = container compiledFile = parsed @@ -67,7 +71,7 @@ class SourcePath( parseIfChanged().compileIfNull().doPrepareCompiledFile() private fun doPrepareCompiledFile(): CompiledFile = - CompiledFile(content, compiledFile!!, compiledContext!!, compiledContainer!!, allIncludingThis(), cp) + CompiledFile(content, compiledFile!!, compiledContext!!, compiledContainer!!, allIncludingThis(), cp, kind) private fun allIncludingThis(): Collection = parseIfChanged().let { if (isTemporary) (all().asSequence() + sequenceOf(parsed!!)).toList() @@ -137,23 +141,31 @@ class SourcePath( fun compileFiles(all: Collection): BindingContext { // Figure out what has changed val sources = all.map { files[it]!! } - val changed = sources.filter { it.content != it.compiledFile?.text } + val allChanged = sources.filter { it.content != it.compiledFile?.text } + val (changedBuildScripts, changedSources) = allChanged.partition { it.kind == CompilationKind.BUILD_SCRIPT } // Compile changed files - val parse = changed.map { it.parseIfChanged().parsed!! } - val (context, container) = cp.compiler.compileFiles(parse, all()) - - // Update cache - for (f in changed) { - f.compiledFile = f.parsed - f.compiledContext = context - f.compiledContainer = container + fun compileAndUpdate(changed: List, kind: CompilationKind): BindingContext? { + if (changed.isEmpty()) return null + val parse = changed.map { it.parseIfChanged().parsed!! } + val (context, container) = cp.compiler.compileFiles(parse, all(), kind) + + // Update cache + for (f in changed) { + f.compiledFile = f.parsed + f.compiledContext = context + f.compiledContainer = container + } + + return context } + val buildScriptsContext = compileAndUpdate(changedBuildScripts, CompilationKind.BUILD_SCRIPT) + val sourcesContext = compileAndUpdate(changedSources, CompilationKind.DEFAULT) + // Combine with past compilations - val combined = mutableListOf(context) - val same = sources - changed - combined.addAll(same.map { it.compiledContext!! }) + val same = sources - allChanged + val combined = listOf(buildScriptsContext, sourcesContext).filterNotNull() + same.map { it.compiledContext!! } return CompositeBindingContext.create(combined) } From b8ed07d8969b8f6329f9a710a549b1c508e573c4 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 03:46:34 +0200 Subject: [PATCH 14/31] Query build script classpath and auto-update on change Automatically update the classpath if a Gradle buildfile changes and query the build script classpath in CompilerClassPath. --- .../kotlin/org/javacs/kt/CompilerClassPath.kt | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt b/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt index 40d6f712d..4e96e6f77 100644 --- a/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt +++ b/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt @@ -7,7 +7,8 @@ import java.nio.file.Path class CompilerClassPath(private val config: CompilerConfiguration) : Closeable { private val workspaceRoots = mutableSetOf() private val classPath = mutableSetOf() - var compiler = Compiler(classPath) + private val buildScriptClassPath = mutableSetOf() + var compiler = Compiler(classPath, buildScriptClassPath) private set init { @@ -15,23 +16,39 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable { } private fun refresh() { - val newClassPath = defaultClassPathResolver(workspaceRoots).classpathOrEmpty + val resolver = defaultClassPathResolver(workspaceRoots) + val newClassPath = resolver.classpathOrEmpty + val newBuildScriptClassPath = resolver.buildScriptClasspathOrEmpty + var refreshCompiler = false if (newClassPath != classPath) { - val added = newClassPath - classPath - val removed = classPath - newClassPath + syncClassPath(classPath, newClassPath) + refreshCompiler = true + } - logAdded(added) - logRemoved(removed) + if (newBuildScriptClassPath != buildScriptClassPath) { + syncClassPath(buildScriptClassPath, newBuildScriptClassPath) + refreshCompiler = true + } - classPath.removeAll(removed) - classPath.addAll(added) + if (refreshCompiler) { compiler.close() - compiler = Compiler(classPath) + compiler = Compiler(classPath, buildScriptClassPath) updateCompilerConfiguration() } } + private fun syncClassPath(dest: MutableSet, new: Set) { + val added = new - dest + val removed = dest - new + + logAdded(added) + logRemoved(removed) + + dest.removeAll(removed) + dest.addAll(added) + } + fun updateCompilerConfiguration() { compiler.updateConfiguration(config) } @@ -61,7 +78,8 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable { } fun changedOnDisk(file: Path) { - if (file.fileName.toString() == "pom.xml") + val name = file.fileName.toString() + if (name == "pom.xml" || name == "build.gradle" || name == "build.gradle.kts") refresh() } From ba8040552c3c31ed9b0c596df33a22fd136817ca Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 03:52:43 +0200 Subject: [PATCH 15/31] Delegate build script class path in WithStdlibResolver --- .../main/kotlin/org/javacs/kt/classpath/WithStdlibResolver.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/WithStdlibResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/WithStdlibResolver.kt index 42eb74254..0065544bc 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/WithStdlibResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/WithStdlibResolver.kt @@ -7,6 +7,8 @@ internal class WithStdlibResolver(private val wrapped: ClassPathResolver) : Clas override val resolverType: String get() = "Stdlib + ${wrapped.resolverType}" override val classpath: Set get() = wrapWithStdlib(wrapped.classpath) override val classpathOrEmpty: Set get() = wrapWithStdlib(wrapped.classpathOrEmpty) + override val buildScriptClasspath: Set get() = wrapWithStdlib(wrapped.buildScriptClasspath) + override val buildScriptClasspathOrEmpty: Set get() = wrapWithStdlib(wrapped.buildScriptClasspathOrEmpty) } private fun wrapWithStdlib(paths: Set): Set { From 2100c1c64daf9af5000fad93ec2b9af2a944221d Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 03:55:02 +0200 Subject: [PATCH 16/31] Add Todo note to CompilerClassPath.refresh --- server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt b/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt index 4e96e6f77..4c57598ea 100644 --- a/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt +++ b/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt @@ -16,6 +16,7 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable { } private fun refresh() { + // TODO: Fetch class path and build script class path concurrently (and asynchronously) val resolver = defaultClassPathResolver(workspaceRoots) val newClassPath = resolver.classpathOrEmpty val newBuildScriptClassPath = resolver.buildScriptClasspathOrEmpty From 566fd6f6c5b8e87b0b3f8152f93c5545b50c93c7 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 04:01:53 +0200 Subject: [PATCH 17/31] Just update regular class path when build script changes --- .../kotlin/org/javacs/kt/CompilerClassPath.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt b/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt index 4c57598ea..83274a42b 100644 --- a/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt +++ b/server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt @@ -15,21 +15,23 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable { compiler.updateConfiguration(config) } - private fun refresh() { + private fun refresh(updateBuildScriptClassPath: Boolean = true) { // TODO: Fetch class path and build script class path concurrently (and asynchronously) val resolver = defaultClassPathResolver(workspaceRoots) - val newClassPath = resolver.classpathOrEmpty - val newBuildScriptClassPath = resolver.buildScriptClasspathOrEmpty var refreshCompiler = false + val newClassPath = resolver.classpathOrEmpty if (newClassPath != classPath) { syncClassPath(classPath, newClassPath) refreshCompiler = true } - if (newBuildScriptClassPath != buildScriptClassPath) { - syncClassPath(buildScriptClassPath, newBuildScriptClassPath) - refreshCompiler = true + if (updateBuildScriptClassPath) { + val newBuildScriptClassPath = resolver.buildScriptClasspathOrEmpty + if (newBuildScriptClassPath != buildScriptClassPath) { + syncClassPath(buildScriptClassPath, newBuildScriptClassPath) + refreshCompiler = true + } } if (refreshCompiler) { @@ -81,7 +83,7 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable { fun changedOnDisk(file: Path) { val name = file.fileName.toString() if (name == "pom.xml" || name == "build.gradle" || name == "build.gradle.kts") - refresh() + refresh(updateBuildScriptClassPath = false) } override fun close() { From 31997f6dbbfed385ca440462a5a84c55fb2ede1b Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2019 04:23:19 +0200 Subject: [PATCH 18/31] Use .kts for in-memory script source files --- server/src/main/kotlin/org/javacs/kt/CompiledFile.kt | 5 +++-- server/src/main/kotlin/org/javacs/kt/SourcePath.kt | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt b/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt index e1696d45b..c9b5efb82 100644 --- a/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt +++ b/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.types.KotlinType -import java.nio.file.Path +import java.nio.file.Paths class CompiledFile( val content: String, @@ -24,6 +24,7 @@ class CompiledFile( val container: ComponentProvider, val sourcePath: Collection, val classPath: CompilerClassPath, + val isScript: Boolean = false, val kind: CompilationKind = CompilationKind.DEFAULT ) { /** @@ -122,7 +123,7 @@ class CompiledFile( } val padOffset = " ".repeat(offset) - val recompile = classPath.compiler.createFile(padOffset + surroundingContent) + val recompile = classPath.compiler.createFile(padOffset + surroundingContent, Paths.get(if (isScript) "dummy.virtual.kts" else "dummy.virtual.kt")) return recompile.findElementAt(cursor)?.findParent() } diff --git a/server/src/main/kotlin/org/javacs/kt/SourcePath.kt b/server/src/main/kotlin/org/javacs/kt/SourcePath.kt index 3c30b7364..bf6408612 100644 --- a/server/src/main/kotlin/org/javacs/kt/SourcePath.kt +++ b/server/src/main/kotlin/org/javacs/kt/SourcePath.kt @@ -26,6 +26,7 @@ class SourcePath( var compiledContainer: ComponentProvider? = null, val isTemporary: Boolean = false // A temporary source file will not be returned by .all() ) { + val isScript: Boolean = uri.toString().endsWith(".kts") val kind: CompilationKind = if (path?.fileName?.toString()?.endsWith(".gradle.kts") ?: false) CompilationKind.BUILD_SCRIPT else CompilationKind.DEFAULT @@ -71,7 +72,7 @@ class SourcePath( parseIfChanged().compileIfNull().doPrepareCompiledFile() private fun doPrepareCompiledFile(): CompiledFile = - CompiledFile(content, compiledFile!!, compiledContext!!, compiledContainer!!, allIncludingThis(), cp, kind) + CompiledFile(content, compiledFile!!, compiledContext!!, compiledContainer!!, allIncludingThis(), cp, isScript, kind) private fun allIncludingThis(): Collection = parseIfChanged().let { if (isTemporary) (all().asSequence() + sequenceOf(parsed!!)).toList() From d25bcd7f62f5c864fd2c3501343cbd9043f2ab74 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 00:27:42 +0200 Subject: [PATCH 19/31] Update to Kotlin 1.3.50 and use new scripting API Use the new Kotlin scripting API to register script templates. --- gradle.properties | 8 +++---- scripts/update_kt_version.py | 6 +++++- scripts/utils/cli.py | 8 +++---- server/build.gradle | 4 ++-- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 21 ++++++++++++------- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/gradle.properties b/gradle.properties index d9a4e5ce5..a1b20769d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ projectVersion=0.3.0 teamCityUrl=https://teamcity.jetbrains.com teamCityUsername=guest teamCityPassword=guest -kotlinVersion=1.3.40 -kotlinBuildType=Kotlin_1340_CompilerAllPlugins -kotlinBuild=1.3.41-release-150 -kotlinPluginBuild=1.3.41-release-IJ2019.2-1 +kotlinVersion=1.3.50 +kotlinBuildType=Kotlin_1350_CompilerAllPlugins +kotlinBuild=1.3.50-release-127 +kotlinPluginBuild= diff --git a/scripts/update_kt_version.py b/scripts/update_kt_version.py index 554e32b26..5d5029c09 100644 --- a/scripts/update_kt_version.py +++ b/scripts/update_kt_version.py @@ -18,6 +18,10 @@ def is_plugin_artifact(art_name): def to_plugin_build(art_name): return art_name.lstrip("kotlin-plugin-").rstrip(".zip") +class Unnamed: + def name(self): + return "" + def main(): props_file = "gradle.properties" @@ -39,7 +43,7 @@ def main(): build = prompt_by("build", builds, TeamCityNode.number).follow() artifacts = [art for art in build.follow("artifacts").findall("file") if is_plugin_artifact(art.name())] - artifact = prompt_by("plugin build", artifacts, lambda art: to_plugin_build(art.name())) + artifact = prompt_by("plugin build", artifacts, lambda art: to_plugin_build(art.name()), Unnamed()) changes = { "kotlinVersion": version.name(), diff --git a/scripts/utils/cli.py b/scripts/utils/cli.py index edf87dbd5..0fadc5d44 100644 --- a/scripts/utils/cli.py +++ b/scripts/utils/cli.py @@ -21,7 +21,7 @@ def require_not_none(description, x): if x == None: sys.exit(description + " not present") -def prompt_by(what, nodes, describer): +def prompt_by(what, nodes, describer, default=None): node_dict = {describer(node): node for node in nodes} sorted_described = sorted(node_dict.keys(), key=alphanum_sort_key) @@ -30,12 +30,12 @@ def prompt_by(what, nodes, describer): print() last_entry = sorted_described[-1] if len(sorted_described) > 0 else None - choice = input("Enter a " + what + " to choose [default: " + last_entry + "]: ").strip() + choice = input(f"Enter a {what} to choose [default: {last_entry}]: ").strip() print() - if len(choice) == 0: + if len(choice) == 0 and last_entry: return node_dict[last_entry] elif choice not in node_dict.keys(): - sys.exit("Invalid " + what + "!") + return default else: return node_dict[choice] diff --git a/server/build.gradle b/server/build.gradle index b7fe19870..c972ba55f 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -47,6 +47,7 @@ dependencies { implementation 'org.jetbrains.kotlin:kotlin-compiler-embeddable' implementation 'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable' implementation 'org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable' + implementation 'org.jetbrains.kotlin:kotlin-scripting-jvm-host-embeddable' implementation 'org.jetbrains.kotlin:kotlin-reflect' implementation 'org.jetbrains:fernflower:1.0' implementation 'com.pinterest.ktlint:ktlint-core:0.34.2' @@ -62,10 +63,9 @@ dependencies { testImplementation 'org.hamcrest:hamcrest-all:1.3' testImplementation 'junit:junit:4.11' testImplementation 'org.openjdk.jmh:jmh-core:1.20' - testImplementation 'org.jetbrains.kotlin:kotlin-scripting-jvm-host-embeddable' // See https://github.com/JetBrains/kotlin/blob/65b0a5f90328f4b9addd3a10c6f24f3037482276/libraries/examples/scripting/jvm-embeddable-host/build.gradle.kts#L8 - testCompileOnly 'org.jetbrains.kotlin:kotlin-scripting-jvm-host' + compileOnly 'org.jetbrains.kotlin:kotlin-scripting-jvm-host' annotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.20' } diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 87f5033d7..41b38f2e9 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.container.ComponentProvider import org.jetbrains.kotlin.container.get import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.idea.KotlinLanguage -import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTraceContext @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer import org.jetbrains.kotlin.resolve.TopDownAnalysisMode.TopLevelDeclarations import org.jetbrains.kotlin.resolve.calls.components.InferenceSession import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar @@ -39,7 +40,8 @@ import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition -import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition +import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition +import org.jetbrains.kotlin.scripting.extensions.ScriptExtraImportsProviderExtension import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices import org.jetbrains.kotlin.util.KotlinFrontEndException @@ -51,6 +53,10 @@ import java.nio.file.Paths import java.net.URLClassLoader import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock +import kotlin.script.experimental.host.ScriptingHostConfiguration +import kotlin.script.experimental.host.configurationDependencies +import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration +import kotlin.script.experimental.jvm.JvmDependency import org.javacs.kt.util.KotlinLSException import org.javacs.kt.util.KotlinNullableNotNullManager import org.javacs.kt.util.LoggingMessageCollector @@ -75,13 +81,13 @@ private class CompilationEnvironment( parentDisposable = disposable, // Not to be confused with the CompilerConfiguration in the language server Configuration configuration = KotlinCompilerConfiguration().apply { - put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME) + put(CommonConfigurationKeys.MODULE_NAME, JvmProtoBufUtil.DEFAULT_MODULE_NAME) put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector) add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) addJvmClasspathRoots(classPath.map { it.toFile() }) // Setup script templates - var scriptDefinitions: List = listOf(StandardScriptDefinition) + var scriptDefinitions: List = listOf(ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration)) if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { LOG.info("Configuring Kotlin DSL script templates...") @@ -95,9 +101,10 @@ private class CompilationEnvironment( try { // Load template classes val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) - // TODO: Use org.jetbrains.kotlin.scripting.definitions.ScriptDefinition instead of - // KotlinScriptDefinition since the latter will be deprecated soon. - scriptDefinitions = scriptTemplates.map { KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin) } + val scriptHostConfig = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { + configurationDependencies(JvmDependency(classPath.map { it.toFile() })) + } + scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin) } } catch (e: Exception) { LOG.error("Error while loading script template classes") LOG.printStackTrace(e) From bd86167ab60b8724674229cd66be9ac46a63b9f7 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 02:02:25 +0200 Subject: [PATCH 20/31] WIP: Use ScriptDefinition.FromLegacy --- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 41b38f2e9..77be54ca2 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -36,11 +36,14 @@ import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar +import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition +import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition +import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.extensions.ScriptExtraImportsProviderExtension import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices @@ -83,11 +86,9 @@ private class CompilationEnvironment( configuration = KotlinCompilerConfiguration().apply { put(CommonConfigurationKeys.MODULE_NAME, JvmProtoBufUtil.DEFAULT_MODULE_NAME) put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector) - add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) addJvmClasspathRoots(classPath.map { it.toFile() }) - // Setup script templates - var scriptDefinitions: List = listOf(ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration)) + var scriptDefinitions: List = listOf() if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { LOG.info("Configuring Kotlin DSL script templates...") @@ -101,16 +102,22 @@ private class CompilationEnvironment( try { // Load template classes val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) + val fileClassPath = classPath.map { it.toFile() } val scriptHostConfig = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { - configurationDependencies(JvmDependency(classPath.map { it.toFile() })) + configurationDependencies(JvmDependency(fileClassPath)) } - scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin) } + scriptDefinitions = scriptTemplates + .map { object : ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin)) { + override val isDefault = true + } } } catch (e: Exception) { LOG.error("Error while loading script template classes") LOG.printStackTrace(e) } } addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) + put(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION, true) + add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) }, configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES ) @@ -121,7 +128,7 @@ private class CompilationEnvironment( } parser = KtPsiFactory(project) - scripts = ScriptDefinitionProvider.getInstance(project)!! + scripts = ScriptDefinitionProvider.getInstance(project)!! as CliScriptDefinitionProvider } fun updateConfiguration(config: CompilerConfiguration) { @@ -249,6 +256,7 @@ class Compiler(classPath: Set, buildScriptClassPath: Set = emptySet( compileFiles(listOf(file), sourcePath, kind) fun compileFiles(files: Collection, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair { + files.forEach { LOG.info("$it -> ScriptDefinition: ${it.findScriptDefinition()?.legacyDefinition?.template?.simpleName}") } compileLock.withLock { val (container, trace) = compileEnvironmentFor(kind).createContainer(sourcePath) val topDownAnalyzer = container.get() From da8f4042dcd652000bc7f9758c606f5df2589430 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 02:16:47 +0200 Subject: [PATCH 21/31] WIP: Use compilation kinds throughout Compiler --- .../main/kotlin/org/javacs/kt/CompiledFile.kt | 2 +- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 92 ++++++++++--------- .../main/kotlin/org/javacs/kt/SourcePath.kt | 2 +- .../javacs/kt/j2k/JavaToKotlinConverter.kt | 3 +- 4 files changed, 52 insertions(+), 47 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt b/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt index c9b5efb82..c1d2e22a1 100644 --- a/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt +++ b/server/src/main/kotlin/org/javacs/kt/CompiledFile.kt @@ -123,7 +123,7 @@ class CompiledFile( } val padOffset = " ".repeat(offset) - val recompile = classPath.compiler.createFile(padOffset + surroundingContent, Paths.get(if (isScript) "dummy.virtual.kts" else "dummy.virtual.kt")) + val recompile = classPath.compiler.createFile(padOffset + surroundingContent, Paths.get("dummy.virtual" + if (isScript) ".kts" else ".kt"), kind) return recompile.findElementAt(cursor)?.findParent() } diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 77be54ca2..bdcfbf25b 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -86,38 +86,9 @@ private class CompilationEnvironment( configuration = KotlinCompilerConfiguration().apply { put(CommonConfigurationKeys.MODULE_NAME, JvmProtoBufUtil.DEFAULT_MODULE_NAME) put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector) - addJvmClasspathRoots(classPath.map { it.toFile() }) - // Setup script templates - var scriptDefinitions: List = listOf() - - if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { - LOG.info("Configuring Kotlin DSL script templates...") - - val scriptTemplates = listOf( - // "org.gradle.kotlin.dsl.KotlinInitScript", - // "org.gradle.kotlin.dsl.KotlinSettingsScript", - "org.gradle.kotlin.dsl.KotlinBuildScript" - ) - - try { - // Load template classes - val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) - val fileClassPath = classPath.map { it.toFile() } - val scriptHostConfig = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { - configurationDependencies(JvmDependency(fileClassPath)) - } - scriptDefinitions = scriptTemplates - .map { object : ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin)) { - override val isDefault = true - } } - } catch (e: Exception) { - LOG.error("Error while loading script template classes") - LOG.printStackTrace(e) - } - } - addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) - put(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION, true) add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) + addJvmClasspathRoots(classPath.map { it.toFile() }) + // addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) }, configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES ) @@ -129,6 +100,39 @@ private class CompilationEnvironment( parser = KtPsiFactory(project) scripts = ScriptDefinitionProvider.getInstance(project)!! as CliScriptDefinitionProvider + + // Setup script templates + var scriptDefinitions: List = listOf() + + if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { + LOG.info("Configuring Kotlin DSL script templates...") + + val scriptTemplates = listOf( + // "org.gradle.kotlin.dsl.KotlinInitScript", + // "org.gradle.kotlin.dsl.KotlinSettingsScript", + "org.gradle.kotlin.dsl.KotlinBuildScript" + ) + + try { + // Load template classes + val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) + val fileClassPath = classPath.map { it.toFile() } + val scriptHostConfig = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { + configurationDependencies(JvmDependency(fileClassPath)) + } + scriptDefinitions = scriptTemplates + .map { object : ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin)) { + override val isDefault = true + } } + } catch (e: Exception) { + LOG.error("Error while loading script template classes") + LOG.printStackTrace(e) + } + } + + LOG.info("Definitions: ${scriptDefinitions.map { it?.legacyDefinition?.template?.simpleName }}") + scripts.setScriptDefinitions(scriptDefinitions) + LOG.info("Default def: ${scripts.getDefaultDefinition()?.legacyDefinition?.template?.simpleName}") } fun updateConfiguration(config: CompilerConfiguration) { @@ -189,9 +193,6 @@ class Compiler(classPath: Set, buildScriptClassPath: Set = emptySet( private val buildScriptCompileEnvironment = buildScriptClassPath.takeIf { it.isNotEmpty() }?.let(::CompilationEnvironment) private val compileLock = ReentrantLock() // TODO: Lock at file-level - val psiFileFactory - get() = PsiFileFactory.getInstance(defaultCompileEnvironment.environment.project) - companion object { init { setIdeaIoUseFallback() @@ -211,26 +212,26 @@ class Compiler(classPath: Set, buildScriptClassPath: Set = emptySet( buildScriptCompileEnvironment?.updateConfiguration(config) } - fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtFile { + fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtFile { assert(!content.contains('\r')) - val new = psiFileFactory.createFileFromText(file.toString(), KotlinLanguage.INSTANCE, content, true, false) as KtFile + val new = psiFileFactoryFor(kind).createFileFromText(file.toString(), KotlinLanguage.INSTANCE, content, true, false) as KtFile assert(new.virtualFile != null) return new } - fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtExpression { - val property = parseDeclaration("val x = $content", file) as KtProperty + fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtExpression { + val property = parseDeclaration("val x = $content", file, kind) as KtProperty return property.initializer!! } - fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtDeclaration = - parseDeclaration(content, file) + fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtDeclaration = + parseDeclaration(content, file, kind) - private fun parseDeclaration(content: String, file: Path): KtDeclaration { - val parse = createFile(content, file) + private fun parseDeclaration(content: String, file: Path, kind: CompilationKind = CompilationKind.DEFAULT): KtDeclaration { + val parse = createFile(content, file, kind) val declarations = parse.declarations assert(declarations.size == 1) { "${declarations.size} declarations in $content" } @@ -252,11 +253,14 @@ class Compiler(classPath: Set, buildScriptClassPath: Set = emptySet( CompilationKind.BUILD_SCRIPT -> buildScriptCompileEnvironment ?: defaultCompileEnvironment } + fun psiFileFactoryFor(kind: CompilationKind): PsiFileFactory = + PsiFileFactory.getInstance(compileEnvironmentFor(kind).environment.project) + fun compileFile(file: KtFile, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair = - compileFiles(listOf(file), sourcePath, kind) + compileFiles(listOf(file), sourcePath, kind) fun compileFiles(files: Collection, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair { - files.forEach { LOG.info("$it -> ScriptDefinition: ${it.findScriptDefinition()?.legacyDefinition?.template?.simpleName}") } + files.forEach { LOG.info("$it -> ScriptDefinition: ${it.findScriptDefinition()?.legacyDefinition?.template?.simpleName}, provider: ${(ScriptDefinitionProvider.getInstance(buildScriptCompileEnvironment?.environment?.project!!) as CliScriptDefinitionProvider).getDefaultDefinition()?.legacyDefinition?.template?.simpleName}") } compileLock.withLock { val (container, trace) = compileEnvironmentFor(kind).createContainer(sourcePath) val topDownAnalyzer = container.get() diff --git a/server/src/main/kotlin/org/javacs/kt/SourcePath.kt b/server/src/main/kotlin/org/javacs/kt/SourcePath.kt index bf6408612..97057961e 100644 --- a/server/src/main/kotlin/org/javacs/kt/SourcePath.kt +++ b/server/src/main/kotlin/org/javacs/kt/SourcePath.kt @@ -37,7 +37,7 @@ class SourcePath( fun parseIfChanged(): SourceFile { if (content != parsed?.text) { - parsed = cp.compiler.createFile(content, path ?: Paths.get("sourceFile.virtual.kt")) + parsed = cp.compiler.createFile(content, path ?: Paths.get("sourceFile.virtual.kt"), kind) } return this diff --git a/server/src/main/kotlin/org/javacs/kt/j2k/JavaToKotlinConverter.kt b/server/src/main/kotlin/org/javacs/kt/j2k/JavaToKotlinConverter.kt index c5bba1276..400c47ac0 100644 --- a/server/src/main/kotlin/org/javacs/kt/j2k/JavaToKotlinConverter.kt +++ b/server/src/main/kotlin/org/javacs/kt/j2k/JavaToKotlinConverter.kt @@ -6,10 +6,11 @@ import org.jetbrains.kotlin.com.intellij.openapi.project.Project // import org.jetbrains.kotlin.j2k.JavaToKotlinTranslator import org.javacs.kt.LOG import org.javacs.kt.Compiler +import org.javacs.kt.CompilationKind import org.javacs.kt.util.nonNull fun convertJavaToKotlin(javaCode: String, compiler: Compiler): String { - val psiFactory = compiler.psiFileFactory + val psiFactory = compiler.psiFileFactoryFor(CompilationKind.DEFAULT) val javaAST = psiFactory.createFileFromText("snippet.java", JavaLanguage.INSTANCE, javaCode) LOG.info("Parsed {} to {}", javaCode, javaAST) From f57c52284989223670ddd9eec52885c123555412 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 02:20:12 +0200 Subject: [PATCH 22/31] Use custom ScriptingHostConfiguration for DSL script templates --- server/src/main/kotlin/org/javacs/kt/Compiler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index bdcfbf25b..aa02ed09f 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -121,7 +121,7 @@ private class CompilationEnvironment( configurationDependencies(JvmDependency(fileClassPath)) } scriptDefinitions = scriptTemplates - .map { object : ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin)) { + .map { object : ScriptDefinition.FromLegacy(scriptHostConfig, KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin)) { override val isDefault = true } } } catch (e: Exception) { From 0d8f245674c72453029130d789751a0a84bc6bd0 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 02:22:45 +0200 Subject: [PATCH 23/31] Define script templates using ScriptingConfigurationKeys --- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 63 +++++++++---------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index aa02ed09f..bcb98b279 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -88,7 +88,35 @@ private class CompilationEnvironment( put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector) add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) addJvmClasspathRoots(classPath.map { it.toFile() }) - // addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) + // Setup script templates + var scriptDefinitions: List = listOf() + + if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { + LOG.info("Configuring Kotlin DSL script templates...") + + val scriptTemplates = listOf( + // "org.gradle.kotlin.dsl.KotlinInitScript", + // "org.gradle.kotlin.dsl.KotlinSettingsScript", + "org.gradle.kotlin.dsl.KotlinBuildScript" + ) + + try { + // Load template classes + val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) + val fileClassPath = classPath.map { it.toFile() } + val scriptHostConfig = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { + configurationDependencies(JvmDependency(fileClassPath)) + } + scriptDefinitions = scriptTemplates + .map { object : ScriptDefinition.FromLegacy(scriptHostConfig, KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin)) { + override val isDefault = true + } } + } catch (e: Exception) { + LOG.error("Error while loading script template classes") + LOG.printStackTrace(e) + } + } + addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) }, configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES ) @@ -100,39 +128,6 @@ private class CompilationEnvironment( parser = KtPsiFactory(project) scripts = ScriptDefinitionProvider.getInstance(project)!! as CliScriptDefinitionProvider - - // Setup script templates - var scriptDefinitions: List = listOf() - - if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { - LOG.info("Configuring Kotlin DSL script templates...") - - val scriptTemplates = listOf( - // "org.gradle.kotlin.dsl.KotlinInitScript", - // "org.gradle.kotlin.dsl.KotlinSettingsScript", - "org.gradle.kotlin.dsl.KotlinBuildScript" - ) - - try { - // Load template classes - val scriptClassLoader = URLClassLoader(classPath.map { it.toUri().toURL() }.toTypedArray()) - val fileClassPath = classPath.map { it.toFile() } - val scriptHostConfig = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { - configurationDependencies(JvmDependency(fileClassPath)) - } - scriptDefinitions = scriptTemplates - .map { object : ScriptDefinition.FromLegacy(scriptHostConfig, KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin)) { - override val isDefault = true - } } - } catch (e: Exception) { - LOG.error("Error while loading script template classes") - LOG.printStackTrace(e) - } - } - - LOG.info("Definitions: ${scriptDefinitions.map { it?.legacyDefinition?.template?.simpleName }}") - scripts.setScriptDefinitions(scriptDefinitions) - LOG.info("Default def: ${scripts.getDefaultDefinition()?.legacyDefinition?.template?.simpleName}") } fun updateConfiguration(config: CompilerConfiguration) { From e3aae5781d1aebaf1fcc1d4389b5ec0847bea5cf Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 02:31:40 +0200 Subject: [PATCH 24/31] Add support for Kotlin DSL settings/init scripts Additionally, clean up the ScriptDefinition construction by getting rid of the legacy 'KotlinScriptDefinition' type. --- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index bcb98b279..130faa412 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -42,7 +42,6 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition -import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.extensions.ScriptExtraImportsProviderExtension import org.jetbrains.kotlin.types.TypeUtils @@ -88,15 +87,16 @@ private class CompilationEnvironment( put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector) add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) addJvmClasspathRoots(classPath.map { it.toFile() }) + // Setup script templates - var scriptDefinitions: List = listOf() + var scriptDefinitions: List = listOf(ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration)) if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { LOG.info("Configuring Kotlin DSL script templates...") val scriptTemplates = listOf( - // "org.gradle.kotlin.dsl.KotlinInitScript", - // "org.gradle.kotlin.dsl.KotlinSettingsScript", + "org.gradle.kotlin.dsl.KotlinInitScript", + "org.gradle.kotlin.dsl.KotlinSettingsScript", "org.gradle.kotlin.dsl.KotlinBuildScript" ) @@ -107,15 +107,13 @@ private class CompilationEnvironment( val scriptHostConfig = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { configurationDependencies(JvmDependency(fileClassPath)) } - scriptDefinitions = scriptTemplates - .map { object : ScriptDefinition.FromLegacy(scriptHostConfig, KotlinScriptDefinition(scriptClassLoader.loadClass(it).kotlin)) { - override val isDefault = true - } } + scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacyTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin) } } catch (e: Exception) { LOG.error("Error while loading script template classes") LOG.printStackTrace(e) } } + addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) }, configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES @@ -255,7 +253,9 @@ class Compiler(classPath: Set, buildScriptClassPath: Set = emptySet( compileFiles(listOf(file), sourcePath, kind) fun compileFiles(files: Collection, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair { - files.forEach { LOG.info("$it -> ScriptDefinition: ${it.findScriptDefinition()?.legacyDefinition?.template?.simpleName}, provider: ${(ScriptDefinitionProvider.getInstance(buildScriptCompileEnvironment?.environment?.project!!) as CliScriptDefinitionProvider).getDefaultDefinition()?.legacyDefinition?.template?.simpleName}") } + if (kind == CompilationKind.BUILD_SCRIPT) { + files.forEach { LOG.info("$it -> ScriptDefinition: ${it.findScriptDefinition()?.legacyDefinition?.template?.simpleName}") } + } compileLock.withLock { val (container, trace) = compileEnvironmentFor(kind).createContainer(sourcePath) val topDownAnalyzer = container.get() From c6c4d95869e522d454250cc3530ffbf1d1a03ce2 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 02:38:24 +0200 Subject: [PATCH 25/31] Improve script definition logging --- server/src/main/kotlin/org/javacs/kt/Compiler.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 130faa412..45247baa1 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -42,6 +42,7 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition +import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition // Legacy import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.extensions.ScriptExtraImportsProviderExtension import org.jetbrains.kotlin.types.TypeUtils @@ -254,8 +255,10 @@ class Compiler(classPath: Set, buildScriptClassPath: Set = emptySet( fun compileFiles(files: Collection, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair { if (kind == CompilationKind.BUILD_SCRIPT) { - files.forEach { LOG.info("$it -> ScriptDefinition: ${it.findScriptDefinition()?.legacyDefinition?.template?.simpleName}") } + // Print the (legacy) script template used by the compiled Kotlin DSL build file + files.forEach { LOG.info("$it -> ScriptDefinition: ${it.findScriptDefinition()?.asLegacyOrNull()?.template?.simpleName}") } } + compileLock.withLock { val (container, trace) = compileEnvironmentFor(kind).createContainer(sourcePath) val topDownAnalyzer = container.get() From 09815a59014d0b8eb24d90153d4f67b279f0ba83 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 03:45:33 +0200 Subject: [PATCH 26/31] WIP: Experiment with script definition construction --- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 45247baa1..106a21903 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -38,13 +38,16 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys +import org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition // Legacy import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition +import org.jetbrains.kotlin.scripting.definitions.getEnvironment import org.jetbrains.kotlin.scripting.extensions.ScriptExtraImportsProviderExtension +import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices import org.jetbrains.kotlin.util.KotlinFrontEndException @@ -56,6 +59,12 @@ import java.nio.file.Paths import java.net.URLClassLoader import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock +import kotlin.script.dependencies.Environment +import kotlin.script.dependencies.ScriptContents +import kotlin.script.experimental.dependencies.ScriptDependencies +import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.dependencies.DependenciesResolver +import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.configurationDependencies import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration @@ -89,7 +98,7 @@ private class CompilationEnvironment( add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) addJvmClasspathRoots(classPath.map { it.toFile() }) - // Setup script templates + // Setup script templates (e.g. used by Gradle's Kotlin DSL) var scriptDefinitions: List = listOf(ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration)) if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { @@ -108,7 +117,21 @@ private class CompilationEnvironment( val scriptHostConfig = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { configurationDependencies(JvmDependency(fileClassPath)) } - scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacyTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin) } + // TODO: KotlinScriptDefinition will soon be deprecated, use + // ScriptDefinition.compilationConfiguration and its defaultImports instead + // of KotlinScriptDefinition.dependencyResolver + // TODO: Use ScriptDefinition.FromLegacyTemplate directly if possible + scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacyTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin, fileClassPath) } + // scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacy(scriptHostConfig, object : KotlinScriptDefinitionFromAnnotatedTemplate( + // scriptClassLoader.loadClass(it).kotlin, + // scriptHostConfig[ScriptingHostConfiguration.getEnvironment]?.invoke() + // ) { + // override val dependencyResolver: DependenciesResolver = object : DependenciesResolver { + // override fun resolve(scriptContents: ScriptContents, environment: Environment) = ResolveResult.Success(ScriptDependencies( + // imports = listOf("org.gradle.kotlin.api.*") + // )) + // } + // }) } } catch (e: Exception) { LOG.error("Error while loading script template classes") LOG.printStackTrace(e) From a5edd498e61abcd9c06843b5eee86f311787a059 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 04:16:17 +0200 Subject: [PATCH 27/31] Provide more helpful error messages when the Gradle build fails --- server/src/main/kotlin/org/javacs/kt/Compiler.kt | 3 ++- .../kt/classpath/GradleClassPathResolver.kt | 8 ++++++-- .../src/main/kotlin/org/javacs/kt/util/Utils.kt | 15 ++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 106a21903..43d6ffae0 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -121,7 +121,7 @@ private class CompilationEnvironment( // ScriptDefinition.compilationConfiguration and its defaultImports instead // of KotlinScriptDefinition.dependencyResolver // TODO: Use ScriptDefinition.FromLegacyTemplate directly if possible - scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacyTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin, fileClassPath) } + scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacyTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin) } // scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacy(scriptHostConfig, object : KotlinScriptDefinitionFromAnnotatedTemplate( // scriptClassLoader.loadClass(it).kotlin, // scriptHostConfig[ScriptingHostConfiguration.getEnvironment]?.invoke() @@ -138,6 +138,7 @@ private class CompilationEnvironment( } } + LOG.info("Adding script definitions ${scriptDefinitions.map { it.asLegacyOrNull()?.template?.simpleName }}") addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinitions) }, configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt index a0e06f519..62508f376 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt @@ -3,7 +3,7 @@ package org.javacs.kt.classpath import org.javacs.kt.LOG import org.javacs.kt.util.firstNonNull import org.javacs.kt.util.tryResolving -import org.javacs.kt.util.execAndReadStdout +import org.javacs.kt.util.execAndReadStdoutAndStderr import org.javacs.kt.util.KotlinLSException import org.javacs.kt.util.isOSWindows import org.javacs.kt.util.findCommandOnPath @@ -87,12 +87,16 @@ private fun readDependenciesViaGradleCLI(projectDirectory: Path, gradleScripts: } private fun findGradleCLIDependencies(command: String, projectDirectory: Path): Set? { - val result = execAndReadStdout(command, projectDirectory) + val (result, errors) = execAndReadStdoutAndStderr(command, projectDirectory) LOG.debug(result) + if ("FAILURE: Build failed" in errors) { + LOG.warn("Gradle task failed: {}", errors.lines().firstOrNull()) + } return parseGradleCLIDependencies(result) } private val artifactPattern by lazy { "kotlin-lsp-gradle (.+)(?:\r?\n)".toRegex() } +private val gradleErrorWherePattern by lazy { "\\*\\s+Where:[\r\n]+(\\S\\.*)".toRegex() } private fun parseGradleCLIDependencies(output: String): Set? { LOG.debug(output) diff --git a/shared/src/main/kotlin/org/javacs/kt/util/Utils.kt b/shared/src/main/kotlin/org/javacs/kt/util/Utils.kt index 0b7a893fe..45cc3de19 100644 --- a/shared/src/main/kotlin/org/javacs/kt/util/Utils.kt +++ b/shared/src/main/kotlin/org/javacs/kt/util/Utils.kt @@ -10,11 +10,16 @@ import java.util.concurrent.CompletableFuture fun execAndReadStdout(shellCommand: String, directory: Path): String { val process = Runtime.getRuntime().exec(shellCommand, null, directory.toFile()) val stdout = process.inputStream - var result = "" - stdout.bufferedReader().use { - result = it.readText() - } - return result + return stdout.bufferedReader().use { it.readText() } +} + +fun execAndReadStdoutAndStderr(shellCommand: String, directory: Path): Pair { + val process = Runtime.getRuntime().exec(shellCommand, null, directory.toFile()) + val stdout = process.inputStream + val stderr = process.errorStream + val output = stdout.bufferedReader().use { it.readText() } + val errors = stderr.bufferedReader().use { it.readText() } + return Pair(output, errors) } inline fun withCustomStdout(delegateOut: PrintStream, task: () -> Unit) { From be6f43d558e9e678e2faae12f93859a0e8664d67 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 04:21:59 +0200 Subject: [PATCH 28/31] Implicitly import Kotlin DSL --- .../src/main/kotlin/org/javacs/kt/Compiler.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index 43d6ffae0..c4eee1deb 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -121,17 +121,17 @@ private class CompilationEnvironment( // ScriptDefinition.compilationConfiguration and its defaultImports instead // of KotlinScriptDefinition.dependencyResolver // TODO: Use ScriptDefinition.FromLegacyTemplate directly if possible - scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacyTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin) } - // scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacy(scriptHostConfig, object : KotlinScriptDefinitionFromAnnotatedTemplate( - // scriptClassLoader.loadClass(it).kotlin, - // scriptHostConfig[ScriptingHostConfiguration.getEnvironment]?.invoke() - // ) { - // override val dependencyResolver: DependenciesResolver = object : DependenciesResolver { - // override fun resolve(scriptContents: ScriptContents, environment: Environment) = ResolveResult.Success(ScriptDependencies( - // imports = listOf("org.gradle.kotlin.api.*") - // )) - // } - // }) } + // scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacyTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin) } + scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacy(scriptHostConfig, object : KotlinScriptDefinitionFromAnnotatedTemplate( + scriptClassLoader.loadClass(it).kotlin, + scriptHostConfig[ScriptingHostConfiguration.getEnvironment]?.invoke() + ) { + override val dependencyResolver: DependenciesResolver = object : DependenciesResolver { + override fun resolve(scriptContents: ScriptContents, environment: Environment) = ResolveResult.Success(ScriptDependencies( + imports = listOf("org.gradle.kotlin.dsl.*") + )) + } + }) } } catch (e: Exception) { LOG.error("Error while loading script template classes") LOG.printStackTrace(e) From 5fb582503bb48d2051868883895292efd0cb8446 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 04:27:15 +0200 Subject: [PATCH 29/31] Log script template at debug level --- server/src/main/kotlin/org/javacs/kt/Compiler.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index c4eee1deb..364de25e4 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -99,7 +99,7 @@ private class CompilationEnvironment( addJvmClasspathRoots(classPath.map { it.toFile() }) // Setup script templates (e.g. used by Gradle's Kotlin DSL) - var scriptDefinitions: List = listOf(ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration)) + val scriptDefinitions: MutableList = mutableListOf(ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration)) if (classPath.any { GRADLE_DSL_DEPENDENCY_PATTERN.matches(it.fileName.toString()) }) { LOG.info("Configuring Kotlin DSL script templates...") @@ -122,7 +122,7 @@ private class CompilationEnvironment( // of KotlinScriptDefinition.dependencyResolver // TODO: Use ScriptDefinition.FromLegacyTemplate directly if possible // scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacyTemplate(scriptHostConfig, scriptClassLoader.loadClass(it).kotlin) } - scriptDefinitions = scriptTemplates.map { ScriptDefinition.FromLegacy(scriptHostConfig, object : KotlinScriptDefinitionFromAnnotatedTemplate( + scriptDefinitions.addAll(scriptTemplates.map { ScriptDefinition.FromLegacy(scriptHostConfig, object : KotlinScriptDefinitionFromAnnotatedTemplate( scriptClassLoader.loadClass(it).kotlin, scriptHostConfig[ScriptingHostConfiguration.getEnvironment]?.invoke() ) { @@ -131,7 +131,7 @@ private class CompilationEnvironment( imports = listOf("org.gradle.kotlin.dsl.*") )) } - }) } + }) }) } catch (e: Exception) { LOG.error("Error while loading script template classes") LOG.printStackTrace(e) @@ -280,7 +280,7 @@ class Compiler(classPath: Set, buildScriptClassPath: Set = emptySet( fun compileFiles(files: Collection, sourcePath: Collection, kind: CompilationKind = CompilationKind.DEFAULT): Pair { if (kind == CompilationKind.BUILD_SCRIPT) { // Print the (legacy) script template used by the compiled Kotlin DSL build file - files.forEach { LOG.info("$it -> ScriptDefinition: ${it.findScriptDefinition()?.asLegacyOrNull()?.template?.simpleName}") } + files.forEach { LOG.debug { "$it -> ScriptDefinition: ${it.findScriptDefinition()?.asLegacyOrNull()?.template?.simpleName}" } } } compileLock.withLock { From 122678803b5a1512b2816fb912764f23adca3852 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 04:36:48 +0200 Subject: [PATCH 30/31] Fix tests --- server/build.gradle | 1 + server/src/test/kotlin/org/javacs/kt/OneFilePerformance.kt | 4 ++-- server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index c972ba55f..104e46430 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -66,6 +66,7 @@ dependencies { // See https://github.com/JetBrains/kotlin/blob/65b0a5f90328f4b9addd3a10c6f24f3037482276/libraries/examples/scripting/jvm-embeddable-host/build.gradle.kts#L8 compileOnly 'org.jetbrains.kotlin:kotlin-scripting-jvm-host' + testCompileOnly 'org.jetbrains.kotlin:kotlin-scripting-jvm-host' annotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.20' } diff --git a/server/src/test/kotlin/org/javacs/kt/OneFilePerformance.kt b/server/src/test/kotlin/org/javacs/kt/OneFilePerformance.kt index 265c28746..e3351a9eb 100644 --- a/server/src/test/kotlin/org/javacs/kt/OneFilePerformance.kt +++ b/server/src/test/kotlin/org/javacs/kt/OneFilePerformance.kt @@ -17,7 +17,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.container.ComponentProvider import org.jetbrains.kotlin.container.ValueDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtPsiFactory @@ -48,7 +48,7 @@ class OneFilePerformance { class ReusableParts : Closeable { internal var config = CompilerConfiguration() init { - config.put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME) + config.put(CommonConfigurationKeys.MODULE_NAME, JvmProtoBufUtil.DEFAULT_MODULE_NAME) config.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector) } internal val disposable = Disposer.newDisposable() diff --git a/server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt b/server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt index 5206f5de2..d13e70e52 100644 --- a/server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt +++ b/server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt @@ -32,6 +32,8 @@ private class SnippetRunner { } class SimpleScriptTest { + // TODO: Test a script using the language server instead + // of just experimenting with the API @Test fun basicScript() { val runner = SnippetRunner() From 3dd14ba55077ab1e55f0333e052af3173001133f Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 5 Sep 2019 15:26:17 +0200 Subject: [PATCH 31/31] Add Gradle DSL unit test Verify that hovers and code completion work correctly in *.gradle.kts build files. --- server/build.gradle | 7 +++++- .../org/javacs/kt/GradleDSLScriptTest.kt | 22 +++++++++++++++++++ .../resources/kotlinDSLWorkspace/.gitignore | 2 ++ .../kotlinDSLWorkspace/build.gradle.kts | 9 ++++++++ .../kotlinDSLWorkspace/settings.gradle.kts | 1 + 5 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 server/src/test/kotlin/org/javacs/kt/GradleDSLScriptTest.kt create mode 100644 server/src/test/resources/kotlinDSLWorkspace/.gitignore create mode 100644 server/src/test/resources/kotlinDSLWorkspace/build.gradle.kts create mode 100644 server/src/test/resources/kotlinDSLWorkspace/settings.gradle.kts diff --git a/server/build.gradle b/server/build.gradle index 104e46430..da5af46bd 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -97,6 +97,11 @@ task copyPropertiesToTestWorkspace(type: Copy) { into file('src/test/resources/additionalWorkspace') } +task copyPropertiesToDSLTestWorkspace(type: Copy) { + from "$rootDir/gradle.properties" + into file('src/test/resources/kotlinDSLWorkspace') +} + task fixFilePermissions(type: Exec) { // When running on macOS or Linux the start script // needs executable permissions to run. @@ -133,7 +138,7 @@ run { } test { - dependsOn copyPropertiesToTestWorkspace + dependsOn copyPropertiesToTestWorkspace, copyPropertiesToDSLTestWorkspace testLogging { events 'failed' diff --git a/server/src/test/kotlin/org/javacs/kt/GradleDSLScriptTest.kt b/server/src/test/kotlin/org/javacs/kt/GradleDSLScriptTest.kt new file mode 100644 index 000000000..dd2b43acc --- /dev/null +++ b/server/src/test/kotlin/org/javacs/kt/GradleDSLScriptTest.kt @@ -0,0 +1,22 @@ +package org.javacs.kt + +import org.junit.Test +import org.junit.Assert.assertThat +import org.hamcrest.Matchers.* +import org.eclipse.lsp4j.MarkedString + +class GradleDSLScriptTest : SingleFileTestFixture("kotlinDSLWorkspace", "build.gradle.kts") { + @Test fun `edit repositories`() { + val completions = languageServer.textDocumentService.completion(completionParams(file, 7, 13)).get().right!! + val labels = completions.items.map { it.label } + + assertThat(labels, hasItem("repositories")) + } + + @Test fun `hover plugin`() { + val hover = languageServer.textDocumentService.hover(textDocumentPosition(file, 4, 8)).get()!! + val contents = hover.contents.left.first().right + + assertThat(contents, equalTo(MarkedString("kotlin", "fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec"))) + } +} diff --git a/server/src/test/resources/kotlinDSLWorkspace/.gitignore b/server/src/test/resources/kotlinDSLWorkspace/.gitignore new file mode 100644 index 000000000..1b915fd3b --- /dev/null +++ b/server/src/test/resources/kotlinDSLWorkspace/.gitignore @@ -0,0 +1,2 @@ +# Auto-generated by :server:copyPropertiesToDSLTestWorkspace +gradle.properties diff --git a/server/src/test/resources/kotlinDSLWorkspace/build.gradle.kts b/server/src/test/resources/kotlinDSLWorkspace/build.gradle.kts new file mode 100644 index 000000000..2f3017905 --- /dev/null +++ b/server/src/test/resources/kotlinDSLWorkspace/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + // TODO: Currently not possible, see https://github.com/gradle/gradle/issues/9830 + // kotlin("jvm") version "$kotlinVersion" + kotlin("jvm") version "1.3.50" +} + +repositories { + jcenter() +} diff --git a/server/src/test/resources/kotlinDSLWorkspace/settings.gradle.kts b/server/src/test/resources/kotlinDSLWorkspace/settings.gradle.kts new file mode 100644 index 000000000..1dbf00c9b --- /dev/null +++ b/server/src/test/resources/kotlinDSLWorkspace/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "Kotlin DSL Workspace"