Skip to content

RectSLEditorExtension.kt

src/jvmMain/kotlin/afield/lang/rectsl/RectSLEditorExtension.kt · 78 lines

package afield.lang.rectsl

import java.io.File

Serves rectsl.js, the browser-side RectSL editor support, to the page and to the test that checks it.

RectSL runs in Kotlin, in RectSL.kt; a RectSL program is a triple-quoted string beginning #rectsl inside an ordinary Kotlin box. So there is no execution path to hook, and this file is the whole of the language's presence in the editor. rectsl.js lives in src/commonMain/resources, reaches the browser as the AMD module bithero/rectsl (one of the modules bootstrapMonaco.js requires by name while the editor starts), and is read back verbatim here, so the smoke test rectsl.editor-extension.workout runs the same bytes the editor does. A browser-side language that can only be tested in a browser does not stay correct.

Once installed, the extension brings syntax highlighting, flow decorations, lexical diagnostics, a context badge and completions to a #rectsl region. Colouring is decoration-based rather than a Monarch grammar, so there is no grammar here that can fail to compile and take Monaco's startup down with it.

rectsl.js installs itself either way it is loaded: as an AMD module when a Monaco loader is present, and immediately when it is evaluated bare, which is the path html() uses. The two differ in one respect. The AMD path also registers a standalone rectsl Monaco language, so that a loose .rectsl file opened in the editor gets # comment toggling and bracket handling; it carries no tokenizer of its own, because the decoration highlighter already treats a whole rectsl model as one region.

object RectSLEditorExtension

The RectSL editor extension, as bytes: the resource itself, and a page fragment that installs it.

object RectSLEditorExtension {

const val id: String = "rectsl"

The extension's id, which is also the extensionId rectsl.js registers itself under.

fun javascript(): String

The bytes of rectsl.js.

Read off the classpath, and from src/commonMain/resources/rectsl.js on disk when the classpath has no copy, so this works from a source tree as well as from a jar. It fails rather than returning an empty extension when neither is there.

    fun javascript(): String {
        RectSLEditorExtension::class.java.getResourceAsStream("/rectsl.js")?.use { stream ->
            return stream.readBytes().toString(Charsets.UTF_8)
        }
        val onDisk = File("./src/commonMain/resources/rectsl.js")
        require(onDisk.isFile) { "rectsl.js not found on the classpath or at ${onDisk.absolutePath}" }
        return onDisk.readText(Charsets.UTF_8)
    }

fun html(): String

The extension as a drop-in HTML fragment: a titled line of prose saying what it does, with the whole of rectsl.js inlined in a script tag.

Nothing in src/ calls this. rectSLEditorExtensionHtml is the unqualified name for it.

    fun html(): String = """
        <div style="font-family:Iosevka Web, monospace; background:#050505; color:#d8d8d8; border:1px solid #303030; padding:10px; max-width:680px">
          <strong>RectSL editor extension</strong>
          <div style="margin-top:4px; color:#a8a8a8">
            Registers <code>#rectsl</code> embedded Kotlin regions with syntax highlighting, flow decorations, and a context badge.
          </div>
          <script>
        ${javascript()}
          </script>
        </div>
    """.trimIndent()
}

fun rectSLEditorExtensionJavascript(): String = RectSLEditorExtension.javascript()

RectSLEditorExtension.javascript, as a top-level function.

fun rectSLEditorExtensionHtml(): String = RectSLEditorExtension.html()

RectSLEditorExtension.html, as a top-level function.