Skip to content

Commit 96e0d2e

Browse files
committedMar 4, 2020
Switch to a new REPL API
1 parent 873e547 commit 96e0d2e

File tree

14 files changed

+148
-116
lines changed

14 files changed

+148
-116
lines changed
 

‎build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ configurations {
155155
dependencies {
156156
compile project(":jupyter-lib")
157157
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
158-
compile "org.jetbrains.kotlin:kotlin-scripting-jvm-host-embeddable:$kotlinVersion"
158+
compile "org.jetbrains.kotlin:kotlin-scripting-ide-services-impl-embeddable:$kotlinVersion"
159159
compile "org.jetbrains.kotlin:kotlin-scripting-common:$kotlinVersion"
160160
compile "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:$kotlinVersion"
161161
compile "org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package jupyter.kotlin.receivers
2+
3+
class ConstReceiver(val value: Int)

‎src/main/kotlin/org/jetbrains/kotlin/jupyter/receivers.kt ‎jupyter-lib/src/main/kotlin/jupyter/kotlin/receivers/TypeProviderReceiver.kt

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
package org.jetbrains.kotlin.jupyter
2-
3-
class ConstReceiver(val value: Int)
1+
package jupyter.kotlin.receivers
42

53
class TypeProviderReceiver {
64

75
fun generateCode(values: List<Int>): List<String> {
86
val properties = (0 until values.size)
97
.map { "val value$it : Int get() = list[$it]" }
10-
.joinToLines()
8+
.joinToString("\n")
119

1210
val classDeclaration = """
1311
class TypedIntList###(val list: List<Int>): List<Int> by list {

‎resources/notebook-extension/kernel.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ define(function(){
153153
return -1;
154154
}
155155

156-
function getTokenBounds(buf, cursor) {
156+
function getTokenBounds(buf, cursor, editor) {
157157
if (cursor > buf.length) {
158158
throw new Error("Position " + cursor + " does not exist in code snippet <" + buf + ">");
159159
}
@@ -174,7 +174,9 @@ define(function(){
174174
tokenBeforeCursor: buf.substring(start, cursor),
175175
after: buf.substring(end, buf.length),
176176
start: start,
177-
end: end
177+
end: end,
178+
posStart: editor.posFromIndex(start),
179+
posEnd: editor.posFromIndex(end)
178180
}
179181
}
180182

@@ -211,7 +213,7 @@ define(function(){
211213
cursor_pos = utils.js_idx_to_char_idx(cursor_pos, text);
212214

213215
var prevBounds = this.tokenBounds;
214-
var bounds = getTokenBounds(text, cursor_pos);
216+
var bounds = getTokenBounds(text, cursor_pos, this.editor);
215217
this.tokenBounds = bounds;
216218
if (prevBounds && this.raw_result) {
217219
if (bounds.before === prevBounds.before &&
@@ -223,6 +225,10 @@ define(function(){
223225
if (displayName[0] === '`')
224226
displayName = displayName.substring(1, displayName.length - 1);
225227
return displayName.startsWith(bounds.tokenBeforeCursor)
228+
}).map((completion) => {
229+
completion.from = bounds.posStart;
230+
completion.to = bounds.posEnd;
231+
return completion;
226232
});
227233

228234
if (newResult.length > 0) {

‎src/main/kotlin/org/jetbrains/kotlin/jupyter/config.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ val LocalSettingsPath = Paths.get(System.getProperty("user.home"), ".jupyter_kot
2323
val GitHubApiHost = "api.github.com"
2424
val GitHubRepoOwner = "kotlin"
2525
val GitHubRepoName = "kotlin-jupyter"
26-
val GitHubBranchName = "demo-jan2020"
26+
val GitHubBranchName = "psi_completion_with_typing"
2727
val GitHubApiPrefix = "https://$GitHubApiHost/repos/$GitHubRepoOwner/$GitHubRepoName"
2828

2929
val LibraryDescriptorExt = "json"

‎src/main/kotlin/org/jetbrains/kotlin/jupyter/protocol.kt

+10-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import java.io.PrintStream
1313
import java.lang.reflect.InvocationTargetException
1414
import java.util.concurrent.atomic.AtomicLong
1515
import kotlin.concurrent.timer
16+
import kotlin.script.experimental.api.ScriptDiagnostic
1617

1718
enum class ResponseState {
1819
Ok, Error, Abort
@@ -356,17 +357,18 @@ fun JupyterConnection.evalWithIO(config: OutputConfig, srcMessage: Message, body
356357
forkedOut.flush()
357358
forkedError.flush()
358359

359-
val additionalInfo = ex.errorResult.location?.let {
360-
val errorMessage = ex.errorResult.message
361-
jsonObject("lineStart" to it.line, "colStart" to it.column,
362-
"lineEnd" to it.lineEnd, "colEnd" to it.columnEnd,
360+
val firstDiagnostic = ex.firstDiagnostics
361+
val additionalInfo = firstDiagnostic?.location?.let {
362+
val errorMessage = firstDiagnostic.message
363+
jsonObject("lineStart" to it.start.line, "colStart" to it.start.col,
364+
"lineEnd" to (it.end?.line ?: -1), "colEnd" to (it.end?.col ?: -1),
363365
"message" to errorMessage,
364-
"path" to it.path)
366+
"path" to firstDiagnostic.sourcePath.orEmpty())
365367
} ?: jsonObject()
366368

367369
ErrorResponseWithMessage(
368370
textResult("Error!"),
369-
ex.errorResult.message,
371+
ex.message,
370372
ex.javaClass.canonicalName,
371373
ex.message ?: "",
372374
ex.stackTrace.map { it.toString() },
@@ -376,8 +378,8 @@ fun JupyterConnection.evalWithIO(config: OutputConfig, srcMessage: Message, body
376378

377379
val stdErr = StringBuilder()
378380
with(stdErr) {
379-
val cause = ex.errorResult.cause
380-
if (cause == null) appendln(ex.errorResult.message)
381+
val cause = ex.cause
382+
if (cause == null) appendln(ex.message)
381383
else {
382384
when (cause) {
383385
is InvocationTargetException -> appendln(cause.targetException.toString())

‎src/main/kotlin/org/jetbrains/kotlin/jupyter/repl.kt

+52-55
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,42 @@ package org.jetbrains.kotlin.jupyter
33
import jupyter.kotlin.*
44
import jupyter.kotlin.KotlinContext
55
import jupyter.kotlin.KotlinReceiver
6-
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
7-
import org.jetbrains.kotlin.cli.common.repl.*
86
import org.jetbrains.kotlin.config.KotlinCompilerVersion
97
import org.jetbrains.kotlin.jupyter.repl.completion.CompletionResult
108
import org.jetbrains.kotlin.jupyter.repl.completion.KotlinCompleter
119
import org.jetbrains.kotlin.jupyter.repl.completion.ListErrorsResult
10+
import org.jetbrains.kotlin.jupyter.repl.completion.SourceCodeImpl
1211
import org.jetbrains.kotlin.jupyter.repl.reflect.ContextUpdater
13-
import org.jetbrains.kotlin.jupyter.repl.reflect.lines
1412
import org.jetbrains.kotlin.jupyter.repl.spark.ClassWriter
13+
import org.jetbrains.kotlin.scripting.ide_services.compiler.KJvmReplCompilerImpl
14+
import org.jetbrains.kotlin.scripting.ide_services.evaluator.KJvmReplEvaluatorImpl
1515
import java.io.File
1616
import java.net.URLClassLoader
1717
import java.util.*
18-
import java.util.concurrent.locks.ReentrantReadWriteLock
1918
import kotlin.script.dependencies.ScriptContents
2019
import kotlin.script.experimental.api.*
2120
import kotlin.script.experimental.host.withDefaultsFrom
2221
import kotlin.script.experimental.jvm.*
23-
import kotlin.script.experimental.jvmhost.repl.JvmReplCompiler
24-
import kotlin.script.experimental.jvmhost.repl.JvmReplEvaluator
22+
import kotlin.script.experimental.util.hasErrors
23+
import kotlin.script.experimental.util.isIncomplete
24+
import kotlin.script.experimental.util.renderError
2525

2626
data class EvalResult(val resultValue: Any?)
2727

28-
data class CheckResult(val codeLine: LineId, val isComplete: Boolean = true)
28+
data class CheckResult(val isComplete: Boolean = true)
2929

3030
open class ReplException(message: String, cause: Throwable? = null) : Exception(message, cause)
3131

32-
class ReplEvalRuntimeException(val errorResult: ReplEvalResult.Error.Runtime) : ReplException(errorResult.message, errorResult.cause)
32+
class ReplEvalRuntimeException(message: String, cause: Throwable? = null) : ReplException(message, cause)
3333

34-
class ReplCompilerException(val errorResult: ReplCompileResult.Error) : ReplException(errorResult.message) {
35-
constructor (checkResult: ReplCheckResult.Error) : this(ReplCompileResult.Error(checkResult.message, checkResult.location))
36-
constructor (incompleteResult: ReplCompileResult.Incomplete) : this(ReplCompileResult.Error("Incomplete Code", null))
37-
constructor (checkResult: ReplEvalResult.Error.CompileTime) : this(ReplCompileResult.Error(checkResult.message, checkResult.location))
38-
constructor (incompleteResult: ReplEvalResult.Incomplete) : this(ReplCompileResult.Error("Incomplete Code", null))
39-
constructor (historyMismatchResult: ReplEvalResult.HistoryMismatch) : this(ReplCompileResult.Error("History Mismatch", CompilerMessageLocation.create(null, historyMismatchResult.lineNo, 0, null)))
40-
constructor (message: String) : this(ReplCompileResult.Error(message, null))
34+
class ReplCompilerException(val errorResult: ResultWithDiagnostics.Failure? = null, message: String? = null)
35+
: ReplException(message ?: errorResult?.renderError() ?: "") {
36+
37+
val firstDiagnostics = errorResult?.reports?.firstOrNull {
38+
it.severity == ScriptDiagnostic.Severity.ERROR || it.severity == ScriptDiagnostic.Severity.FATAL
39+
}
40+
41+
constructor(message: String): this(null, message)
4142
}
4243

4344
enum class ExecutedCodeLogging {
@@ -241,25 +242,17 @@ class ReplForJupyterImpl(val scriptClasspath: List<File> = emptyList(),
241242

242243
private var executionCounter = 0
243244

244-
private val compiler: IDELikeReplCompiler by lazy {
245-
JvmReplCompiler(compilerConfiguration)
245+
private val compiler: KJvmReplCompilerImpl by lazy {
246+
KJvmReplCompilerImpl()
246247
}
247248

248-
private val evaluator: ReplEvaluator by lazy {
249-
JvmReplEvaluator(evaluatorConfiguration)
249+
private val evaluator: KJvmReplEvaluatorImpl by lazy {
250+
KJvmReplEvaluatorImpl()
250251
}
251252

252-
private val stateLock = ReentrantReadWriteLock()
253-
254-
private val compilerState = compiler.createState(stateLock)
255-
256-
private val evaluatorState = evaluator.createState(stateLock)
257-
258-
private val state = AggregatedReplStageState(compilerState, evaluatorState, stateLock)
259-
260253
private val completer = KotlinCompleter()
261254

262-
private val contextUpdater = ContextUpdater(state, ctx)
255+
private val contextUpdater = ContextUpdater(ctx, evaluator)
263256

264257
private val typeProvidersProcessor: TypeProvidersProcessor = TypeProvidersProcessorImpl(contextUpdater)
265258

@@ -270,12 +263,12 @@ class ReplForJupyterImpl(val scriptClasspath: List<File> = emptyList(),
270263
private val scheduledExecutions = LinkedList<Code>()
271264

272265
override fun checkComplete(executionNumber: Long, code: String): CheckResult {
273-
val codeLine = ReplCodeLine(executionNumber.toInt(), 0, code)
274-
return when (val result = compiler.check(compilerState, codeLine)) {
275-
is ReplCheckResult.Error -> throw ReplCompilerException(result)
276-
is ReplCheckResult.Ok -> CheckResult(LineId(codeLine), true)
277-
is ReplCheckResult.Incomplete -> CheckResult(LineId(codeLine), false)
278-
else -> throw IllegalStateException("Unknown check result type ${result}")
266+
val codeLine = SourceCodeImpl(executionNumber.toInt(), code)
267+
val result = compiler.analyze(codeLine, 0, compilerConfiguration)
268+
return when {
269+
result.isIncomplete() -> CheckResult(false)
270+
result.hasErrors() -> throw ReplException(result.renderError())
271+
else -> CheckResult(true)
279272
}
280273
}
281274

@@ -329,7 +322,7 @@ class ReplForJupyterImpl(val scriptClasspath: List<File> = emptyList(),
329322
typeRenderers.putAll(p.typeRenderers.map { it.className to it.code })
330323
}
331324

332-
private fun lastReplLine() = state.lines[0]
325+
private fun lastReplLine() = evaluator.lastEvaluatedSnippet()?.kInstance
333326

334327
override fun eval(code: String, displayHandler: ((Any) -> Unit)?, jupyterId: Int): EvalResult {
335328
synchronized(this) {
@@ -412,14 +405,16 @@ class ReplForJupyterImpl(val scriptClasspath: List<File> = emptyList(),
412405

413406
private val completionQueue = LockQueue<CompletionResult, CompletionArgs>()
414407
override suspend fun complete(code: String, cursor: Int, callback: (CompletionResult) -> Unit) = doWithLock(CompletionArgs(code, cursor, callback), completionQueue, CompletionResult.Empty(code, cursor)) {
415-
completer.complete(compiler, compilerState, code, executionCounter++, cursor)
408+
//val preprocessed = preprocessCode(code)
409+
completer.complete(compiler, compilerConfiguration, code, executionCounter++, cursor)
416410
}
417411

418412
private val listErrorsQueue = LockQueue<ListErrorsResult, ListErrorsArgs>()
419413
override suspend fun listErrors(code: String, callback: (ListErrorsResult) -> Unit) = doWithLock(ListErrorsArgs(code, callback), listErrorsQueue, ListErrorsResult(code)) {
420-
val codeLine = ReplCodeLine(executionCounter++, 0, code)
421-
val errorsList = compiler.listErrors(compilerState, codeLine)
422-
ListErrorsResult(code, errorsList)
414+
//val preprocessed = preprocessCode(code)
415+
val codeLine = SourceCodeImpl(executionCounter++, code)
416+
val errorsList = compiler.analyze(codeLine, 0, compilerConfiguration)
417+
ListErrorsResult(code, errorsList.valueOrThrow())
423418
}
424419

425420
private fun <T, Args: LockQueueArgs<T>> doWithLock(args: Args, queue: LockQueue<T, Args>, default: T, action: (Args) -> T) {
@@ -467,29 +462,31 @@ class ReplForJupyterImpl(val scriptClasspath: List<File> = emptyList(),
467462
if (executedCodeLogging == ExecutedCodeLogging.All)
468463
println(code)
469464
val id = executionCounter++
470-
val codeLine = ReplCodeLine(id, 0, code)
471-
when (val compileResult = compiler.compile(compilerState, codeLine)) {
472-
is ReplCompileResult.CompiledClasses -> {
473-
classWriter?.writeClasses(compileResult)
474-
val scriptArgs = ScriptArgsWithTypes(arrayOf(this), arrayOf(KotlinKernelHost::class))
475-
val result = evaluator.eval(evaluatorState, compileResult, scriptArgs)
465+
val codeLine = SourceCodeImpl(id, code)
466+
when (val compileResultWithDiagnostics = compiler.compile(codeLine, compilerConfiguration)) {
467+
is ResultWithDiagnostics.Success -> {
468+
val compileResult = compileResultWithDiagnostics.value
469+
classWriter?.writeClasses(compileResult())
470+
val repl = this
471+
val currentEvalConfig = ScriptEvaluationConfiguration(evaluatorConfiguration) {
472+
constructorArgs.invoke(repl as KotlinKernelHost)
473+
}
474+
val result = evaluator.eval(compileResult, currentEvalConfig).valueOrThrow()
476475
contextUpdater.update()
477-
return when (result) {
478-
is ReplEvalResult.Error.CompileTime -> throw ReplCompilerException(result)
479-
is ReplEvalResult.Error.Runtime -> throw ReplEvalRuntimeException(result)
480-
is ReplEvalResult.Incomplete -> throw ReplCompilerException(result)
481-
is ReplEvalResult.HistoryMismatch -> throw ReplCompilerException(result)
482-
is ReplEvalResult.UnitResult -> {
476+
477+
val pureResult = result()
478+
return when {
479+
pureResult.isErrorResult -> throw ReplEvalRuntimeException(pureResult.error?.message.orEmpty(), pureResult.error)
480+
pureResult.isUnitResult -> {
483481
InternalEvalResult(Unit, id)
484482
}
485-
is ReplEvalResult.ValueResult -> {
486-
InternalEvalResult(result.value, id)
483+
pureResult.isValueResult -> {
484+
InternalEvalResult(pureResult.result, id)
487485
}
488486
else -> throw IllegalStateException("Unknown eval result type ${this}")
489487
}
490488
}
491-
is ReplCompileResult.Error -> throw ReplCompilerException(compileResult)
492-
is ReplCompileResult.Incomplete -> throw ReplCompilerException(compileResult)
489+
is ResultWithDiagnostics.Failure -> throw ReplCompilerException(compileResultWithDiagnostics)
493490
}
494491
}
495492

‎src/main/kotlin/org/jetbrains/kotlin/jupyter/repl/completion/KotlinCompleter.kt

+15-12
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@ package org.jetbrains.kotlin.jupyter.repl.completion
33
import com.beust.klaxon.JsonObject
44
import org.jetbrains.annotations.TestOnly
55
import org.jetbrains.kotlin.jupyter.jsonObject
6-
import org.jetbrains.kotlin.cli.common.repl.IReplStageState
7-
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
8-
import org.jetbrains.kotlin.cli.common.repl.IDELikeReplCompiler
9-
import org.jetbrains.kotlin.utils.CompletionVariant
10-
import org.jetbrains.kotlin.utils.KotlinReplError
116
import java.io.PrintWriter
127
import java.io.StringWriter
8+
import kotlin.script.experimental.api.*
139

1410
enum class CompletionStatus(private val value: String) {
1511
OK("ok"),
@@ -32,7 +28,7 @@ abstract class CompletionResult(
3228
open class Success(
3329
private val matches: List<String>,
3430
private val bounds: CompletionTokenBounds,
35-
private val metadata: List<CompletionVariant>,
31+
private val metadata: List<ReplCompletionVariant>,
3632
private val text: String,
3733
private val cursor: Int
3834
): CompletionResult(CompletionStatus.OK) {
@@ -93,7 +89,7 @@ abstract class CompletionResult(
9389
}
9490
}
9591

96-
data class ListErrorsResult(val code: String, val errors: List<KotlinReplError> = emptyList()) {
92+
data class ListErrorsResult(val code: String, val errors: Iterable<ReplDiagnosticMessage> = emptyList()) {
9793
fun toJson(): JsonObject {
9894
return jsonObject("code" to code,
9995
"errors" to errors.map {
@@ -115,15 +111,22 @@ data class ListErrorsResult(val code: String, val errors: List<KotlinReplError>
115111
}
116112
}
117113

114+
internal class SourceCodeImpl(number: Int, override val text: String) : SourceCode {
115+
override val name: String? = "Line_$number"
116+
override val locationId: String? = "location_$number"
117+
}
118+
118119
class KotlinCompleter {
119-
fun complete(compiler: IDELikeReplCompiler, compilerState: IReplStageState<*>, code: String, id: Int, cursor: Int): CompletionResult {
120+
fun complete(compiler: ReplCompleter, configuration: ScriptCompilationConfiguration, code: String, id: Int, cursor: Int): CompletionResult {
120121
return try {
121-
val codeLine = ReplCodeLine(id, 0, code)
122-
val completionList = compiler.complete(compilerState, codeLine, cursor)
122+
val codeLine = SourceCodeImpl(id, code)
123+
val completionResult = compiler.complete(codeLine, cursor, configuration)
123124

124-
val bounds = getTokenBounds(code, cursor)
125+
completionResult.valueOrNull()?.toList()?.let { completionList ->
126+
val bounds = getTokenBounds(code, cursor)
127+
CompletionResult.Success(completionList.map { it.text }, bounds, completionList, code, cursor)
128+
} ?: CompletionResult.Empty(code, cursor)
125129

126-
CompletionResult.Success(completionList.map { it.text }, bounds, completionList, code, cursor)
127130
} catch (e: Exception) {
128131
val sw = StringWriter()
129132
e.printStackTrace(PrintWriter(sw))

‎src/main/kotlin/org/jetbrains/kotlin/jupyter/repl/reflect/ContextUpdater.kt

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package org.jetbrains.kotlin.jupyter.repl.reflect
33
import jupyter.kotlin.KotlinContext
44
import jupyter.kotlin.KotlinFunctionInfo
55
import jupyter.kotlin.KotlinVariableInfo
6-
import org.jetbrains.kotlin.cli.common.repl.AggregatedReplStageState
6+
import org.jetbrains.kotlin.jupyter.instances
7+
import org.jetbrains.kotlin.scripting.ide_services.evaluator.KJvmReplEvaluatorImpl
78
import org.slf4j.LoggerFactory
89

910
import java.lang.reflect.Field
@@ -15,12 +16,12 @@ import kotlin.reflect.jvm.kotlinProperty
1516
* ContextUpdater updates current user-defined functions and variables
1617
* to use in completion and KotlinContext.
1718
*/
18-
class ContextUpdater(private val state: AggregatedReplStageState<*, *>,
19-
val context: KotlinContext) {
19+
class ContextUpdater(val context: KotlinContext, private val evaluator: KJvmReplEvaluatorImpl) {
2020

2121
fun update() {
2222
try {
23-
val lines = state.lines
23+
val lastSnippet = evaluator.lastEvaluatedSnippet
24+
val lines = lastSnippet.instances()
2425
refreshVariables(lines)
2526
refreshMethods(lines)
2627
} catch (e: ReflectiveOperationException) {

0 commit comments

Comments
 (0)