Skip to content

Commit 194e96e

Browse files
committed
working demo
1 parent bdb42f2 commit 194e96e

File tree

14 files changed

+424
-8
lines changed

14 files changed

+424
-8
lines changed

build.gradle

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
buildscript {
22
ext.loader_version = '1.0.3' // Don't forget to update the version also in kotlin-ace-loader/package.json
3-
ext.wrapper_version = '1.3.34'
3+
ext.wrapper_version = '1.3.35'
44
ext.kotlin_version = '1.2.60'
55
ext.ace_version = '1.3.3'
66
}
77

88
plugins {
99
id 'kotlin2js' version '1.2.60' apply false
10-
id 'kotlinFrontend' version '0.0.33' apply false
10+
id 'org.jetbrains.kotlin.frontend' version '0.0.33' apply false
1111
}
1212

1313
subprojects { project ->
@@ -36,6 +36,24 @@ subprojects { project ->
3636

3737
}
3838

39+
if (project.name.startsWith("demo")) {
40+
41+
apply plugin: 'kotlin2js'
42+
apply plugin: 'org.jetbrains.kotlin.frontend'
43+
44+
compileKotlin2Js {
45+
kotlinOptions {
46+
moduleKind = "commonjs"
47+
sourceMap = true
48+
sourceMapEmbedSources = "always"
49+
metaInfo = true
50+
outputFile = "$project.buildDir.path/js/${project.name}.js"
51+
main = "call"
52+
}
53+
}
54+
55+
}
56+
3957
ext.setupNPM = {
4058

4159
//noinspection GroovyAssignabilityCheck

demo-web/build.gradle

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
dependencies {
2+
implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
3+
implementation 'com.github.daemontus.kotlin-ace-wrapper:kotlin-ace-web:1.3.34'
4+
}
5+
6+
kotlinFrontend {
7+
npm {
8+
dependency("kotlin-ace-web", "1.3.34")
9+
}
10+
11+
webpackBundle {
12+
bundleName = "web"
13+
// This is the path where demo.html is copied and where we also deliver the worker bundle.
14+
contentPath = file('build/resources/main')
15+
}
16+
}
17+
18+
// Copy the worker bundle from worker module to content path.
19+
task copyWorkerBundle(type: Copy) {
20+
from('../demo-worker/build/bundle/worker.bundle.js')
21+
into('build/resources/main')
22+
}
23+
24+
copyWorkerBundle.dependsOn(":demo-worker:webpack-bundle")
25+
run.dependsOn(copyWorkerBundle)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import ace.*
2+
import ace.mode.Mode
3+
import ace.worker.WorkerClient
4+
import kotlin.browser.window
5+
6+
/**
7+
* A simple mode which replaces internal rule-based tokenizer with our custom implementation and
8+
* starts our syntax checking demo worker on demand.
9+
*/
10+
class DemoMode(
11+
private val editor: Editor
12+
) : Mode() {
13+
14+
override fun getTokenizer(): Tokenizer<*, *> = DemoTokenizer
15+
16+
override fun createWorker(session: EditSession): WorkerClient? {
17+
println("Creating worker.")
18+
19+
// get current url minus the file name
20+
val pathPrefix = window.location.href.replace(Regex("[^/]*\$"), "")
21+
22+
// start client with all dependencies
23+
val client = WorkerClient(
24+
topLevelNamespaces = arrayOf("ace"),
25+
mod = "ace/worker/DemoWorker",
26+
classname = "DemoWorker",
27+
workerUrl = "worker.bundle.js",
28+
importScripts = ""
29+
)/* startWorkerClient(
30+
workerClassName = "DemoWorker",
31+
workerInitUrl = "$pathPrefix/lib/ace-worker-1.3.1-0/worker-init.js",
32+
dependencies = arrayOf(
33+
// Kotlin
34+
"$pathPrefix/lib/kotlin-stdlib-js-1.2.41/kotlin.js",
35+
36+
// ace-worker
37+
"$pathPrefix/lib/ace-worker-1.3.1-0/ace-classes.js",
38+
"$pathPrefix/lib/ace-common-1.3.1-0/ace-common.js",
39+
"$pathPrefix/lib/ace-worker-1.3.1-0/ace-worker.js",
40+
41+
// demo-worker
42+
"$pathPrefix/lib/demo-worker-1.3.1-0/demo-worker.js"
43+
)
44+
)*/
45+
46+
// listen on error events from our parenthesis checking workers
47+
client.on<WorkerClient.Event<Array<GutterAnnotation>>>("errors") { event ->
48+
editor.getSession().setAnnotations(event.data)
49+
}
50+
51+
client.attachToDocument(session.getDocument())
52+
53+
return client
54+
}
55+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import DemoToken.Rule.*
2+
import ace.theme.Theme
3+
import ace.theme.register
4+
5+
object DemoTheme : Theme {
6+
7+
private const val name: String = "theme-demo"
8+
9+
override val id: String = "ace/theme/$name"
10+
override val isDark: Boolean = true
11+
override val cssClass: String = name
12+
13+
override val cssString: String = """
14+
15+
/* Basic styling of editor */
16+
17+
.$name {
18+
color: #A9B7C6;
19+
background: #2B2B2B;
20+
}
21+
22+
.$name .ace_gutter {
23+
color: #606366;
24+
background: #313335;
25+
}
26+
27+
.$name .ace_cursor {
28+
color: #ababab;
29+
}
30+
31+
.$name .ace_marker-layer .ace_selection {
32+
background: rgba(221, 240, 255, 0.20);
33+
}
34+
35+
.$name .ace_marker-layer .ace_active-line {
36+
background: rgba(255, 255, 255, 0.031);
37+
}
38+
39+
.$name .ace_gutter-active-line {
40+
rgba(255, 255, 255, 0.031);
41+
}
42+
43+
/* Styles for our custom tokens */
44+
45+
.$name .ace_$NUMBER {
46+
color: #6897BB;
47+
fontStyle: italic;
48+
}
49+
50+
.$name .ace_$OPERATOR {
51+
color: #CC7832;
52+
font-style: bold;
53+
}
54+
55+
.$name .ace_$PARENTHESIS {
56+
font-style: bold;
57+
}
58+
59+
.$name .ace_$UNKNOWN {
60+
-webkit-text-decoration-line: underline;
61+
-webkit-text-decoration-color: red;
62+
}
63+
64+
""".trimIndent()
65+
66+
init {
67+
this.register()
68+
}
69+
70+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import ace.Tokenizer
2+
3+
/**
4+
* Simple demo grammar will recognize these types of tokens:
5+
* - Natural numbers (1,2,456,15, ...)
6+
* - Operators (+,-,*,/)
7+
* - Parenthesis ()
8+
* - Whitespace
9+
* - Unknown - everything else
10+
*
11+
* If you had a bigger grammar, you could try to distinguish lexing rules and tokens
12+
* (having a set of rules, each rule reading one token). Since this is an extremely
13+
* simple grammar, all rules are baked directly into [DemoTokenizer].
14+
*/
15+
sealed class DemoToken : Tokenizer.Token {
16+
17+
enum class Rule(val type: String) {
18+
NUMBER("demo-number"),
19+
OPERATOR("demo-operator"),
20+
PARENTHESIS("demo-parenthesis"),
21+
WHITESPACE("demo-whitespace"),
22+
UNKNOWN("demo-unknown")
23+
;
24+
25+
override fun toString(): String = type
26+
}
27+
28+
data class Number(override val value: String) : DemoToken() {
29+
override val type: String = Rule.NUMBER.type
30+
}
31+
32+
data class Operator(override val value: String) : DemoToken() {
33+
override val type: String = Rule.OPERATOR.type
34+
}
35+
36+
data class Parenthesis(override val value: String) : DemoToken() {
37+
override val type: String = Rule.PARENTHESIS.type
38+
}
39+
40+
data class Whitespace(override val value: String) : DemoToken() {
41+
override val type: String = Rule.WHITESPACE.type
42+
}
43+
44+
data class Unknown(override val value: String) : DemoToken() {
45+
override val type: String = Rule.UNKNOWN.type
46+
}
47+
48+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import ace.Tokenizer
2+
3+
/**
4+
* A simple stateless tokenizer which can parse the tokens specified in [DemoToken].
5+
*
6+
* Lexing essentially tests for presence of all possible tokens and then greedily
7+
* reads as much as possible at once, since all the tokens are completely distinct.
8+
*/
9+
object DemoTokenizer : Tokenizer<DemoToken, Unit>(null) {
10+
11+
private val operators = charArrayOf('+', '-', '*', '/')
12+
13+
override fun getLineTokens(line: String, startState: Unit?): Tokens<DemoToken, Unit> {
14+
// override default getLineTokens functionality with our custom parsing rules:
15+
16+
val tokens = ArrayList<DemoToken>()
17+
var text = line
18+
while (text.isNotEmpty()) {
19+
val token = when {
20+
text[0] in operators -> {
21+
// Token is an operator:
22+
DemoToken.Operator(text[0].toString())
23+
}
24+
text[0] == '(' || text[0] == ')' -> {
25+
// Token is parenthesis:
26+
DemoToken.Parenthesis(text[0].toString())
27+
}
28+
text[0].isWhitespace() -> {
29+
// Token is whitespace:
30+
val token = text.takeWhile { it.isWhitespace() }
31+
DemoToken.Whitespace(token)
32+
}
33+
text[0] in '0'..'9' -> {
34+
// Token is a number:
35+
val token = text.takeWhile { it in '0'..'9' }
36+
DemoToken.Number(token)
37+
}
38+
else -> {
39+
// We don't know what this is:
40+
val token = text.takeWhile {
41+
it !in '0'..'9'
42+
&& it !in operators
43+
&& it != '(' && it != ')'
44+
&& !it.isWhitespace()
45+
}
46+
DemoToken.Unknown(token)
47+
}
48+
}
49+
tokens.add(token)
50+
text = text.drop(token.value.length)
51+
}
52+
53+
return DemoTokens(null, tokens.toTypedArray())
54+
}
55+
56+
private class DemoTokens(
57+
override val state: Unit?,
58+
override val tokens: Array<DemoToken>
59+
) : Tokens<DemoToken, Unit>
60+
61+
}

demo-web/src/main/kotlin/main.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
import ace.Ace
3+
import ace.Editor
4+
5+
fun main(args: Array<String>) {
6+
println("Demo script just started!")
7+
8+
println("First, we declare a new dark theme for the editor with custom styles for our tokens.")
9+
val editor: Editor = Ace.edit("editor") // Start Ace editor
10+
editor.setTheme(DemoTheme.id)
11+
12+
println("Next, we want to initialize syntax highlighting. To do this, we need a text mode.")
13+
val mode = DemoMode(editor)
14+
editor.getSession().setMode(mode)
15+
16+
println("And that's it. Ace will create syntax checking worker on demand once needed...")
17+
18+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Editor</title>
7+
8+
<style type="text/css" media="screen">
9+
#editor {
10+
position: absolute;
11+
top: 0;
12+
right: 0;
13+
bottom: 0;
14+
left: 0;
15+
}
16+
</style>
17+
</head>
18+
<body>
19+
20+
<div id="editor">
21+
(123 + 4) - (4 * 14 / (56 + 2))
22+
some invalid tokens
23+
634 - 325 * (134 + (41234 / 123)
24+
</div>
25+
26+
<script src="web.bundle.js" type="text/javascript"></script>
27+
28+
</body>
29+
</html>

demo-worker/build.gradle

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
dependencies {
2+
implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
3+
implementation 'com.github.daemontus.kotlin-ace-wrapper:kotlin-ace-worker:1.3.34'
4+
}
5+
6+
kotlinFrontend {
7+
npm {
8+
dependency("kotlin-ace-worker", "1.3.34")
9+
}
10+
11+
webpackBundle {
12+
bundleName = "worker"
13+
webpackConfigFile = "$project.projectDir.path/webpack.config.js"
14+
}
15+
}

0 commit comments

Comments
 (0)