Add snippet result type to analysis output of REPL IDE services
This commit is contained in:
committed by
Ilya Chernikov
parent
c496b93218
commit
8a1b44cc2b
+95
-67
@@ -6,12 +6,16 @@
|
||||
package org.jetbrains.kotlin.scripting.ide_services.compiler
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptDiagnosticsMessageCollector
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.failure
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.withMessageCollector
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.IdeLikeReplCodeAnalyzer
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.getKJvmCompletion
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.prepareCodeForCompletion
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.*
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
@@ -29,58 +33,33 @@ class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfigurat
|
||||
configuration: ScriptCompilationConfiguration
|
||||
): ResultWithDiagnostics<ReplCompletionResult> =
|
||||
withMessageCollector(snippet) { messageCollector ->
|
||||
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
|
||||
return it
|
||||
}
|
||||
|
||||
val cursorAbs = cursor.calcAbsolute(snippet)
|
||||
|
||||
val newText =
|
||||
prepareCodeForCompletion(snippet.text, cursorAbs)
|
||||
val newSnippet = object : SourceCode {
|
||||
override val text: String
|
||||
get() = newText
|
||||
override val name: String?
|
||||
get() = snippet.name
|
||||
override val locationId: String?
|
||||
get() = snippet.locationId
|
||||
|
||||
}
|
||||
|
||||
val compilationState = state.getCompilationState(initialConfiguration)
|
||||
|
||||
val (_, errorHolder, snippetKtFile) = prepareForAnalyze(
|
||||
newSnippet,
|
||||
messageCollector,
|
||||
compilationState,
|
||||
checkSyntaxErrors = false
|
||||
).valueOr { return@withMessageCollector it }
|
||||
|
||||
val analysisResult =
|
||||
compilationState.analyzerEngine.statelessAnalyzeWithImportedScripts(snippetKtFile, emptyList(), scriptPriority.get() + 1)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
|
||||
val (_, bindingContext, resolutionFacade, moduleDescriptor) = when (analysisResult) {
|
||||
is IdeLikeReplCodeAnalyzer.ReplLineAnalysisResultWithStateless.Stateless -> {
|
||||
analysisResult
|
||||
val analyzeResult = analyzeWithCursor(
|
||||
messageCollector, snippet, configuration, cursor
|
||||
) { snippet, cursorAbs ->
|
||||
val newText =
|
||||
prepareCodeForCompletion(snippet.text, cursorAbs)
|
||||
object : SourceCode {
|
||||
override val text: String
|
||||
get() = newText
|
||||
override val name: String?
|
||||
get() = snippet.name
|
||||
override val locationId: String?
|
||||
get() = snippet.locationId
|
||||
}
|
||||
else -> return failure(
|
||||
newSnippet,
|
||||
messageCollector,
|
||||
"Unexpected result ${analysisResult::class.java}"
|
||||
)
|
||||
}
|
||||
|
||||
return getKJvmCompletion(
|
||||
snippetKtFile,
|
||||
bindingContext,
|
||||
resolutionFacade,
|
||||
moduleDescriptor,
|
||||
cursorAbs
|
||||
).asSuccess(messageCollector.diagnostics)
|
||||
with(analyzeResult.valueOr { return it }) {
|
||||
return getKJvmCompletion(
|
||||
ktScript,
|
||||
bindingContext,
|
||||
resolutionFacade,
|
||||
moduleDescriptor,
|
||||
cursorAbs
|
||||
).asSuccess(messageCollector.diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<ScriptDiagnostic>.toAnalyzeResult() = (filter {
|
||||
private fun List<ScriptDiagnostic>.toAnalyzeResultSequence() = (filter {
|
||||
when (it.severity) {
|
||||
ScriptDiagnostic.Severity.FATAL,
|
||||
ScriptDiagnostic.Severity.ERROR,
|
||||
@@ -96,24 +75,73 @@ class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfigurat
|
||||
configuration: ScriptCompilationConfiguration
|
||||
): ResultWithDiagnostics<ReplAnalyzerResult> {
|
||||
return withMessageCollector(snippet) { messageCollector ->
|
||||
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
|
||||
return it
|
||||
val analyzeResult = analyzeWithCursor(
|
||||
messageCollector, snippet, configuration
|
||||
)
|
||||
|
||||
with(analyzeResult.valueOr { return it }) {
|
||||
val resultRenderedType = resultProperty?.let {
|
||||
DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it.type)
|
||||
}
|
||||
return ReplAnalyzerResult {
|
||||
analysisDiagnostics(messageCollector.diagnostics.toAnalyzeResultSequence())
|
||||
renderedResultType(resultRenderedType)
|
||||
}.asSuccess()
|
||||
}
|
||||
|
||||
val compilationState = state.getCompilationState(initialConfiguration)
|
||||
|
||||
val (_, errorHolder, snippetKtFile) = prepareForAnalyze(
|
||||
snippet,
|
||||
messageCollector,
|
||||
compilationState,
|
||||
checkSyntaxErrors = true
|
||||
).valueOr { return@withMessageCollector messageCollector.diagnostics.toAnalyzeResult().asSuccess() }
|
||||
|
||||
val analysisResult =
|
||||
compilationState.analyzerEngine.statelessAnalyzeWithImportedScripts(snippetKtFile, emptyList(), scriptPriority.get() + 1)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
|
||||
messageCollector.diagnostics.toAnalyzeResult().asSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
private fun analyzeWithCursor(
|
||||
messageCollector: ScriptDiagnosticsMessageCollector,
|
||||
snippet: SourceCode,
|
||||
configuration: ScriptCompilationConfiguration,
|
||||
cursor: SourceCode.Position? = null,
|
||||
getNewSnippet: (SourceCode, Int) -> SourceCode = { code, _ -> code }
|
||||
): ResultWithDiagnostics<AnalyzeWithCursorResult> {
|
||||
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
|
||||
return it
|
||||
}
|
||||
|
||||
val cursorAbs = cursor?.calcAbsolute(snippet) ?: -1
|
||||
val newSnippet = if (cursorAbs == -1) snippet else getNewSnippet(snippet, cursorAbs)
|
||||
|
||||
val compilationState = state.getCompilationState(initialConfiguration)
|
||||
|
||||
val (_, errorHolder, snippetKtFile) = prepareForAnalyze(
|
||||
newSnippet,
|
||||
messageCollector,
|
||||
compilationState,
|
||||
checkSyntaxErrors = false
|
||||
).valueOr { return it }
|
||||
|
||||
val analysisResult =
|
||||
compilationState.analyzerEngine.statelessAnalyzeWithImportedScripts(snippetKtFile, emptyList(), scriptPriority.get() + 1)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
|
||||
val (_, bindingContext, resolutionFacade, moduleDescriptor, resultProperty) = when (analysisResult) {
|
||||
is IdeLikeReplCodeAnalyzer.ReplLineAnalysisResultWithStateless.Stateless -> {
|
||||
analysisResult
|
||||
}
|
||||
else -> return failure(
|
||||
newSnippet,
|
||||
messageCollector,
|
||||
"Unexpected result ${analysisResult::class.java}"
|
||||
)
|
||||
}
|
||||
|
||||
return AnalyzeWithCursorResult(
|
||||
snippetKtFile, bindingContext, resolutionFacade, moduleDescriptor, cursorAbs, resultProperty
|
||||
).asSuccess()
|
||||
}
|
||||
|
||||
companion object {
|
||||
data class AnalyzeWithCursorResult(
|
||||
val ktScript: KtFile,
|
||||
val bindingContext: BindingContext,
|
||||
val resolutionFacade: KotlinResolutionFacadeForRepl,
|
||||
val moduleDescriptor: ModuleDescriptor,
|
||||
val cursorAbs: Int,
|
||||
val resultProperty: PropertyDescriptor?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-3
@@ -10,6 +10,8 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.container.getService
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
@@ -25,7 +27,8 @@ class IdeLikeReplCodeAnalyzer(private val environment: KotlinCoreEnvironment) :
|
||||
override val diagnostics: Diagnostics,
|
||||
val bindingContext: BindingContext,
|
||||
val resolutionFacade: KotlinResolutionFacadeForRepl,
|
||||
val moduleDescriptor: ModuleDescriptor
|
||||
val moduleDescriptor: ModuleDescriptor,
|
||||
val resultProperty: PropertyDescriptor?,
|
||||
) :
|
||||
ReplLineAnalysisResultWithStateless {
|
||||
override val scriptDescriptor: ClassDescriptorWithResolutionScopes? get() = null
|
||||
@@ -51,7 +54,9 @@ class IdeLikeReplCodeAnalyzer(private val environment: KotlinCoreEnvironment) :
|
||||
)
|
||||
replState.submitLine(linePsi)
|
||||
|
||||
topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi) + importedScripts)
|
||||
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi) + importedScripts)
|
||||
|
||||
val resultPropertyDescriptor = (context.scripts[linePsi.script] as? ScriptDescriptor)?.resultValue
|
||||
|
||||
val moduleDescriptor = container.getService(ModuleDescriptor::class.java)
|
||||
val resolutionFacade =
|
||||
@@ -61,7 +66,8 @@ class IdeLikeReplCodeAnalyzer(private val environment: KotlinCoreEnvironment) :
|
||||
diagnostics,
|
||||
trace.bindingContext,
|
||||
resolutionFacade,
|
||||
moduleDescriptor
|
||||
moduleDescriptor,
|
||||
resultPropertyDescriptor,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user