Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completion base support #22

Merged
merged 8 commits into from
Nov 25, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
buildscript {
ext.shadowJarVersion = "5.2.0"
//ext.kotlinVersion = '1.3.60-dev-2180-83'
ext.kotlinVersion = '1.3-SNAPSHOT'
ext.kotlinVersion = '1.3.70-dev-1590'
repositories {
jcenter()
mavenLocal()
Expand All @@ -23,6 +23,7 @@ allprojects {
jcenter()
mavenLocal()
mavenCentral()
maven { url '/home/ileasile/maven_repo/repo_1/maven' }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, remove this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

// only when using Kotlin EAP releases ...
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
Expand Down Expand Up @@ -50,6 +51,7 @@ configurations {

dependencies {
compile project(":jupyter-lib")
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
compile "org.jetbrains.kotlin:kotlin-scripting-jvm-host-embeddable:$kotlinVersion"
compile "org.jetbrains.kotlin:kotlin-scripting-common:$kotlinVersion"
compile "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:$kotlinVersion"
Expand Down
24 changes: 16 additions & 8 deletions src/main/kotlin/org/jetbrains/kotlin/jupyter/protocol.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fun JupyterConnection.Socket.shellMessagesHandler(msg: Message, repl: ReplForJup
.map { Pair("${it.name}_port", connection.config.ports[it.ordinal]) })))
"execute_request" -> {
connection.contextMessage = msg
var count = executionCount.getAndIncrement()
val count = executionCount.getAndIncrement()
val startedTime = ISO8601DateNow

connection.iopub.send(makeReplyMessage(msg, "status", content = jsonObject("execution_state" to "busy")))
Expand Down Expand Up @@ -122,18 +122,26 @@ fun JupyterConnection.Socket.shellMessagesHandler(msg: Message, repl: ReplForJup
connection.iopub.send(makeReplyMessage(msg, "status", content = jsonObject("execution_state" to "idle")))
connection.contextMessage = null
}
"complete_request" -> {
val code = msg.content["code"].toString()
val cursor = msg.content["cursor_pos"] as Int
val result = repl?.complete(code, cursor)?.toJson()
if (result == null) {
System.err.println("Repl is not yet initialized on complete request")
return
}
send(makeReplyMessage(msg, "complete_reply", content = result))
}
"is_complete_request" -> {
val code = msg.content["code"].toString()
val resStr = if (isCommand(code)) "complete" else {
val result = try {
val check = repl?.checkComplete(executionCount.get(), code)
if (check == null) {
"error: no repl"
} else if (check.isComplete) {
"complete"
} else {
"incomplete"
}
when {
check == null -> "error: no repl"
check.isComplete -> "complete"
else -> "incomplete"
}
} catch (ex: ReplCompilerException) {
"invalid"
}
Expand Down
74 changes: 49 additions & 25 deletions src/main/kotlin/org/jetbrains/kotlin/jupyter/repl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import jupyter.kotlin.ScriptTemplateWithDisplayHelpers
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.jupyter.repl.building.ReplBuilding
import org.jetbrains.kotlin.jupyter.repl.completion.CompletionResult
import org.jetbrains.kotlin.jupyter.repl.completion.KotlinCompleter
import org.jetbrains.kotlin.jupyter.repl.context.KotlinContext
import org.jetbrains.kotlin.jupyter.repl.context.KotlinReceiver
import org.jetbrains.kotlin.jupyter.repl.reflect.ContextUpdater
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.script.dependencies.ScriptContents
Expand Down Expand Up @@ -59,6 +65,8 @@ class ReplForJupyter(val classpath: List<File> = emptyList(), val config: Resolv
}
}

private val receiver = KotlinReceiver()

private val compilerConfiguration by lazy {
ScriptCompilationConfiguration {
hostConfiguration.update { it.withDefaultsFrom(defaultJvmScriptingHostConfiguration) }
Expand All @@ -71,10 +79,18 @@ class ReplForJupyter(val classpath: List<File> = emptyList(), val config: Resolv
refineConfiguration {
onAnnotations(DependsOn::class, Repository::class, handler = { configureMavenDepsOnAnnotations(it) })
}

val kt = KotlinType(receiver.javaClass.canonicalName)
implicitReceivers.invoke(listOf(kt))

val receiverClassPath = receiver.javaClass.protectionDomain.codeSource.location.path
compilerOptions.invoke(listOf("-classpath", receiverClassPath))
}
}

private val evaluatorConfiguration = ScriptEvaluationConfiguration { }
private val evaluatorConfiguration = ScriptEvaluationConfiguration {
implicitReceivers.invoke(receiver)
}

private var executionCounter = 0

Expand All @@ -88,14 +104,19 @@ class ReplForJupyter(val classpath: List<File> = emptyList(), val config: Resolv

private val stateLock = ReentrantReadWriteLock()

private val state = compiler.createState(stateLock)

private val compilerState = compiler.createState(stateLock)
private val evaluatorState = evaluator.createState(stateLock)

private val state = AggregatedReplStageState(compilerState, evaluatorState, stateLock)

private val ctx = KotlinContext()
private val contextUpdater = ContextUpdater(state, ctx.vars, ctx.functions)

private val completer = KotlinCompleter(ctx)

fun checkComplete(executionNumber: Long, code: String): CheckResult {
val codeLine = ReplCodeLine(executionNumber.toInt(), 0, code)
var result = compiler.check(state, codeLine)
return when(result) {
return when(val result = compiler.check(compilerState, codeLine)) {
is ReplCheckResult.Error -> throw ReplCompilerException(result)
is ReplCheckResult.Ok -> CheckResult(LineId(codeLine), true)
is ReplCheckResult.Incomplete -> CheckResult(LineId(codeLine), false)
Expand Down Expand Up @@ -130,31 +151,34 @@ class ReplForJupyter(val classpath: List<File> = emptyList(), val config: Resolv
return EvalResult(result, displays)
}


fun complete(code: String, cursor: Int): CompletionResult = completer.complete(code, cursor)

private fun doEval(code: String): Any? {
synchronized(this) {
val codeLine = ReplCodeLine(executionCounter++, 0, code)
val compileResult = compiler.compile(state, codeLine)
when (compileResult) {
is ReplCompileResult.CompiledClasses -> {
var result = evaluator.eval(evaluatorState, compileResult)
return when (result) {
is ReplEvalResult.Error.CompileTime -> throw ReplCompilerException(result)
is ReplEvalResult.Error.Runtime -> throw ReplEvalRuntimeException(result)
is ReplEvalResult.Incomplete -> throw ReplCompilerException(result)
is ReplEvalResult.HistoryMismatch -> throw ReplCompilerException(result)
is ReplEvalResult.UnitResult -> {
Unit
}
is ReplEvalResult.ValueResult -> {
result.value
}
else -> throw IllegalStateException("Unknown eval result type ${this}")
synchronized(this) {
val codeLine = ReplCodeLine(executionCounter++, 0, code)
when (val compileResult = compiler.compile(compilerState, codeLine)) {
is ReplCompileResult.CompiledClasses -> {
val result = evaluator.eval(evaluatorState, compileResult)
contextUpdater.update()
return when (result) {
is ReplEvalResult.Error.CompileTime -> throw ReplCompilerException(result)
is ReplEvalResult.Error.Runtime -> throw ReplEvalRuntimeException(result)
is ReplEvalResult.Incomplete -> throw ReplCompilerException(result)
is ReplEvalResult.HistoryMismatch -> throw ReplCompilerException(result)
is ReplEvalResult.UnitResult -> {
Unit
}
is ReplEvalResult.ValueResult -> {
result.value
}
else -> throw IllegalStateException("Unknown eval result type ${this}")
}
is ReplCompileResult.Error -> throw ReplCompilerException(compileResult)
is ReplCompileResult.Incomplete -> throw ReplCompilerException(compileResult)
}
is ReplCompileResult.Error -> throw ReplCompilerException(compileResult)
is ReplCompileResult.Incomplete -> throw ReplCompilerException(compileResult)
}
}
}

init {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jetbrains.kotlin.jupyter.repl.building

import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import java.io.File
import java.util.ArrayList
import java.util.Collections
import java.util.HashSet
import org.jetbrains.kotlin.jupyter.repl.context.KotlinReceiver

/**
* Class that holds properties for Kotlin REPL creation,
* namely implicit receiver, classpath, preloaded code, directory for class bytecode output,
* max result limit and shortening types flag.
*
* Set its parameters by chaining corresponding methods, e.g.
* properties.outputDir(dir).shortenTypes(false)
*
* Get its parameters via getters.
*/
class KotlinReplProperties {

val hostConf = defaultJvmScriptingHostConfiguration

var receiver: KotlinReceiver? = null
private set
private val classpath: MutableSet<String>
private val codeOnLoad: MutableList<String>
var outputDir: String? = null
private set
var maxResult = 1000
private set
var shortenTypes = true
private set

init {
this.receiver = KotlinReceiver()

this.classpath = HashSet()
val javaClasspath = System.getProperty("java.class.path").split(File.pathSeparator.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
Collections.addAll(classpath, *javaClasspath)

this.codeOnLoad = ArrayList()
}

fun receiver(receiver: KotlinReceiver): KotlinReplProperties {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need in setter methods, all fields in Kotlin are properties and optional getters/setters can be added if needed (https://kotlinlang.org/docs/reference/properties.html)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I know this, but this was auto-converted from Java :)

this.receiver = receiver
return this
}

fun classPath(path: String): KotlinReplProperties {
this.classpath.add(path)
return this
}

fun classPath(paths: Collection<String>): KotlinReplProperties {
this.classpath.addAll(paths)
return this
}

fun codeOnLoad(code: String): KotlinReplProperties {
this.codeOnLoad.add(code)
return this
}

fun codeOnLoad(code: Collection<String>): KotlinReplProperties {
this.codeOnLoad.addAll(code)
return this
}

fun outputDir(outputDir: String): KotlinReplProperties {
this.outputDir = outputDir
return this
}

fun maxResult(maxResult: Int): KotlinReplProperties {
this.maxResult = maxResult
return this
}

fun shortenTypes(shortenTypes: Boolean): KotlinReplProperties {
this.shortenTypes = shortenTypes
return this
}

fun getClasspath(): Set<String> {
return classpath
}

fun getCodeOnLoad(): List<String> {
return codeOnLoad
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jetbrains.kotlin.jupyter.repl.building

import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerImpl
import java.io.File
import java.util.StringJoiner
import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
import kotlin.script.experimental.jvm.dependenciesFromCurrentContext
import kotlin.script.experimental.jvm.jvm
import kotlin.script.experimental.host.withDefaultsFrom
import kotlin.script.experimental.jvmhost.repl.JvmReplCompiler
import kotlin.script.experimental.jvmhost.repl.JvmReplEvaluator

/**
* Util class for building REPL components.
*/
object ReplBuilding {
fun buildCompiler(properties: KotlinReplProperties): JvmReplCompiler {
val receiverClassPath = properties.receiver!!.javaClass
.protectionDomain.codeSource.location.path
properties.classPath(receiverClassPath)

val compilerImpl = KJvmReplCompilerImpl(properties.hostConf)

return JvmReplCompiler(
buildCompilationConfiguration(properties),
properties.hostConf,
compilerImpl)
}

fun buildEvaluator(properties: KotlinReplProperties): JvmReplEvaluator {
return JvmReplEvaluator(
buildEvaluationConfiguration(properties),
BasicJvmScriptEvaluator())
}

private fun buildClassPath(p: KotlinReplProperties): String {
val joiner = StringJoiner(File.pathSeparator)
for (path in p.getClasspath()) {
if (path != "") {
joiner.add(path)
}
}
return joiner.toString()
}

private fun buildCompilationConfiguration(
p: KotlinReplProperties): ScriptCompilationConfiguration {
return ScriptCompilationConfiguration {
hostConfiguration.invoke(p.hostConf)

val jvmBuilder = jvm
jvmBuilder.dependenciesFromCurrentContext(wholeClasspath = true, unpackJarCollections = false)

val compilerOptions = listOf("-classpath", buildClassPath(p))

this.compilerOptions.invoke(compilerOptions)

val kt = KotlinType(p.receiver!!.javaClass.canonicalName)
val receivers = listOf(kt)
implicitReceivers.invoke(receivers)

Unit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to return Unit here

}
}

private fun buildEvaluationConfiguration(
p: KotlinReplProperties): ScriptEvaluationConfiguration {
return ScriptEvaluationConfiguration {
hostConfiguration.invoke(p.hostConf)

val receivers = listOf<Any?>(p.receiver)
implicitReceivers.invoke(receivers)

Unit
}
}
}
Loading