Skip to content

Commit 1fd8b0e

Browse files
authored
Merge pull request #709 from zhelenskiy/zhelenskiy/remove_internals
Replace compiler internal API with compiler main function call besides completion
2 parents 43a8023 + d4ae695 commit 1fd8b0e

21 files changed

+574
-342
lines changed

common/src/main/kotlin/component/KotlinEnvironment.kt

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import com.intellij.openapi.util.Disposer
44
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
55
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
66
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
7-
import org.jetbrains.kotlin.cli.common.createPhaseConfig
8-
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
9-
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
107
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
11-
import org.jetbrains.kotlin.cli.js.K2JsIrCompiler
128
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
139
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
1410
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
@@ -18,7 +14,6 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
1814
import org.jetbrains.kotlin.config.CompilerConfiguration
1915
import org.jetbrains.kotlin.config.JVMConfigurationKeys
2016
import org.jetbrains.kotlin.config.languageVersionSettings
21-
import org.jetbrains.kotlin.ir.backend.js.jsPhases
2217
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
2318
import org.jetbrains.kotlin.serialization.js.JsModuleDescriptor
2419
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
@@ -39,15 +34,14 @@ class KotlinEnvironment(
3934
* See [org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments] and
4035
* [org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments] for list of possible flags
4136
*/
42-
private val additionalCompilerArguments: List<String> = listOf(
37+
val additionalCompilerArguments: List<String> = listOf(
4338
"-opt-in=kotlin.ExperimentalStdlibApi",
4439
"-opt-in=kotlin.time.ExperimentalTime",
4540
"-opt-in=kotlin.RequiresOptIn",
4641
"-opt-in=kotlin.ExperimentalUnsignedTypes",
4742
"-opt-in=kotlin.contracts.ExperimentalContracts",
4843
"-opt-in=kotlin.experimental.ExperimentalTypeInference",
4944
"-Xcontext-receivers",
50-
"-XXLanguage:+RangeUntilOperator"
5145
)
5246
}
5347

@@ -81,22 +75,6 @@ class KotlinEnvironment(
8175
put(JSConfigurationKeys.WASM_ENABLE_ASSERTS, false)
8276
}
8377

84-
private val messageCollector = object : MessageCollector {
85-
override fun clear() {}
86-
override fun hasErrors(): Boolean {
87-
return false
88-
}
89-
90-
override fun report(
91-
severity: CompilerMessageSeverity,
92-
message: String,
93-
location: CompilerMessageSourceLocation?
94-
) {
95-
}
96-
}
97-
98-
val jsIrPhaseConfig = createPhaseConfig(jsPhases, K2JsIrCompiler().createArguments(), messageCollector)
99-
10078
private val environment = KotlinCoreEnvironment.createForProduction(
10179
parentDisposable = Disposer.newDisposable(),
10280
configuration = configuration.copy(),
@@ -128,4 +106,4 @@ class KotlinEnvironment(
128106
}
129107
}
130108
}
131-
}
109+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package com.compiler.server.compiler.components
2+
3+
import com.compiler.server.compiler.components.IndexationProvider.Companion.UNRESOLVED_REFERENCE_PREFIX
4+
import com.compiler.server.model.CompilerDiagnostics
5+
import com.compiler.server.model.ErrorDescriptor
6+
import com.compiler.server.model.ProjectSeveriry
7+
import com.compiler.server.model.TextInterval
8+
import org.jetbrains.kotlin.cli.common.CLICompiler
9+
import org.jetbrains.kotlin.cli.common.CLITool
10+
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
11+
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
12+
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
13+
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
14+
import org.jetbrains.kotlin.psi.KtFile
15+
import java.nio.file.Path
16+
import java.nio.file.Paths
17+
import java.util.*
18+
import kotlin.io.path.*
19+
20+
private fun minusOne(value: Int) = if (value > 0) value - 1 else value
21+
22+
sealed class CompilationResult<out T> {
23+
abstract val compilerDiagnostics: CompilerDiagnostics
24+
25+
fun <R> flatMap(action: (T) -> CompilationResult<R>): CompilationResult<R> = when (this) {
26+
is Compiled -> {
27+
val innerResult = action(result)
28+
val newDiagnostics = (compilerDiagnostics.map.keys + innerResult.compilerDiagnostics.map.keys).associateWith {
29+
val l1 = compilerDiagnostics.map[it]
30+
val l2 = innerResult.compilerDiagnostics.map[it]
31+
if (l1 != null && l2 != null) l1 + l2 else (l1 ?: l2)!!
32+
}.let(::CompilerDiagnostics)
33+
when (innerResult) {
34+
is Compiled -> innerResult.copy(compilerDiagnostics = newDiagnostics)
35+
is NotCompiled -> innerResult.copy(compilerDiagnostics = newDiagnostics)
36+
}
37+
}
38+
is NotCompiled -> this
39+
}
40+
fun <R> map(action: (T) -> R): CompilationResult<R> = when (this) {
41+
is Compiled -> Compiled(compilerDiagnostics, action(result))
42+
is NotCompiled -> this
43+
}
44+
}
45+
46+
data class Compiled<T>(override val compilerDiagnostics: CompilerDiagnostics, val result: T) : CompilationResult<T>()
47+
48+
data class NotCompiled(override val compilerDiagnostics: CompilerDiagnostics) : CompilationResult<Nothing>()
49+
50+
fun CLICompiler<*>.tryCompilation(inputDirectory: Path, inputFiles: List<Path>, arguments: List<String>): CompilationResult<Unit> = tryCompilation(inputDirectory, inputFiles, arguments) {}
51+
52+
fun <T> CLICompiler<*>.tryCompilation(inputDirectory: Path, inputFiles: List<Path>, arguments: List<String>, onSuccess: () -> T): CompilationResult<T> {
53+
fun Path.outputFilePathString() = inputDirectory.relativize(this).pathString
54+
55+
val diagnosticsMap = mutableMapOf<String, MutableList<ErrorDescriptor>>().apply {
56+
inputFiles.forEach { put(it.outputFilePathString(), mutableListOf()) }
57+
}
58+
val defaultFileName = inputFiles.singleOrNull()?.outputFilePathString() ?: ""
59+
val exitCode = CLITool.doMainNoExit(this, arguments.toTypedArray(), object : MessageRenderer {
60+
override fun renderPreamble(): String = ""
61+
62+
override fun render(
63+
severity: CompilerMessageSeverity,
64+
message: String,
65+
location: CompilerMessageSourceLocation?
66+
): String {
67+
val textInterval = location?.let {
68+
TextInterval(
69+
start = TextInterval.TextPosition(minusOne(location.line), minusOne(location.column)),
70+
end = TextInterval.TextPosition(minusOne(location.lineEnd), minusOne(location.columnEnd))
71+
)
72+
}
73+
val messageSeverity: ProjectSeveriry = when (severity) {
74+
EXCEPTION, ERROR -> ProjectSeveriry.ERROR
75+
STRONG_WARNING, WARNING -> ProjectSeveriry.WARNING
76+
INFO, LOGGING, OUTPUT -> return ""
77+
}
78+
val errorFilePath = location?.path?.let(::Path)?.outputFilePathString() ?: defaultFileName
79+
80+
val className = if (!message.startsWith(UNRESOLVED_REFERENCE_PREFIX) && severity == ERROR) "red_wavy_line" else messageSeverity.name
81+
val errorDescriptor = ErrorDescriptor(textInterval, message, messageSeverity, className)
82+
83+
diagnosticsMap.getOrPut(errorFilePath) { mutableListOf() }.add(errorDescriptor)
84+
return ""
85+
}
86+
87+
override fun renderUsage(usage: String): String =
88+
render(STRONG_WARNING, usage, null)
89+
90+
override fun renderConclusion(): String = ""
91+
92+
override fun getName(): String = "Redirector"
93+
})
94+
val diagnostics = CompilerDiagnostics(diagnosticsMap)
95+
return when {
96+
diagnostics.any { it.severity == ProjectSeveriry.ERROR } -> NotCompiled(diagnostics)
97+
exitCode.code != 0 -> ErrorDescriptor(
98+
severity = ProjectSeveriry.ERROR,
99+
message = "Compiler finished with non-null exit code ${exitCode.code}: ${exitCode.name}",
100+
interval = null
101+
).let { NotCompiled(CompilerDiagnostics(mapOf(defaultFileName to listOf(it)))) }
102+
103+
else -> Compiled(result = onSuccess(), compilerDiagnostics = diagnostics)
104+
}
105+
}
106+
107+
@OptIn(ExperimentalPathApi::class)
108+
fun <T> usingTempDirectory(action: (path: Path) -> T): T {
109+
val path = getTempDirectory()
110+
path.createDirectories()
111+
return try {
112+
action(path)
113+
} finally {
114+
path.deleteRecursively()
115+
}
116+
}
117+
118+
private fun getTempDirectory(): Path {
119+
val dir = System.getProperty("java.io.tmpdir")
120+
val sessionId = UUID.randomUUID().toString().replace("-", "")
121+
return Paths.get(dir, sessionId)
122+
}
123+
124+
fun List<KtFile>.writeToIoFiles(inputDir: Path): List<Path> {
125+
val ioFiles = map { inputDir / it.name }
126+
for ((ioFile, ktFile) in ioFiles zip this) {
127+
ioFile.writeText(ktFile.text)
128+
}
129+
return ioFiles
130+
}
131+
132+
val PATH_SEPARATOR: String = java.io.File.pathSeparator

