Skip to content

SonicPiEditorExtension.kt

src/jvmMain/kotlin/afield/audio/synth/SonicPiEditorExtension.kt · 40 lines

@file:afield.runtime.completion.CompletionCategory("Audio")
package afield.audio.synth

import java.io.File

The #sonicpi editor extension and the vocabulary it completes against.

Both are static resources rather than strings built here: sonicpi.js is served to the browser as the AMD module bithero/sonicpi (see bootstrapMonaco.js) and sonicpi-vocabulary.json is fetched by it at install time. Reading them verbatim is what lets the node-probe smoke test exercise the code and data that ship, rather than a copy that can drift.

The vocabulary is generated by tools/generate_sonic_pi_vocabulary.py from the installed Sonic Pi; see that script for why it is extracted rather than written.

object SonicPiEditorExtension

Reads the two shipped #sonicpi resources: the Monaco module, and the vocabulary it completes against.

const val id: String = "sonicpi"

fun javascript(): String = resource("/sonicpi.js", "src/commonMain/resources/sonicpi.js")

fun vocabularyJson(): String

    fun vocabularyJson(): String =
        resource("/sonicpi-vocabulary.json", "src/commonMain/resources/sonicpi-vocabulary.json")

    private fun resource(onClasspath: String, onDiskPath: String): String {
        SonicPiEditorExtension::class.java.getResourceAsStream(onClasspath)?.use { stream ->
            return stream.readBytes().toString(Charsets.UTF_8)
        }
        val onDisk = File("./$onDiskPath")
        require(onDisk.isFile) {
            "$onClasspath not found on the classpath or at ${onDisk.absolutePath}"
        }
        return onDisk.readText(Charsets.UTF_8)
    }
}

fun sonicPiEditorExtensionJavascript(): String = SonicPiEditorExtension.javascript()