Skip to content

Commit a2078c9

Browse files
committed
Add compilation kinds
Use a separate class path for build scripts at the compiler level.
1 parent 66c850d commit a2078c9

File tree

3 files changed

+78
-44
lines changed

3 files changed

+78
-44
lines changed

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
1515
import org.jetbrains.kotlin.resolve.BindingContext
1616
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
1717
import org.jetbrains.kotlin.types.KotlinType
18+
import java.nio.file.Path
1819

1920
class CompiledFile(
20-
val content: String,
21-
val parse: KtFile,
22-
val compile: BindingContext,
23-
val container: ComponentProvider,
24-
val sourcePath: Collection<KtFile>,
25-
val classPath: CompilerClassPath) {
26-
21+
val content: String,
22+
val parse: KtFile,
23+
val compile: BindingContext,
24+
val container: ComponentProvider,
25+
val sourcePath: Collection<KtFile>,
26+
val classPath: CompilerClassPath,
27+
val kind: CompilationKind = CompilationKind.DEFAULT
28+
) {
2729
/**
2830
* Find the type of the expression at `cursor`
2931
*/
@@ -38,7 +40,7 @@ class CompiledFile(
3840
bindingContextOf(expression, scopeWithImports).getType(expression)
3941

4042
fun bindingContextOf(expression: KtExpression, scopeWithImports: LexicalScope): BindingContext =
41-
classPath.compiler.compileExpression(expression, scopeWithImports, sourcePath).first
43+
classPath.compiler.compileExpression(expression, scopeWithImports, sourcePath, kind).first
4244

4345
private fun expandForType(cursor: Int, surroundingExpr: KtExpression): KtExpression {
4446
val dotParent = surroundingExpr.parent as? KtDotQualifiedExpression

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

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -152,18 +152,31 @@ private class CompilationEnvironment(
152152
}
153153
}
154154

155+
/**
156+
* Determines the compilation environment used
157+
* by the compiler (and thus the class path).
158+
*/
159+
enum class CompilationKind {
160+
/** Uses the default class path. */
161+
DEFAULT,
162+
/** Uses the Kotlin DSL class path if available. */
163+
BUILD_SCRIPT
164+
}
165+
155166
/**
156167
* Incrementally compiles files and expressions.
157168
* The basic strategy for compiling one file at-a-time is outlined in OneFilePerformance.
158169
*/
159-
class Compiler(classPath: Set<Path>) : Closeable {
170+
class Compiler(classPath: Set<Path>, buildScriptClassPath: Set<Path> = emptySet()) : Closeable {
160171
private var closed = false
161172
private val localFileSystem: VirtualFileSystem
162-
private val compileEnvironment = CompilationEnvironment(classPath)
173+
174+
private val defaultCompileEnvironment = CompilationEnvironment(classPath)
175+
private val buildScriptCompileEnvironment = buildScriptClassPath.takeIf { it.isNotEmpty() }?.let(::CompilationEnvironment)
163176
private val compileLock = ReentrantLock() // TODO: Lock at file-level
164177

165178
val psiFileFactory
166-
get() = PsiFileFactory.getInstance(compileEnvironment.environment.project)
179+
get() = PsiFileFactory.getInstance(defaultCompileEnvironment.environment.project)
167180

168181
companion object {
169182
init {
@@ -180,10 +193,11 @@ class Compiler(classPath: Set<Path>) : Closeable {
180193
* configuration (which is a class from this project).
181194
*/
182195
fun updateConfiguration(config: CompilerConfiguration) {
183-
compileEnvironment.updateConfiguration(config)
196+
defaultCompileEnvironment.updateConfiguration(config)
197+
buildScriptCompileEnvironment?.updateConfiguration(config)
184198
}
185199

186-
fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtFile {
200+
fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtFile {
187201
assert(!content.contains('\r'))
188202

189203
val new = psiFileFactory.createFileFromText(file.toString(), KotlinLanguage.INSTANCE, content, true, false) as KtFile
@@ -192,13 +206,13 @@ class Compiler(classPath: Set<Path>) : Closeable {
192206
return new
193207
}
194208

195-
fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtExpression {
209+
fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtExpression {
196210
val property = parseDeclaration("val x = $content", file) as KtProperty
197211

198212
return property.initializer!!
199213
}
200214

201-
fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kts")): KtDeclaration =
215+
fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kt")): KtDeclaration =
202216
parseDeclaration(content, file)
203217

204218
private fun parseDeclaration(content: String, file: Path): KtDeclaration {
@@ -219,24 +233,29 @@ class Compiler(classPath: Set<Path>) : Closeable {
219233
else return onlyDeclaration
220234
}
221235

222-
fun compileFile(file: KtFile, sourcePath: Collection<KtFile>): Pair<BindingContext, ComponentProvider> =
223-
compileFiles(listOf(file), sourcePath)
236+
private fun compileEnvironmentFor(kind: CompilationKind): CompilationEnvironment = when (kind) {
237+
CompilationKind.DEFAULT -> defaultCompileEnvironment
238+
CompilationKind.BUILD_SCRIPT -> buildScriptCompileEnvironment ?: defaultCompileEnvironment
239+
}
240+
241+
fun compileFile(file: KtFile, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> =
242+
compileFiles(listOf(file), sourcePath, kind)
224243

225-
fun compileFiles(files: Collection<KtFile>, sourcePath: Collection<KtFile>): Pair<BindingContext, ComponentProvider> {
244+
fun compileFiles(files: Collection<KtFile>, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> {
226245
compileLock.withLock {
227-
val (container, trace) = compileEnvironment.createContainer(sourcePath)
246+
val (container, trace) = compileEnvironmentFor(kind).createContainer(sourcePath)
228247
val topDownAnalyzer = container.get<LazyTopDownAnalyzer>()
229248
topDownAnalyzer.analyzeDeclarations(TopLevelDeclarations, files)
230249

231250
return Pair(trace.bindingContext, container)
232251
}
233252
}
234253

235-
fun compileExpression(expression: KtExpression, scopeWithImports: LexicalScope, sourcePath: Collection<KtFile>): Pair<BindingContext, ComponentProvider> {
254+
fun compileExpression(expression: KtExpression, scopeWithImports: LexicalScope, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> {
236255
try {
237256
// Use same lock as 'compileFile' to avoid concurrency issues such as #42
238257
compileLock.withLock {
239-
val (container, trace) = compileEnvironment.createContainer(sourcePath)
258+
val (container, trace) = compileEnvironmentFor(kind).createContainer(sourcePath)
240259
val incrementalCompiler = container.get<ExpressionTypingServices>()
241260
incrementalCompiler.getTypeInfo(
242261
scopeWithImports,
@@ -255,7 +274,8 @@ class Compiler(classPath: Set<Path>) : Closeable {
255274

256275
override fun close() {
257276
if (!closed) {
258-
compileEnvironment.close()
277+
defaultCompileEnvironment.close()
278+
buildScriptCompileEnvironment?.close()
259279
closed = true
260280
} else {
261281
LOG.warn("Compiler is already closed!")

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

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@ class SourcePath(
1717
private val files = mutableMapOf<URI, SourceFile>()
1818

1919
private inner class SourceFile(
20-
val uri: URI,
21-
var content: String,
22-
val path: Path? = uri.filePath,
23-
var parsed: KtFile? = null,
24-
var compiledFile: KtFile? = null,
25-
var compiledContext: BindingContext? = null,
26-
var compiledContainer: ComponentProvider? = null,
27-
val isTemporary: Boolean = false // A temporary source file will not be returned by .all()
20+
val uri: URI,
21+
var content: String,
22+
val path: Path? = uri.filePath,
23+
var parsed: KtFile? = null,
24+
var compiledFile: KtFile? = null,
25+
var compiledContext: BindingContext? = null,
26+
var compiledContainer: ComponentProvider? = null,
27+
val isTemporary: Boolean = false // A temporary source file will not be returned by .all()
2828
) {
29+
val kind: CompilationKind =
30+
if (path?.fileName?.toString()?.endsWith(".gradle.kts") ?: false) CompilationKind.BUILD_SCRIPT
31+
else CompilationKind.DEFAULT
32+
2933
fun put(newContent: String) {
3034
content = newContent
3135
}
@@ -54,7 +58,7 @@ class SourcePath(
5458
if (parsed?.text != compiledFile?.text) {
5559
LOG.debug("Compiling {}", path?.fileName)
5660

57-
val (context, container) = cp.compiler.compileFile(parsed!!, allIncludingThis())
61+
val (context, container) = cp.compiler.compileFile(parsed!!, allIncludingThis(), kind)
5862
compiledContext = context
5963
compiledContainer = container
6064
compiledFile = parsed
@@ -67,7 +71,7 @@ class SourcePath(
6771
parseIfChanged().compileIfNull().doPrepareCompiledFile()
6872

6973
private fun doPrepareCompiledFile(): CompiledFile =
70-
CompiledFile(content, compiledFile!!, compiledContext!!, compiledContainer!!, allIncludingThis(), cp)
74+
CompiledFile(content, compiledFile!!, compiledContext!!, compiledContainer!!, allIncludingThis(), cp, kind)
7175

7276
private fun allIncludingThis(): Collection<KtFile> = parseIfChanged().let {
7377
if (isTemporary) (all().asSequence() + sequenceOf(parsed!!)).toList()
@@ -137,23 +141,31 @@ class SourcePath(
137141
fun compileFiles(all: Collection<URI>): BindingContext {
138142
// Figure out what has changed
139143
val sources = all.map { files[it]!! }
140-
val changed = sources.filter { it.content != it.compiledFile?.text }
144+
val allChanged = sources.filter { it.content != it.compiledFile?.text }
145+
val (changedBuildScripts, changedSources) = allChanged.partition { it.kind == CompilationKind.BUILD_SCRIPT }
141146

142147
// Compile changed files
143-
val parse = changed.map { it.parseIfChanged().parsed!! }
144-
val (context, container) = cp.compiler.compileFiles(parse, all())
145-
146-
// Update cache
147-
for (f in changed) {
148-
f.compiledFile = f.parsed
149-
f.compiledContext = context
150-
f.compiledContainer = container
148+
fun compileAndUpdate(changed: List<SourceFile>, kind: CompilationKind): BindingContext? {
149+
if (changed.isEmpty()) return null
150+
val parse = changed.map { it.parseIfChanged().parsed!! }
151+
val (context, container) = cp.compiler.compileFiles(parse, all(), kind)
152+
153+
// Update cache
154+
for (f in changed) {
155+
f.compiledFile = f.parsed
156+
f.compiledContext = context
157+
f.compiledContainer = container
158+
}
159+
160+
return context
151161
}
152162

163+
val buildScriptsContext = compileAndUpdate(changedBuildScripts, CompilationKind.BUILD_SCRIPT)
164+
val sourcesContext = compileAndUpdate(changedSources, CompilationKind.DEFAULT)
165+
153166
// Combine with past compilations
154-
val combined = mutableListOf(context)
155-
val same = sources - changed
156-
combined.addAll(same.map { it.compiledContext!! })
167+
val same = sources - allChanged
168+
val combined = listOf(buildScriptsContext, sourcesContext).filterNotNull() + same.map { it.compiledContext!! }
157169

158170
return CompositeBindingContext.create(combined)
159171
}

0 commit comments

Comments
 (0)