src/main/kotlin/com/compiler/server/compiler/components/CompletionProvider.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.compiler.server.compiler.components
33
import com.compiler.server.compiler.KotlinFile
44
import com.compiler.server.compiler.KotlinResolutionFacade
55
import com.compiler.server.model.Analysis
6-
import com.compiler.server.model.ErrorDescriptor
6+
import com.compiler.server.model.CompilerDiagnostics
77
import com.compiler.server.model.ProjectType
88
import com.intellij.psi.PsiElement
99
import com.intellij.psi.tree.TokenSet
@@ -72,11 +72,11 @@ class CompletionProvider(
7272
importVariants(file, prefix, errors, line, character, projectType)
7373
} else emptyList()
7474
descriptorInfo.descriptors.toMutableList().apply {
75-
sortWith(Comparator { a, b ->
75+
sortWith { a, b ->
7676
val (a1, a2) = a.presentableName()
7777
val (b1, b2) = b.presentableName()
7878
("$a1$a2").compareTo("$b1$b2", true)
79-
})
79+
}
8080
}.mapNotNull { descriptor -> completionVariantFor(prefix, descriptor, element) } +
8181
keywordsCompletionVariants(KtTokens.KEYWORDS, prefix) +
8282
keywordsCompletionVariants(KtTokens.SOFT_KEYWORDS, prefix) +
@@ -113,15 +113,16 @@ class CompletionProvider(
113113
private fun importVariants(
114114
file: KotlinFile,
115115
prefix: String,
116-
errors: Map<String, List<ErrorDescriptor>>,
116+
compilerDiagnostics: CompilerDiagnostics,
117117
line: Int,
118118
character: Int,
119119
projectType: ProjectType
120120
): List<Completion> {
121121
val importCompletionVariants = indexationProvider.getClassesByName(prefix, projectType)
122122
?.map { it.toCompletion() } ?: emptyList()
123-
val currentErrors = errors[file.kotlinFile.name]?.filter {
124-
it.interval.start.line == line &&
123+
val currentErrors = compilerDiagnostics.map[file.kotlinFile.name]?.filter {
124+
it.interval != null &&
125+
it.interval.start.line == line &&
125126
it.interval.start.ch <= character &&
126127
it.interval.end.line == line &&
127128
it.interval.end.ch >= character &&
@@ -257,7 +258,7 @@ class CompletionProvider(
257258

258259
// This code is a fragment of org.jetbrains.kotlin.idea.completion.CompletionSession from Kotlin IDE Plugin
259260
// with a few simplifications which were possible because webdemo has very restricted environment (and well,
260-
// because requirements on compeltion' quality in web-demo are lower)
261+
// because requirements on completion' quality in web-demo are lower)
261262
private inner class VisibilityFilter(
262263
private val inDescriptor: DeclarationDescriptor,
263264
private val bindingContext: BindingContext,

src/main/kotlin/com/compiler/server/compiler/components/ErrorAnalyzer.kt

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import com.intellij.psi.PsiFile
99
import component.KotlinEnvironment
1010
import model.Completion
1111
import org.jetbrains.kotlin.analyzer.AnalysisResult
12-
import org.jetbrains.kotlin.cli.js.klib.TopDownAnalyzerFacadeForWasmJs
1312
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
1413
import org.jetbrains.kotlin.cli.js.klib.TopDownAnalyzerFacadeForJSIR
14+
import org.jetbrains.kotlin.cli.js.klib.TopDownAnalyzerFacadeForWasmJs
1515
import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
1616
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
1717
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.incremental.components.InlineConstTracker
3131
import org.jetbrains.kotlin.incremental.components.LookupTracker
3232
import org.jetbrains.kotlin.ir.backend.js.MainModule
3333
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
34-
import org.jetbrains.kotlin.js.config.ErrorTolerancePolicy
3534
import org.jetbrains.kotlin.js.config.JsConfig
3635
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
3736
import org.jetbrains.kotlin.name.Name
@@ -60,15 +59,15 @@ class ErrorAnalyzer(
6059
projectType: ProjectType
6160
): ErrorsAndAnalysis {
6261
val analysis = when {
63-
projectType.isJvmRelated() ->analysisOf(files, coreEnvironment)
62+
projectType.isJvmRelated() -> analysisOf(files, coreEnvironment)
6463
projectType.isJsRelated() -> analyzeFileForJs(files, coreEnvironment)
6564
projectType == ProjectType.WASM -> analyzeFileForWasm(files, coreEnvironment)
6665
else -> throw IllegalArgumentException("Unknown platform: $projectType")
6766
}
6867
return ErrorsAndAnalysis(
6968
errorsFrom(
7069
analysis.analysisResult.bindingContext.diagnostics.all(),
71-
files.associate { it.name to anylizeErrorsFrom(it, projectType) },
70+
CompilerDiagnostics(files.associate { it.name to analyzeErrorsFrom(it, projectType) }),
7271
projectType
7372
),
7473
analysis
@@ -233,24 +232,13 @@ class ErrorAnalyzer(
233232

234233
fun errorsFrom(
235234
diagnostics: Collection<Diagnostic>,
236-
errors: Map<String, List<ErrorDescriptor>>,
235+
compilerDiagnostics: CompilerDiagnostics,
237236
projectType: ProjectType
238-
): Map<String, List<ErrorDescriptor>> {
239-
return (errors and errorsFrom(diagnostics, projectType)).map { (fileName, errors) ->
240-
fileName to errors.sortedWith { o1, o2 ->
241-
val line = o1.interval.start.line.compareTo(o2.interval.start.line)
242-
when (line) {
243-
0 -> o1.interval.start.ch.compareTo(o2.interval.start.ch)
244-
else -> line
245-
}
246-
}
247-
}.toMap()
248-
}
249-
250-
fun isOnlyWarnings(errors: Map<String, List<ErrorDescriptor>>) =
251-
errors.none { it.value.any { error -> error.severity == ProjectSeveriry.ERROR } }
237+
): CompilerDiagnostics = (compilerDiagnostics.map and errorsFrom(diagnostics, projectType)).map { (fileName, errors) ->
238+
fileName to errors.sortedWith(Comparator.comparing({ it.interval?.start }, nullsFirst()))
239+
}.toMap().let(::CompilerDiagnostics)
252240

253-
private fun anylizeErrorsFrom(file: PsiFile, projectType: ProjectType): List<ErrorDescriptor> {
241+
private fun analyzeErrorsFrom(file: PsiFile, projectType: ProjectType): List<ErrorDescriptor> {
254242
class Visitor : PsiElementVisitor() {
255243
val errors = mutableListOf<PsiErrorElement>()
256244
override fun visitElement(element: PsiElement) {
@@ -362,4 +350,4 @@ class ErrorAnalyzer(
362350
}
363351
}
364352

365-
data class ErrorsAndAnalysis(val errors: Map<String, List<ErrorDescriptor>>, val analysis: Analysis)
353+
data class ErrorsAndAnalysis(val compilerDiagnostics: CompilerDiagnostics, val analysis: Analysis)

0 commit comments

Comments
 (0)