Skip to content

Commit 2780cab

Browse files
committed
Support parsing of PsiFiles other than KtFile in Compiler
1 parent 716c9a7 commit 2780cab

File tree

5 files changed

+44
-31
lines changed

5 files changed

+44
-31
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class CompiledFile(
4242
bindingContextOf(expression, scopeWithImports).getType(expression)
4343

4444
fun bindingContextOf(expression: KtExpression, scopeWithImports: LexicalScope): BindingContext =
45-
classPath.compiler.compileExpression(expression, scopeWithImports, sourcePath, kind).first
45+
classPath.compiler.compileKtExpression(expression, scopeWithImports, sourcePath, kind).first
4646

4747
private fun expandForType(cursor: Int, surroundingExpr: KtExpression): KtExpression {
4848
val dotParent = surroundingExpr.parent as? KtDotQualifiedExpression
@@ -98,7 +98,7 @@ class CompiledFile(
9898

9999
val (surroundingContent, offset) = contentAndOffsetFromElement(psi, oldParent, asReference)
100100
val padOffset = " ".repeat(offset)
101-
val recompile = classPath.compiler.createFile(padOffset + surroundingContent, Paths.get("dummy.virtual" + if (isScript) ".kts" else ".kt"), kind)
101+
val recompile = classPath.compiler.createKtFile(padOffset + surroundingContent, Paths.get("dummy.virtual" + if (isScript) ".kts" else ".kt"), kind)
102102
return recompile.findElementAt(cursor)?.findParent<KtElement>()
103103
}
104104

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

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package org.javacs.kt
22

33
import org.jetbrains.kotlin.com.intellij.codeInsight.NullableNotNullManager
4+
import org.jetbrains.kotlin.com.intellij.lang.Language
45
import org.jetbrains.kotlin.com.intellij.openapi.Disposable
56
import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer
67
import org.jetbrains.kotlin.com.intellij.openapi.vfs.StandardFileSystems
78
import org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFileManager
89
import org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFileSystem
10+
import org.jetbrains.kotlin.com.intellij.psi.PsiFile
911
import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory
1012
import org.jetbrains.kotlin.com.intellij.mock.MockProject
1113
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
@@ -244,26 +246,25 @@ class Compiler(classPath: Set<Path>, buildScriptClassPath: Set<Path> = emptySet(
244246
buildScriptCompileEnvironment?.updateConfiguration(config)
245247
}
246248

247-
fun createFile(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtFile {
249+
fun createPsiFile(content: String, file: Path = Paths.get("dummy.virtual.kt"), language: Language = KotlinLanguage.INSTANCE, kind: CompilationKind = CompilationKind.DEFAULT): PsiFile {
248250
assert(!content.contains('\r'))
249251

250-
val new = psiFileFactoryFor(kind).createFileFromText(file.toString(), KotlinLanguage.INSTANCE, content, true, false) as KtFile
252+
val new = psiFileFactoryFor(kind).createFileFromText(file.toString(), language, content, true, false)
251253
assert(new.virtualFile != null)
252254

253255
return new
254256
}
255257

256-
fun createExpression(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtExpression {
257-
val property = parseDeclaration("val x = $content", file, kind) as KtProperty
258+
fun createKtFile(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtFile =
259+
createPsiFile(content, file, language = KotlinLanguage.INSTANCE, kind = kind) as KtFile
258260

261+
fun createKtExpression(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtExpression {
262+
val property = createKtDeclaration("val x = $content", file, kind) as KtProperty
259263
return property.initializer!!
260264
}
261265

262-
fun createDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtDeclaration =
263-
parseDeclaration(content, file, kind)
264-
265-
private fun parseDeclaration(content: String, file: Path, kind: CompilationKind = CompilationKind.DEFAULT): KtDeclaration {
266-
val parse = createFile(content, file, kind)
266+
fun createKtDeclaration(content: String, file: Path = Paths.get("dummy.virtual.kt"), kind: CompilationKind = CompilationKind.DEFAULT): KtDeclaration {
267+
val parse = createKtFile(content, file, kind)
267268
val declarations = parse.declarations
268269

269270
assert(declarations.size == 1) { "${declarations.size} declarations in $content" }
@@ -288,10 +289,10 @@ class Compiler(classPath: Set<Path>, buildScriptClassPath: Set<Path> = emptySet(
288289
fun psiFileFactoryFor(kind: CompilationKind): PsiFileFactory =
289290
PsiFileFactory.getInstance(compileEnvironmentFor(kind).environment.project)
290291

291-
fun compileFile(file: KtFile, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> =
292-
compileFiles(listOf(file), sourcePath, kind)
292+
fun compileKtFile(file: KtFile, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> =
293+
compileKtFiles(listOf(file), sourcePath, kind)
293294

294-
fun compileFiles(files: Collection<KtFile>, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> {
295+
fun compileKtFiles(files: Collection<KtFile>, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> {
295296
if (kind == CompilationKind.BUILD_SCRIPT) {
296297
// Print the (legacy) script template used by the compiled Kotlin DSL build file
297298
files.forEach { LOG.debug { "$it -> ScriptDefinition: ${it.findScriptDefinition()?.asLegacyOrNull<KotlinScriptDefinition>()?.template?.simpleName}" } }
@@ -306,7 +307,7 @@ class Compiler(classPath: Set<Path>, buildScriptClassPath: Set<Path> = emptySet(
306307
}
307308
}
308309

309-
fun compileExpression(expression: KtExpression, scopeWithImports: LexicalScope, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> {
310+
fun compileKtExpression(expression: KtExpression, scopeWithImports: LexicalScope, sourcePath: Collection<KtFile>, kind: CompilationKind = CompilationKind.DEFAULT): Pair<BindingContext, ComponentProvider> {
310311
try {
311312
// Use same lock as 'compileFile' to avoid concurrency issues such as #42
312313
compileLock.withLock {

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package org.javacs.kt
22

3+
import org.javacs.kt.util.fileExtension
34
import org.javacs.kt.util.filePath
45
import org.javacs.kt.util.describeURI
56
import org.jetbrains.kotlin.com.intellij.lang.Language
7+
import org.jetbrains.kotlin.com.intellij.psi.PsiFile
68
import org.jetbrains.kotlin.container.ComponentProvider
79
import org.jetbrains.kotlin.psi.KtFile
810
import org.jetbrains.kotlin.resolve.BindingContext
@@ -33,7 +35,8 @@ class SourcePath(
3335
val language: Language? = null,
3436
val isTemporary: Boolean = false // A temporary source file will not be returned by .all()
3537
) {
36-
val isScript: Boolean = uri.toString().endsWith(".kts")
38+
val extension: String? = uri.fileExtension ?: language?.associatedFileType?.defaultExtension
39+
val isScript: Boolean = extension == "kts"
3740
val kind: CompilationKind =
3841
if (path?.fileName?.toString()?.endsWith(".gradle.kts") ?: false) CompilationKind.BUILD_SCRIPT
3942
else CompilationKind.DEFAULT
@@ -44,7 +47,8 @@ class SourcePath(
4447

4548
fun parseIfChanged(): SourceFile {
4649
if (content != parsed?.text) {
47-
parsed = cp.compiler.createFile(content, path ?: Paths.get("sourceFile.virtual.${language?.associatedFileType?.defaultExtension ?: "kt"}"), kind)
50+
// TODO: Create PsiFile using the stored language instead
51+
parsed = cp.compiler.createKtFile(content, path ?: Paths.get("sourceFile.virtual.$extension"), kind)
4852
}
4953

5054
return this
@@ -66,7 +70,7 @@ class SourcePath(
6670
if (parsed?.text != compiledFile?.text) {
6771
LOG.debug("Compiling {}", path?.fileName)
6872

69-
val (context, container) = cp.compiler.compileFile(parsed!!, allIncludingThis(), kind)
73+
val (context, container) = cp.compiler.compileKtFile(parsed!!, allIncludingThis(), kind)
7074
parseDataWriteLock.withLock {
7175
compiledContext = context
7276
compiledContainer = container
@@ -160,7 +164,7 @@ class SourcePath(
160164
val parse = changed.associateWith { it.parseIfChanged().parsed!! }
161165
val allFiles = all()
162166
beforeCompileCallback.invoke()
163-
val (context, container) = cp.compiler.compileFiles(parse.values, allFiles, kind)
167+
val (context, container) = cp.compiler.compileKtFiles(parse.values, allFiles, kind)
164168

165169
// Update cache
166170
for ((f, parsed) in parse) {

server/src/test/kotlin/org/javacs/kt/CompilerTest.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,17 @@ private class FileToEdit {
2929

3030
@Test fun compileFile() {
3131
val content = Files.readAllLines(file).joinToString("\n")
32-
val original = compiler.createFile(content, file)
33-
val (context, _) = compiler.compileFile(original, listOf(original))
32+
val original = compiler.createKtFile(content, file)
33+
val (context, _) = compiler.compileKtFile(original, listOf(original))
3434
val psi = original.findElementAt(45)!!
3535
val kt = psi.parentsWithSelf.filterIsInstance<KtExpression>().first()
3636

3737
assertThat(context.getType(kt), hasToString("String"))
3838
}
3939

4040
@Test fun newFile() {
41-
val original = compiler.createFile(editedText, file)
42-
val (context, _) = compiler.compileFile(original, listOf(original))
41+
val original = compiler.createKtFile(editedText, file)
42+
val (context, _) = compiler.compileKtFile(original, listOf(original))
4343
val psi = original.findElementAt(46)!!
4444
val kt = psi.parentsWithSelf.filterIsInstance<KtExpression>().first()
4545

@@ -48,15 +48,15 @@ private class FileToEdit {
4848

4949
@Test fun editFile() {
5050
val content = Files.readAllLines(file).joinToString("\n")
51-
val original = compiler.createFile(content, file)
52-
var (context, _) = compiler.compileFile(original, listOf(original))
51+
val original = compiler.createKtFile(content, file)
52+
var (context, _) = compiler.compileKtFile(original, listOf(original))
5353
var psi = original.findElementAt(46)!!
5454
var kt = psi.parentsWithSelf.filterIsInstance<KtExpression>().first()
5555

5656
assertThat(context.getType(kt), hasToString("String"))
5757

58-
val edited = compiler.createFile(editedText, file)
59-
context = compiler.compileFile(edited, listOf(edited)).first
58+
val edited = compiler.createKtFile(editedText, file)
59+
context = compiler.compileKtFile(edited, listOf(edited)).first
6060
psi = edited.findElementAt(46)!!
6161
kt = psi.parentsWithSelf.filterIsInstance<KtExpression>().first()
6262

@@ -66,12 +66,12 @@ private class FileToEdit {
6666
@Test fun editRef() {
6767
val file1 = testResourcesRoot().resolve("hover/Recover.kt")
6868
val content = Files.readAllLines(file1).joinToString("\n")
69-
val original = compiler.createFile(content, file1)
70-
val (context, _) = compiler.compileFile(original, listOf(original))
69+
val original = compiler.createKtFile(content, file1)
70+
val (context, _) = compiler.compileKtFile(original, listOf(original))
7171
val function = original.findElementAt(49)!!.parentsWithSelf.filterIsInstance<KtNamedFunction>().first()
7272
val scope = context.get(BindingContext.LEXICAL_SCOPE, function.bodyExpression)!!
73-
val recompile = compiler.createDeclaration("""private fun singleExpressionFunction() = intFunction()""")
74-
val (recompileContext, _) = compiler.compileExpression(recompile, scope, setOf(original))
73+
val recompile = compiler.createKtDeclaration("""private fun singleExpressionFunction() = intFunction()""")
74+
val (recompileContext, _) = compiler.compileKtExpression(recompile, scope, setOf(original))
7575
val intFunctionRef = recompile.findElementAt(41)!!.parentsWithSelf.filterIsInstance<KtReferenceExpression>().first()
7676
val target = recompileContext.get(BindingContext.REFERENCE_TARGET, intFunctionRef)!!
7777

shared/src/main/kotlin/org/javacs/kt/util/URIs.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ fun parseURI(uri: String): URI = URI.create(runCatching { URLDecoder.decode(uri,
1515

1616
val URI.filePath: Path? get() = runCatching { Paths.get(this) }.getOrNull()
1717

18+
/** Fetches the file extension WITHOUT the dot. */
19+
val URI.fileExtension: String?
20+
get() {
21+
val str = toString()
22+
val dotOffset = str.lastIndexOf(".")
23+
return if (dotOffset < 0) null else str.substring(dotOffset + 1)
24+
}
25+
1826
fun describeURIs(uris: Collection<URI>): String =
1927
if (uris.isEmpty()) "0 files"
2028
else if (uris.size > 5) "${uris.size} files"

0 commit comments

Comments
 (0)