Skip to content

Running local Kotlin playground

Posted on:October 30, 2022 (8 min read)

Table of contents

Open Table of contents

Kotlin Playground

You might see Kotlin Playground on the https://play.kotlinlang.org/ page. It is also widely used in the official documentation portal.

It is open-source, so we can run it in the local environment or using a custom server. There might be a few reasons one needs to run it separately:

How to build the frontend part

Download or clone source code from the GitHub

Run yarn install and yarn build:all

The dist folder will contain the build results:

How to run the server part

Download or clone source code from the GitHub

Create a public folder in the /src/main/resources to serve the frontend resources:

<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Kotlin Playground examples</title>
<link rel="stylesheet" href="examples.css" />
<link rel="stylesheet" href="examples-highlight.css" />
<style>
.markdown-body {
max-width: 980px;
margin: 50px auto;
}
</style>
<script src="playground.min.js" data-selector=".kotlin-code" data-server="http://127.0.0.1:8080"></script>
</head>
<body class="markdown-body">
<h1>Kotlin Playground demo</h1>
<pre>
<code class="kotlin-code" data-crosslink="disabled" auto-indent="true">
class Contact(val id: Int, var email: String)
fun main(args:Array&lt;String&gt;) {
val contact = Contact(1,&quot;mary@gmail.com&quot;)
println(contact.id)
}
</code>
</pre>
</body>
</html>

You can refer to the whole list of options for the script part and the code element in the frontend project README.md

Run from IDE, main class is com.compiler.server.CompilerApplication or build with

Terminal window
./gradlew bootJar

and run with

Terminal window
java -jar build/libs/kotlin-compiler-server-1.7.20-SNAPSHOT.jar

Customizations

Server part

Server runs code as a separate process and you can modify the JVM options:

FrontEnd

You can modify the executeKotlinCode function in src/webdemo-api.js:

case TargetPlatform.JAVA:
if (data.text) output = processJVMOutput(data.text, theme);
break;

processJVMOutput generates raw HTML code based on the output from the server side.

Or we can even create new TargetPlatform (target platform is one of the html element attributes like <code data-target-platform="java">), correspondent value should be addes to the server side ProjectType enum.

E.g., we want to have custom render for the code output:

Server side:

Project.kt
enum class ProjectType(@JsonValue val id: String) {
JAVA("java"),
JAVA_CUSTOM("java-custom"),
JUNIT("junit"),
CANVAS("canvas"),
JS("js"),
JS_IR("js-ir");
fun isJsRelated(): Boolean = this == JS || this == JS_IR || this == CANVAS
}

KotlinPlaygroundRestController.kt
ProjectType.JAVA -> kotlinProjectExecutor.run(project)
ProjectType.JAVA_CUSTOM -> kotlinProjectExecutor.run(project)

FrontEnd:

target-platform.js
class TargetPlatform {
constructor(id, printableName) {
this.id = id;
this.printableName = printableName;
}
static getById(id) {
switch (id) {
case 'js':
return TargetPlatform.JS;
case 'js-ir':
return TargetPlatform.JS_IR;
case 'junit':
return TargetPlatform.JUNIT;
case 'java-custom':
return TargetPlatform.JAVA_CUSTOM;
case 'canvas':
return TargetPlatform.CANVAS;
default:
return TargetPlatform.JAVA;
}
}
static isJavaRelated(platform) {
return (
platform === TargetPlatform.JAVA ||
platform === TargetPlatform.JAVA_CUSTOM ||
platform === TargetPlatform.JUNIT
);
}
static isJsRelated(platform) {
return (
platform === TargetPlatform.JS ||
platform === TargetPlatform.JS_IR ||
platform === TargetPlatform.CANVAS
);
}
}
TargetPlatform.JS = new TargetPlatform('js', 'JavaScript');
TargetPlatform.JS_IR = new TargetPlatform('js-ir', 'JavaScript IR');
TargetPlatform.JAVA = new TargetPlatform('java', 'JVM');
TargetPlatform.JAVA_CUSTOM = new TargetPlatform('java-custom', 'JVM SVG');
TargetPlatform.JUNIT = new TargetPlatform('junit', 'JUnit');
TargetPlatform.CANVAS = new TargetPlatform('canvas', 'JavaScript(canvas)');
export default TargetPlatform;

config.js
export const API_URLS = {
server: RUNTIME_CONFIG.server || __WEBDEMO_URL__,
COMPILE(platform, version) {
let url;
switch (platform) {
case TargetPlatform.JAVA:
case TargetPlatform.JAVA_CUSTOM:
url = `${this.server}/api/${version}/compiler/run`;
break;
// ... rest of the code
}
}
}

webdemo-api.js
static executeKotlinCode(code, compilerVersion, platform, args, theme, hiddenDependencies, onTestPassed, onTestFailed) {
return executeCode(API_URLS.COMPILE(platform, compilerVersion), code, compilerVersion, platform, args, hiddenDependencies).then(function (data) {
let output = "";
let errorsAndWarnings = flatten(Object.values(data.errors));
let errors = errorsAndWarnings.filter(error => error.severity === "ERROR");
if (errors.length > 0) {
output = processErrors(errors, theme);
} else {
switch (platform) {
case TargetPlatform.JAVA:
if (data.text) output = processJVMOutput(data.text, theme);
break;
case TargetPlatform.JAVA_CUSTOM:
if (data.text) output = processJVMOutputCustom(data.text, theme);
break;
// rest of the code
}
})
}

and implement the processJVMOutputCustom function similar to the processJVMOutput.