Skip to content

Commit c17d2b7

Browse files
committed
Setup JDT LS extension
1 parent cfd1717 commit c17d2b7

File tree

21 files changed

+1706
-16
lines changed

21 files changed

+1706
-16
lines changed

server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import org.javacs.kt.classpath.defaultClassPathResolver
55
import org.javacs.kt.compiler.Compiler
66
import org.javacs.kt.util.AsyncExecutor
77
import java.io.Closeable
8+
import java.io.File
89
import java.nio.file.FileSystems
910
import java.nio.file.Path
11+
import java.nio.file.Paths
1012

1113
/**
1214
* Manages the class path (compiled JARs, etc), the Java source path
@@ -17,6 +19,7 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable {
1719
private val javaSourcePath = mutableSetOf<Path>()
1820
private val buildScriptClassPath = mutableSetOf<Path>()
1921
val classPath = mutableSetOf<ClassPathEntry>()
22+
private var outputDirectory: File? = null
2023

2124
var compiler = Compiler(javaSourcePath, classPath.map { it.compiledJar }.toSet(), buildScriptClassPath)
2225
private set
@@ -66,7 +69,7 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable {
6669
if (refreshCompiler) {
6770
LOG.info("Reinstantiating compiler")
6871
compiler.close()
69-
compiler = Compiler(javaSourcePath, classPath.map { it.compiledJar }.toSet(), buildScriptClassPath)
72+
compiler = Compiler(javaSourcePath, classPath.map { it.compiledJar }.toSet(), buildScriptClassPath, outputDirectory)
7073
updateCompilerConfiguration()
7174
}
7275

@@ -95,6 +98,10 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable {
9598
workspaceRoots.add(root)
9699
javaSourcePath.addAll(findJavaSourceFiles(root))
97100

101+
val outputDir = Paths.get(root.toString(), "kls").toFile()
102+
outputDirectory = outputDir
103+
compiler.outputDirectory = outputDir
104+
98105
return refresh()
99106
}
100107

@@ -103,6 +110,8 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable {
103110

104111
workspaceRoots.remove(root)
105112
javaSourcePath.removeAll(findJavaSourceFiles(root))
113+
outputDirectory = null
114+
compiler.outputDirectory = null
106115

107116
return refresh()
108117
}

server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ class KotlinTextDocumentService(
179179
// Lint after saving to prevent inconsistent diagnostics
180180
val uri = parseURI(params.textDocument.uri)
181181
lintNow(uri)
182+
debounceLint.schedule {
183+
sp.save(uri)
184+
}
182185
}
183186

184187
override fun signatureHelp(position: SignatureHelpParams): CompletableFuture<SignatureHelp?> = async.compute {
@@ -263,6 +266,7 @@ class KotlinTextDocumentService(
263266
fun lintAll() {
264267
debounceLint.submitImmediately {
265268
sp.compileAllFiles()
269+
sp.saveAllFiles()
266270
sp.refreshDependencyIndexes()
267271
}
268272
}

server/src/main/kotlin/org/javacs/kt/SourcePath.kt

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.psi.KtFile
1515
import org.jetbrains.kotlin.resolve.BindingContext
1616
import org.jetbrains.kotlin.resolve.CompositeBindingContext
1717
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
18+
import java.lang.Exception
1819
import kotlin.concurrent.withLock
1920
import java.nio.file.Path
2021
import java.nio.file.Paths
@@ -50,7 +51,8 @@ class SourcePath(
5051
var compiledContext: BindingContext? = null,
5152
var compiledContainer: ComponentProvider? = null,
5253
val language: Language? = null,
53-
val isTemporary: Boolean = false // A temporary source file will not be returned by .all()
54+
val isTemporary: Boolean = false, // A temporary source file will not be returned by .all()
55+
var lastSavedFile: KtFile? = null,
5456
) {
5557
val extension: String? = uri.fileExtension ?: "kt" // TODO: Use language?.associatedFileType?.defaultExtension again
5658
val isScript: Boolean = extension == "kts"
@@ -162,7 +164,10 @@ class SourcePath(
162164
}
163165

164166
fun delete(uri: URI) {
165-
files[uri]?.let { refreshWorkspaceIndexes(listOf(it), listOf()) }
167+
files[uri]?.let {
168+
refreshWorkspaceIndexes(listOf(it), listOf())
169+
cp.compiler.removeGeneratedCode(listOfNotNull(it.lastSavedFile))
170+
}
166171

167172
files.remove(uri)
168173
}
@@ -250,10 +255,34 @@ class SourcePath(
250255
// TODO: Investigate the possibility of compiling all files at once, instead of iterating here
251256
// At the moment, compiling all files at once sometimes leads to an internal error from the TopDownAnalyzer
252257
files.keys.forEach {
253-
compileFiles(listOf(it))
258+
// If one of the files fails to compile, we compile the others anyway
259+
try {
260+
compileFiles(listOf(it))
261+
} catch (ex: Exception) {
262+
LOG.printStackTrace(ex)
263+
}
254264
}
255265
}
256266

267+
/**
268+
* Saves a file. This generates code for the file and deletes previously generated code for this file.
269+
*/
270+
fun save(uri: URI) {
271+
files[uri]?.let {
272+
cp.compiler.removeGeneratedCode(listOfNotNull(it.lastSavedFile))
273+
it.compiledContainer?.let { container ->
274+
it.compiledContext?.let { context ->
275+
cp.compiler.generateCode(container, context, listOfNotNull(it.compiledFile))
276+
it.lastSavedFile = it.compiledFile
277+
}
278+
}
279+
}
280+
}
281+
282+
fun saveAllFiles() {
283+
files.keys.forEach { save(it) }
284+
}
285+
257286
fun refreshDependencyIndexes() {
258287
compileAllFiles()
259288

server/src/main/kotlin/org/javacs/kt/compiler/Compiler.kt

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
package org.javacs.kt.compiler
22

3-
import com.intellij.codeInsight.NullableNotNullManager
43
import com.intellij.lang.Language
5-
import com.intellij.openapi.Disposable
64
import com.intellij.openapi.util.Disposer
75
import com.intellij.openapi.vfs.StandardFileSystems
86
import com.intellij.openapi.vfs.VirtualFileManager
97
import com.intellij.openapi.vfs.VirtualFileSystem
108
import com.intellij.psi.PsiFile
119
import com.intellij.psi.PsiFileFactory
12-
import com.intellij.mock.MockProject
1310
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
1411
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
1512
import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
@@ -18,7 +15,6 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
1815
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
1916
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoots
2017
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
21-
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
2218
import org.jetbrains.kotlin.config.CommonConfigurationKeys
2319
import org.jetbrains.kotlin.config.CompilerConfiguration as KotlinCompilerConfiguration
2420
import org.jetbrains.kotlin.config.JVMConfigurationKeys
@@ -39,26 +35,20 @@ import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
3935
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
4036
import org.jetbrains.kotlin.resolve.calls.components.InferenceSession
4137
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
42-
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
4338
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
4439
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
4540
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
4641
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
4742
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
48-
import org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition
4943
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
50-
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
51-
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
5244
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
5345
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition // Legacy
5446
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
5547
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
56-
import org.jetbrains.kotlin.scripting.extensions.ScriptExtraImportsProviderExtension
5748
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
5849
import org.jetbrains.kotlin.types.TypeUtils
5950
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
6051
import org.jetbrains.kotlin.util.KotlinFrontEndException
61-
import org.jetbrains.kotlin.utils.PathUtil
6252
import java.io.Closeable
6353
import java.io.File
6454
import java.nio.file.Path
@@ -69,7 +59,6 @@ import kotlin.concurrent.withLock
6959
import kotlin.script.dependencies.Environment
7060
import kotlin.script.dependencies.ScriptContents
7161
import kotlin.script.experimental.dependencies.ScriptDependencies
72-
import kotlin.script.experimental.api.ScriptCompilationConfiguration
7362
import kotlin.script.experimental.dependencies.DependenciesResolver
7463
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult
7564
import kotlin.script.experimental.host.ScriptingHostConfiguration
@@ -80,6 +69,12 @@ import org.javacs.kt.LOG
8069
import org.javacs.kt.CompilerConfiguration
8170
import org.javacs.kt.util.KotlinLSException
8271
import org.javacs.kt.util.LoggingMessageCollector
72+
import org.jetbrains.kotlin.cli.common.output.writeAllTo
73+
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
74+
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
75+
import org.jetbrains.kotlin.codegen.state.GenerationState
76+
import org.jetbrains.kotlin.container.getService
77+
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
8378

8479
private val GRADLE_DSL_DEPENDENCY_PATTERN = Regex("^gradle-(?:kotlin-dsl|core).*\\.jar$")
8580

@@ -445,7 +440,7 @@ enum class CompilationKind {
445440
* Incrementally compiles files and expressions.
446441
* The basic strategy for compiling one file at-a-time is outlined in OneFilePerformance.
447442
*/
448-
class Compiler(javaSourcePath: Set<Path>, classPath: Set<Path>, buildScriptClassPath: Set<Path> = emptySet()) : Closeable {
443+
class Compiler(javaSourcePath: Set<Path>, classPath: Set<Path>, buildScriptClassPath: Set<Path> = emptySet(), var outputDirectory: File? = null) : Closeable {
449444
private var closed = false
450445
private val localFileSystem: VirtualFileSystem
451446

@@ -553,6 +548,34 @@ class Compiler(javaSourcePath: Set<Path>, classPath: Set<Path>, buildScriptClass
553548
}
554549
}
555550

551+
fun removeGeneratedCode(files: Collection<KtFile>) {
552+
files.forEach { file ->
553+
file.declarations.forEach { declaration ->
554+
outputDirectory?.resolve(
555+
file.packageFqName.asString().replace(".", File.separator) + File.separator + declaration.name + ".class"
556+
)?.delete()
557+
}
558+
}
559+
}
560+
561+
fun generateCode(container: ComponentProvider, bindingContext: BindingContext, files: Collection<KtFile>) {
562+
outputDirectory?.let {
563+
compileLock.withLock {
564+
val compileEnv = compileEnvironmentFor(CompilationKind.DEFAULT)
565+
val state = GenerationState.Builder(
566+
project = compileEnv.environment.project,
567+
builderFactory = ClassBuilderFactories.BINARIES,
568+
module = container.getService(ModuleDescriptor::class.java),
569+
bindingContext = bindingContext,
570+
files = files.toList(),
571+
configuration = compileEnv.environment.configuration
572+
).build()
573+
KotlinCodegenFacade.compileCorrectFiles(state)
574+
state.factory.writeAllTo(it)
575+
}
576+
}
577+
}
578+
556579
override fun close() {
557580
if (!closed) {
558581
defaultCompileEnvironment.close()

vscode-java-kotlin/.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
node_modules
2+
target
3+
*.vsix
4+
.idea
5+
*.iml
6+
.DS_Store
7+
**/.DS_Store
8+
jars
9+
.vscode/settings.json
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// A launch configuration that compiles the extension and then opens it inside a new window
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
{
6+
"version": "0.2.0",
7+
"configurations": [
8+
{
9+
"name": "Extension",
10+
"type": "extensionHost",
11+
"request": "launch",
12+
"runtimeExecutable": "${execPath}",
13+
"args": [
14+
"--extensionDevelopmentPath=${workspaceFolder}"
15+
],
16+
"outFiles": [
17+
"${workspaceFolder}/out/**/*.js"
18+
]
19+
}
20+
]
21+
}

vscode-java-kotlin/.vscodeignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.vscode/**
2+
.idea/**
3+
*.iml
4+
javaConfig.json
5+
tsconfig.json
6+
org.javacs.kt.jdt.ls.extension
7+
build.sh
8+
.gitignore

vscode-java-kotlin/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# VSCode Extension for Java + Kotlin
2+
3+
A VSCode extension that enchances [vscode-java](https://github.com/redhat-developer/vscode-java) and [vscode-kotlin](https://github.com/fwcd/vscode-kotlin) with java + kotlin interoperability. This uses a JDT LS extension with a custom project importer to allow Java code to have access to Kotlin code.
4+
5+
**Disclaimer**: This is very experimental, but it seems to work for small maven projects at least.
6+
7+
## Setup (for now)
8+
9+
For now, to set this up, you need to run the `build.sh` script in this directory to package the JDT LS extension. Afterwards, you should run `npm install` to install the extension dependencies.
10+
11+
To debug, you can use F5 on VSCode, as with any other extension. To package the extension you can use `vsce package`.

vscode-java-kotlin/build.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
rm -fr jars
3+
mkdir -p jars
4+
5+
cd jdt-ls-extension
6+
mvn clean package
7+
cp org.javacs.kt.jdt.ls.extension/target/org.javacs.kt.jdt.ls.extension-1.0.0-SNAPSHOT.jar ../jars/jdt-ls-extension.jar

vscode-java-kotlin/javaConfig.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"projects": [
3+
"./jdt-ls-extension/org.javacs.kt.jdt.ls.extension"
4+
],
5+
"targetPlatform": "./jdt-ls-extension/target.target"
6+
}

0 commit comments

Comments
 (0)