mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-04 06:28:28 +08:00
Added script editor widget.
This commit is contained in:
parent
0983be905d
commit
cd70d22da2
@ -1,3 +1,5 @@
|
||||
package world.phantasmal.core
|
||||
|
||||
fun Char.isDigit(): Boolean = this in '0'..'9'
|
||||
|
||||
expect fun Int.reinterpretAsFloat(): Float
|
||||
|
@ -0,0 +1,11 @@
|
||||
package world.phantasmal.core
|
||||
|
||||
import org.khronos.webgl.ArrayBuffer
|
||||
import org.khronos.webgl.DataView
|
||||
|
||||
private val dataView = DataView(ArrayBuffer(4))
|
||||
|
||||
actual fun Int.reinterpretAsFloat(): Float {
|
||||
dataView.setInt32(0, this)
|
||||
return dataView.getFloat32(0)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package world.phantasmal.core
|
||||
|
||||
import java.lang.Float.intBitsToFloat
|
||||
|
||||
actual fun Int.reinterpretAsFloat(): Float = intBitsToFloat(this)
|
@ -20,11 +20,15 @@ class AssemblyProblem(
|
||||
|
||||
fun assemble(
|
||||
assembly: List<String>,
|
||||
manualStack: Boolean = false,
|
||||
inlineStackArgs: Boolean = true,
|
||||
): PwResult<List<Segment>> {
|
||||
logger.trace { "Assembly start." }
|
||||
logger.trace {
|
||||
"Assembling ${assembly.size} lines with ${
|
||||
if (inlineStackArgs) "inline stack arguments" else "stack push instructions"
|
||||
}."
|
||||
}
|
||||
|
||||
val result = Assembler(assembly, manualStack).assemble()
|
||||
val result = Assembler(assembly, inlineStackArgs).assemble()
|
||||
|
||||
logger.trace {
|
||||
val warnings = result.problems.count { it.severity == Severity.Warning }
|
||||
@ -36,7 +40,7 @@ fun assemble(
|
||||
return result
|
||||
}
|
||||
|
||||
private class Assembler(private val assembly: List<String>, private val manualStack: Boolean) {
|
||||
private class Assembler(private val assembly: List<String>, private val inlineStackArgs: Boolean) {
|
||||
private var lineNo = 1
|
||||
private lateinit var tokens: MutableList<Token>
|
||||
private var ir: MutableList<Segment> = mutableListOf()
|
||||
@ -355,7 +359,7 @@ private class Assembler(private val assembly: List<String>, private val manualSt
|
||||
}
|
||||
|
||||
val paramCount =
|
||||
if (manualStack && opcode.stack == StackInteraction.Pop) 0
|
||||
if (!inlineStackArgs && opcode.stack == StackInteraction.Pop) 0
|
||||
else opcode.params.size
|
||||
|
||||
val argCount = tokens.count { it !is ArgSeparatorToken }
|
||||
|
@ -0,0 +1,310 @@
|
||||
package world.phantasmal.lib.assembly
|
||||
|
||||
import mu.KotlinLogging
|
||||
import world.phantasmal.core.reinterpretAsFloat
|
||||
import kotlin.math.min
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
private const val INDENT_WIDTH = 4
|
||||
private val INDENT = " ".repeat(INDENT_WIDTH)
|
||||
|
||||
/**
|
||||
* @param inlineStackArgs If true, will output stack arguments inline instead of outputting stack
|
||||
* management instructions (argpush variants).
|
||||
*/
|
||||
fun disassemble(byteCodeIr: List<Segment>, inlineStackArgs: Boolean = true): List<String> {
|
||||
logger.trace {
|
||||
"Disassembling ${byteCodeIr.size} segments with ${
|
||||
if (inlineStackArgs) "inline stack arguments" else "stack push instructions"
|
||||
}."
|
||||
}
|
||||
|
||||
val lines = mutableListOf<String>()
|
||||
val stack = mutableListOf<ArgWithType>()
|
||||
var sectionType: SegmentType? = null
|
||||
|
||||
for (segment in byteCodeIr) {
|
||||
// Section marker (.code, .data or .string).
|
||||
if (sectionType != segment.type) {
|
||||
sectionType = segment.type
|
||||
|
||||
if (lines.isNotEmpty()) {
|
||||
lines.add("")
|
||||
}
|
||||
|
||||
val sectionMarker = when (segment) {
|
||||
is InstructionSegment -> ".code"
|
||||
is DataSegment -> ".data"
|
||||
is StringSegment -> ".string"
|
||||
}
|
||||
|
||||
lines.add(sectionMarker)
|
||||
lines.add("")
|
||||
}
|
||||
|
||||
// Labels.
|
||||
for (label in segment.labels) {
|
||||
lines.add("$label:")
|
||||
}
|
||||
|
||||
// Code or data lines.
|
||||
when (segment) {
|
||||
is InstructionSegment -> {
|
||||
var inVaList = false
|
||||
|
||||
segment.instructions.forEachIndexed { i, instruction ->
|
||||
val opcode = instruction.opcode
|
||||
|
||||
if (opcode.code == OP_VA_START.code) {
|
||||
inVaList = true
|
||||
} else if (opcode.code == OP_VA_END.code) {
|
||||
inVaList = false
|
||||
}
|
||||
|
||||
if (inlineStackArgs &&
|
||||
!inVaList &&
|
||||
opcode.stack == StackInteraction.Push &&
|
||||
canInlinePushedArg(segment, i)
|
||||
) {
|
||||
stack.addAll(addTypeToArgs(opcode.params, instruction.args))
|
||||
} else {
|
||||
val sb = StringBuilder(INDENT)
|
||||
sb.append(opcode.mnemonic)
|
||||
|
||||
if (opcode.stack == StackInteraction.Pop) {
|
||||
if (inlineStackArgs) {
|
||||
sb.appendArgs(
|
||||
opcode.params,
|
||||
stack.takeLast(opcode.params.size),
|
||||
stack = true,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
sb.appendArgs(
|
||||
opcode.params,
|
||||
addTypeToArgs(opcode.params, instruction.args),
|
||||
stack = false
|
||||
)
|
||||
}
|
||||
|
||||
if (opcode.stack != StackInteraction.Push) {
|
||||
stack.clear()
|
||||
}
|
||||
|
||||
lines.add(sb.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is DataSegment -> {
|
||||
val sb = StringBuilder(INDENT)
|
||||
|
||||
for (i in 0 until segment.data.size) {
|
||||
sb.append("0x")
|
||||
sb.append(segment.data.getUByte(i).toString(16).padStart(2, '0'))
|
||||
|
||||
when {
|
||||
// Last line.
|
||||
i == segment.data.size - 1 -> {
|
||||
lines.add(sb.toString())
|
||||
}
|
||||
// Start a new line after every 16 bytes.
|
||||
i % 16 == 15 -> {
|
||||
lines.add(sb.toString())
|
||||
sb.setLength(0)
|
||||
sb.append(INDENT)
|
||||
}
|
||||
// Add a space between each byte.
|
||||
else -> {
|
||||
sb.append(" ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is StringSegment -> {
|
||||
lines.add(StringBuilder(INDENT).appendStringSegment(segment.value).toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure newline at the end.
|
||||
lines.add("")
|
||||
|
||||
logger.trace { "Disassembly finished, line count: ${lines.size}." }
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
private data class ArgWithType(val arg: Arg, val type: AnyType)
|
||||
|
||||
private fun canInlinePushedArg(segment: InstructionSegment, index: Int): Boolean {
|
||||
var pushedArgCount = 0
|
||||
|
||||
for (i in index until segment.instructions.size) {
|
||||
val opcode = segment.instructions[i].opcode
|
||||
|
||||
when (opcode.stack) {
|
||||
StackInteraction.Push -> pushedArgCount++
|
||||
|
||||
StackInteraction.Pop -> {
|
||||
var paramCount = 0
|
||||
var varArgs = false
|
||||
|
||||
for (param in opcode.params) {
|
||||
when (param.type) {
|
||||
is ILabelVarType -> varArgs = true
|
||||
is RegRefVarType -> varArgs = true
|
||||
else -> paramCount++
|
||||
}
|
||||
}
|
||||
|
||||
return pushedArgCount <= paramCount || (pushedArgCount > paramCount && varArgs)
|
||||
}
|
||||
|
||||
null -> return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun addTypeToArgs(params: List<Param>, args: List<Arg>): List<ArgWithType> {
|
||||
val argsWithType = mutableListOf<ArgWithType>()
|
||||
|
||||
for (i in 0 until min(params.size, args.size)) {
|
||||
argsWithType.add(ArgWithType(args[i], params[i].type))
|
||||
}
|
||||
|
||||
// Deal with varargs.
|
||||
val lastParam = params.lastOrNull()
|
||||
|
||||
if (
|
||||
lastParam != null &&
|
||||
(lastParam.type == ILabelVarType || lastParam.type == RegRefVarType)
|
||||
) {
|
||||
for (i in argsWithType.size until args.size) {
|
||||
argsWithType.add(ArgWithType(args[i], lastParam.type))
|
||||
}
|
||||
}
|
||||
|
||||
return argsWithType
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendArgs(params: List<Param>, args: List<ArgWithType>, stack: Boolean) {
|
||||
var i = 0
|
||||
|
||||
while (i < params.size) {
|
||||
val paramType = params[i].type
|
||||
|
||||
if (i == 0) {
|
||||
append(" ")
|
||||
} else {
|
||||
append(", ")
|
||||
}
|
||||
|
||||
if (i < args.size) {
|
||||
val (arg, argType) = args[i]
|
||||
|
||||
if (argType is RegTupRefType) {
|
||||
append("r")
|
||||
append(arg.value)
|
||||
} else {
|
||||
when (paramType) {
|
||||
FloatType -> {
|
||||
// Floats are pushed onto the stack as integers with arg_pushl.
|
||||
if (stack) {
|
||||
append((arg.value as Int).reinterpretAsFloat())
|
||||
} else {
|
||||
append(arg.value)
|
||||
}
|
||||
}
|
||||
|
||||
ILabelVarType -> {
|
||||
while (i < args.size) {
|
||||
append(args[i].arg.value)
|
||||
if (i < args.lastIndex) append(", ")
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
RegRefVarType -> {
|
||||
while (i < args.size) {
|
||||
append("r")
|
||||
append(args[i].arg.value)
|
||||
if (i < args.lastIndex) append(", ")
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
RegRefType,
|
||||
is RegTupRefType,
|
||||
-> {
|
||||
append("r")
|
||||
append(arg.value)
|
||||
}
|
||||
|
||||
StringType -> {
|
||||
appendStringArg(arg.value as String)
|
||||
}
|
||||
|
||||
else -> {
|
||||
append(arg.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendStringArg(value: String) {
|
||||
append("\"")
|
||||
|
||||
for (char in value) {
|
||||
when (char) {
|
||||
'\r' -> append("\\r")
|
||||
'\n' -> append("\\n")
|
||||
'\t' -> append("\\t")
|
||||
'"' -> append("\\\"")
|
||||
else -> append(char)
|
||||
}
|
||||
}
|
||||
|
||||
append("\"")
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendStringSegment(value: String) {
|
||||
append("\"")
|
||||
|
||||
var i = 0
|
||||
|
||||
while (i < value.length) {
|
||||
when (val char = value[i]) {
|
||||
// Replace <cr> with \n.
|
||||
'<' -> {
|
||||
if (i + 3 < value.length &&
|
||||
value[i + 1] == 'c' &&
|
||||
value[i + 2] == 'r' &&
|
||||
value[i + 3] == '>'
|
||||
) {
|
||||
append("\\n")
|
||||
i += 3
|
||||
} else {
|
||||
append(char)
|
||||
}
|
||||
}
|
||||
'\r' -> append("\\r")
|
||||
'\n' -> append("\\n")
|
||||
'\t' -> append("\\t")
|
||||
'"' -> append("\\\"")
|
||||
else -> append(char)
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
append("\"")
|
||||
}
|
@ -58,11 +58,12 @@ fun instructionSize(instruction: Instruction, dcGcFormat: Boolean): Int {
|
||||
is RegTupRefType,
|
||||
-> 1
|
||||
|
||||
// Ensure this case is before the LabelType case because ILabelVarType extends
|
||||
// LabelType.
|
||||
is ILabelVarType -> 1 + 2 * args.size
|
||||
|
||||
is ShortType,
|
||||
is LabelType,
|
||||
is ILabelType,
|
||||
is DLabelType,
|
||||
is SLabelType,
|
||||
-> 2
|
||||
|
||||
is IntType,
|
||||
@ -77,8 +78,6 @@ fun instructionSize(instruction: Instruction, dcGcFormat: Boolean): Int {
|
||||
}
|
||||
}
|
||||
|
||||
is ILabelVarType -> 1 + 2 * args.size
|
||||
|
||||
is RegRefVarType -> 1 + args.size
|
||||
|
||||
else -> error("Parameter type ${type::class} not implemented.")
|
||||
|
@ -62,7 +62,7 @@ object DLabelType : LabelType()
|
||||
object SLabelType : LabelType()
|
||||
|
||||
/**
|
||||
* Arbitrary amount of instruction labels.
|
||||
* Arbitrary amount of instruction labels (variadic arguments).
|
||||
*/
|
||||
object ILabelVarType : LabelType()
|
||||
|
||||
@ -88,7 +88,7 @@ object RegRefType : RefType()
|
||||
class RegTupRefType(val registerTuple: List<Param>) : RefType()
|
||||
|
||||
/**
|
||||
* Arbitrary amount of register references.
|
||||
* Arbitrary amount of register references (variadic arguments).
|
||||
*/
|
||||
object RegRefVarType : RefType()
|
||||
|
||||
|
@ -5,10 +5,30 @@ import world.phantasmal.lib.assembly.*
|
||||
// See https://en.wikipedia.org/wiki/Control-flow_graph.
|
||||
|
||||
enum class BranchType {
|
||||
/**
|
||||
* Only encountered when the last segment of a script has no jump or return.
|
||||
*/
|
||||
None,
|
||||
|
||||
/**
|
||||
* ret
|
||||
*/
|
||||
Return,
|
||||
|
||||
/**
|
||||
* jmp or switch_jmp. switch_jmp is a non-conditional jump because it always jumps even though
|
||||
* the jump location is dynamic.
|
||||
*/
|
||||
Jump,
|
||||
|
||||
/**
|
||||
* Every other jump instruction.
|
||||
*/
|
||||
ConditionalJump,
|
||||
|
||||
/**
|
||||
* call, switch_call or va_call.
|
||||
*/
|
||||
Call,
|
||||
}
|
||||
|
||||
@ -180,7 +200,7 @@ private fun createBasicBlocks(cfg: ControlFlowGraphBuilder, segment: Instruction
|
||||
branchLabels = listOf(inst.args[2].value as Int)
|
||||
}
|
||||
OP_SWITCH_JMP.code -> {
|
||||
branchType = BranchType.ConditionalJump
|
||||
branchType = BranchType.Jump
|
||||
branchLabels = inst.args.drop(1).map { it.value as Int }
|
||||
}
|
||||
|
||||
@ -248,7 +268,7 @@ private fun linkBlocks(cfg: ControlFlowGraphBuilder) {
|
||||
BranchType.ConditionalJump,
|
||||
-> nextBlock?.let(block::linkTo)
|
||||
|
||||
else -> {
|
||||
BranchType.Jump -> {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ private fun parseInstructionsSegment(
|
||||
for (i in instructions.size - 1 downTo 0) {
|
||||
val opcode = instructions[i].opcode.code
|
||||
|
||||
if (opcode == OP_RET.code || opcode == OP_JMP.code) {
|
||||
if (opcode == OP_RET.code || opcode == OP_JMP.code || opcode == OP_SWITCH_JMP.code) {
|
||||
dropThrough = false
|
||||
break
|
||||
}
|
||||
@ -506,11 +506,7 @@ private fun parseInstructionArguments(
|
||||
args.addAll(cursor.uShortArray(argSize.toInt()).map { Arg(it.toInt()) })
|
||||
}
|
||||
|
||||
is LabelType,
|
||||
is ILabelType,
|
||||
is DLabelType,
|
||||
is SLabelType,
|
||||
-> {
|
||||
is LabelType -> {
|
||||
args.add(Arg(cursor.uShort().toInt()))
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,47 @@
|
||||
package world.phantasmal.lib.assembly
|
||||
|
||||
import world.phantasmal.lib.test.LibTestSuite
|
||||
import world.phantasmal.testUtils.assertCloseTo
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class AssemblyTokenizationTests : LibTestSuite() {
|
||||
@Test
|
||||
fun valid_floats_are_parsed_as_FloatTokens() {
|
||||
assertCloseTo(808.9f, (tokenizeLine("808.9")[0] as FloatToken).value)
|
||||
assertCloseTo(-0.9f, (tokenizeLine("-0.9")[0] as FloatToken).value)
|
||||
assertCloseTo(0.001f, (tokenizeLine("1e-3")[0] as FloatToken).value)
|
||||
assertCloseTo(-600.0f, (tokenizeLine("-6e2")[0] as FloatToken).value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalid_floats_area_parsed_as_InvalidNumberTokens_or_InvalidSectionTokens() {
|
||||
val tokens1 = tokenizeLine(" 808.9a ")
|
||||
|
||||
assertEquals(1, tokens1.size)
|
||||
assertEquals(InvalidNumberToken::class, tokens1[0]::class)
|
||||
assertEquals(2, tokens1[0].col)
|
||||
assertEquals(6, tokens1[0].len)
|
||||
|
||||
val tokens2 = tokenizeLine(" -55e ")
|
||||
|
||||
assertEquals(1, tokens2.size)
|
||||
assertEquals(InvalidNumberToken::class, tokens2[0]::class)
|
||||
assertEquals(3, tokens2[0].col)
|
||||
assertEquals(4, tokens2[0].len)
|
||||
|
||||
val tokens3 = tokenizeLine(".7429")
|
||||
|
||||
assertEquals(1, tokens3.size)
|
||||
assertEquals(InvalidSectionToken::class, tokens3[0]::class)
|
||||
assertEquals(1, tokens3[0].col)
|
||||
assertEquals(5, tokens3[0].len)
|
||||
|
||||
val tokens4 = tokenizeLine("\t\t\t4. test")
|
||||
|
||||
assertEquals(2, tokens4.size)
|
||||
assertEquals(InvalidNumberToken::class, tokens4[0]::class)
|
||||
assertEquals(4, tokens4[0].col)
|
||||
assertEquals(2, tokens4[0].len)
|
||||
}
|
||||
}
|
@ -18,6 +18,9 @@ fun Val<Any?>.isNull(): Val<Boolean> =
|
||||
fun Val<Any?>.isNotNull(): Val<Boolean> =
|
||||
map { it != null }
|
||||
|
||||
fun <T> Val<T?>.orElse(defaultValue: () -> T): Val<T> =
|
||||
map { it ?: defaultValue() }
|
||||
|
||||
infix fun <T : Comparable<T>> Val<T>.gt(value: T): Val<Boolean> =
|
||||
map { it > value }
|
||||
|
||||
|
@ -25,6 +25,9 @@ abstract class RegularValTests : ValTests() {
|
||||
|
||||
// Test `isNotNull`.
|
||||
assertEquals(any != null, value.isNotNull().value)
|
||||
|
||||
// Test `orElse`.
|
||||
assertEquals(any ?: "default", value.orElse { "default" }.value)
|
||||
}
|
||||
listOf(10 to 10, 5 to 99, "a" to "a", "x" to "y").forEach { (a, b) ->
|
||||
val aVal = createWithValue(a)
|
||||
|
@ -39,8 +39,11 @@ dependencies {
|
||||
implementation("io.ktor:ktor-client-core-js:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core-js:1.0.0")
|
||||
implementation(npm("golden-layout", "1.5.9"))
|
||||
implementation(npm("@babylonjs/core", "4.2.0-rc.5"))
|
||||
implementation(npm("@babylonjs/core", "^4.2.0-rc.5"))
|
||||
implementation(npm("golden-layout", "^1.5.9"))
|
||||
implementation(npm("monaco-editor", "^0.21.2"))
|
||||
|
||||
implementation(devNpm("file-loader", "^6.0.0"))
|
||||
|
||||
testImplementation(kotlin("test-js"))
|
||||
testImplementation(project(":test-utils"))
|
||||
|
@ -51,8 +51,6 @@ class DockWidget(
|
||||
|
||||
init {
|
||||
js("""require("golden-layout/src/css/goldenlayout-base.css");""")
|
||||
|
||||
observeResize()
|
||||
}
|
||||
|
||||
override fun Node.createElement() =
|
||||
@ -94,11 +92,11 @@ class DockWidget(
|
||||
|
||||
style.width = ""
|
||||
style.height = ""
|
||||
}
|
||||
|
||||
override fun resized(width: Double, height: Double) {
|
||||
goldenLayout.updateSize(width, height)
|
||||
}
|
||||
addDisposable(size.observe { (size) ->
|
||||
goldenLayout.updateSize(size.width, size.height)
|
||||
})
|
||||
}
|
||||
|
||||
override fun internalDispose() {
|
||||
goldenLayout.destroy()
|
||||
@ -155,6 +153,7 @@ class DockWidget(
|
||||
.pw-core-dock {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#pw-root .lm_header {
|
||||
|
@ -19,8 +19,6 @@ class RendererWidget(
|
||||
className = "pw-core-renderer"
|
||||
tabIndex = -1
|
||||
|
||||
observeResize()
|
||||
|
||||
observe(selfOrAncestorVisible) { visible ->
|
||||
if (visible) {
|
||||
renderer.startRendering()
|
||||
@ -29,14 +27,14 @@ class RendererWidget(
|
||||
}
|
||||
}
|
||||
|
||||
addDisposable(size.observe { (size) ->
|
||||
canvas.width = floor(size.width).toInt()
|
||||
canvas.height = floor(size.height).toInt()
|
||||
})
|
||||
|
||||
append(canvas)
|
||||
}
|
||||
|
||||
override fun resized(width: Double, height: Double) {
|
||||
canvas.width = floor(width).toInt()
|
||||
canvas.height = floor(height).toInt()
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
@Suppress("CssUnusedSymbol")
|
||||
|
@ -495,4 +495,11 @@ external class Color4(
|
||||
var g: Double
|
||||
var b: Double
|
||||
var a: Double
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates a new Color4 from integer values (< 256)
|
||||
*/
|
||||
fun FromInts(r: Int, g: Int, b: Int, a: Int): Color4
|
||||
}
|
||||
}
|
||||
|
769
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/editor.kt
vendored
Normal file
769
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/editor.kt
vendored
Normal file
@ -0,0 +1,769 @@
|
||||
@file:JsModule("monaco-editor")
|
||||
@file:JsNonModule
|
||||
@file:JsQualifier("editor")
|
||||
@file:Suppress("unused", "PropertyName")
|
||||
|
||||
package world.phantasmal.web.externals.monacoEditor
|
||||
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.Range
|
||||
|
||||
external fun create(
|
||||
domElement: HTMLElement,
|
||||
options: IStandaloneEditorConstructionOptions = definedExternally,
|
||||
): IStandaloneCodeEditor
|
||||
|
||||
external fun createModel(
|
||||
value: String,
|
||||
language: String = definedExternally,
|
||||
uri: Uri = definedExternally,
|
||||
): ITextModel
|
||||
|
||||
external fun defineTheme(themeName: String, themeData: IStandaloneThemeData)
|
||||
|
||||
external interface IStandaloneThemeData {
|
||||
var base: String /* 'vs' | 'vs-dark' | 'hc-black' */
|
||||
var inherit: Boolean
|
||||
var rules: Array<ITokenThemeRule>
|
||||
var encodedTokensColors: Array<String>?
|
||||
var colors: IColors
|
||||
}
|
||||
|
||||
external interface IColors
|
||||
|
||||
external interface ITokenThemeRule {
|
||||
var token: String
|
||||
var foreground: String?
|
||||
var background: String?
|
||||
var fontStyle: String?
|
||||
}
|
||||
|
||||
external enum class ScrollType {
|
||||
Smooth /* = 0 */,
|
||||
Immediate /* = 1 */
|
||||
}
|
||||
|
||||
external interface IDimension {
|
||||
var width: Number
|
||||
var height: Number
|
||||
}
|
||||
|
||||
external interface IEditor {
|
||||
fun onDidDispose(listener: () -> Unit): IDisposable
|
||||
fun dispose()
|
||||
fun getId(): String
|
||||
fun getEditorType(): String
|
||||
fun updateOptions(newOptions: IEditorOptions)
|
||||
fun layout(dimension: IDimension = definedExternally)
|
||||
fun focus()
|
||||
fun hasTextFocus(): Boolean
|
||||
fun saveViewState(): dynamic /* ICodeEditorViewState? | IDiffEditorViewState? */
|
||||
fun getVisibleColumnFromPosition(position: IPosition): Number
|
||||
fun getPosition(): Position?
|
||||
fun setPosition(position: IPosition)
|
||||
fun revealLine(lineNumber: Number, scrollType: ScrollType = definedExternally)
|
||||
fun revealLineInCenter(lineNumber: Number, scrollType: ScrollType = definedExternally)
|
||||
fun revealLineInCenterIfOutsideViewport(
|
||||
lineNumber: Number,
|
||||
scrollType: ScrollType = definedExternally,
|
||||
)
|
||||
|
||||
fun revealLineNearTop(lineNumber: Number, scrollType: ScrollType = definedExternally)
|
||||
fun revealPosition(position: IPosition, scrollType: ScrollType = definedExternally)
|
||||
fun revealPositionInCenter(position: IPosition, scrollType: ScrollType = definedExternally)
|
||||
fun revealPositionInCenterIfOutsideViewport(
|
||||
position: IPosition,
|
||||
scrollType: ScrollType = definedExternally,
|
||||
)
|
||||
|
||||
fun revealPositionNearTop(position: IPosition, scrollType: ScrollType = definedExternally)
|
||||
fun getSelection(): Selection?
|
||||
fun getSelections(): Array<Selection>?
|
||||
fun setSelection(selection: IRange)
|
||||
fun setSelection(selection: Range)
|
||||
fun setSelection(selection: ISelection)
|
||||
fun setSelection(selection: Selection)
|
||||
fun setSelections(selections: Any)
|
||||
fun revealLines(
|
||||
startLineNumber: Number,
|
||||
endLineNumber: Number,
|
||||
scrollType: ScrollType = definedExternally,
|
||||
)
|
||||
|
||||
fun revealLinesInCenter(
|
||||
lineNumber: Number,
|
||||
endLineNumber: Number,
|
||||
scrollType: ScrollType = definedExternally,
|
||||
)
|
||||
|
||||
fun revealLinesInCenterIfOutsideViewport(
|
||||
lineNumber: Number,
|
||||
endLineNumber: Number,
|
||||
scrollType: ScrollType = definedExternally,
|
||||
)
|
||||
|
||||
fun revealLinesNearTop(
|
||||
lineNumber: Number,
|
||||
endLineNumber: Number,
|
||||
scrollType: ScrollType = definedExternally,
|
||||
)
|
||||
|
||||
fun revealRange(range: IRange, scrollType: ScrollType = definedExternally)
|
||||
fun revealRangeInCenter(range: IRange, scrollType: ScrollType = definedExternally)
|
||||
fun revealRangeAtTop(range: IRange, scrollType: ScrollType = definedExternally)
|
||||
fun revealRangeInCenterIfOutsideViewport(
|
||||
range: IRange,
|
||||
scrollType: ScrollType = definedExternally,
|
||||
)
|
||||
|
||||
fun revealRangeNearTop(range: IRange, scrollType: ScrollType = definedExternally)
|
||||
fun revealRangeNearTopIfOutsideViewport(
|
||||
range: IRange,
|
||||
scrollType: ScrollType = definedExternally,
|
||||
)
|
||||
|
||||
fun trigger(source: String?, handlerId: String, payload: Any)
|
||||
fun getModel(): dynamic /* ITextModel? | IDiffEditorModel? */
|
||||
fun setModel(model: ITextModel?)
|
||||
}
|
||||
|
||||
external interface ICodeEditor : IEditor {
|
||||
fun onDidChangeModelContent(listener: (e: IModelContentChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeModelLanguage(listener: (e: IModelLanguageChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeModelLanguageConfiguration(listener: (e: IModelLanguageConfigurationChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeModelOptions(listener: (e: IModelOptionsChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeCursorPosition(listener: (e: ICursorPositionChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeCursorSelection(listener: (e: ICursorSelectionChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeModelDecorations(listener: (e: IModelDecorationsChangedEvent) -> Unit): IDisposable
|
||||
fun onDidFocusEditorText(listener: () -> Unit): IDisposable
|
||||
fun onDidBlurEditorText(listener: () -> Unit): IDisposable
|
||||
fun onDidFocusEditorWidget(listener: () -> Unit): IDisposable
|
||||
fun onDidBlurEditorWidget(listener: () -> Unit): IDisposable
|
||||
fun onDidCompositionStart(listener: () -> Unit): IDisposable
|
||||
fun onDidCompositionEnd(listener: () -> Unit): IDisposable
|
||||
fun onDidAttemptReadOnlyEdit(listener: () -> Unit): IDisposable
|
||||
fun hasWidgetFocus(): Boolean
|
||||
override fun getModel(): ITextModel?
|
||||
override fun setModel(model: ITextModel?)
|
||||
fun getRawOptions(): IEditorOptions
|
||||
fun setValue(newValue: String)
|
||||
fun getContentWidth(): Number
|
||||
fun getScrollWidth(): Number
|
||||
fun getScrollLeft(): Number
|
||||
fun getContentHeight(): Number
|
||||
fun getScrollHeight(): Number
|
||||
fun getScrollTop(): Number
|
||||
fun pushUndoStop(): Boolean
|
||||
fun executeEdits(
|
||||
source: String?,
|
||||
edits: Array<IIdentifiedSingleEditOperation>,
|
||||
endCursorState: ICursorStateComputer = definedExternally,
|
||||
): Boolean
|
||||
|
||||
fun executeEdits(
|
||||
source: String?,
|
||||
edits: Array<IIdentifiedSingleEditOperation>,
|
||||
endCursorState: Array<Selection> = definedExternally,
|
||||
): Boolean
|
||||
|
||||
fun getLineDecorations(lineNumber: Number): Array<IModelDecoration>?
|
||||
fun deltaDecorations(
|
||||
oldDecorations: Array<String>,
|
||||
newDecorations: Array<IModelDeltaDecoration>,
|
||||
): Array<String>
|
||||
|
||||
fun getVisibleRanges(): Array<Range>
|
||||
fun getTopForLineNumber(lineNumber: Number): Number
|
||||
fun getTopForPosition(lineNumber: Number, column: Number): Number
|
||||
fun getContainerDomNode(): HTMLElement
|
||||
fun getDomNode(): HTMLElement?
|
||||
fun getOffsetForColumn(lineNumber: Number, column: Number): Number
|
||||
fun render(forceRedraw: Boolean = definedExternally)
|
||||
fun applyFontInfo(target: HTMLElement)
|
||||
}
|
||||
|
||||
external interface IStandaloneCodeEditor : ICodeEditor {
|
||||
override fun updateOptions(newOptions: IEditorOptions /* IEditorOptions & IGlobalEditorOptions */)
|
||||
}
|
||||
|
||||
external interface IGlobalEditorOptions {
|
||||
var tabSize: Number?
|
||||
var insertSpaces: Boolean?
|
||||
var detectIndentation: Boolean?
|
||||
var trimAutoWhitespace: Boolean?
|
||||
var largeFileOptimizations: Boolean?
|
||||
var wordBasedSuggestions: Boolean?
|
||||
var stablePeek: Boolean?
|
||||
var maxTokenizationLineLength: Number?
|
||||
var theme: String?
|
||||
}
|
||||
|
||||
external interface IEditorScrollbarOptions {
|
||||
var arrowSize: Number?
|
||||
var vertical: String? /* 'auto' | 'visible' | 'hidden' */
|
||||
var horizontal: String? /* 'auto' | 'visible' | 'hidden' */
|
||||
var useShadows: Boolean?
|
||||
var verticalHasArrows: Boolean?
|
||||
var horizontalHasArrows: Boolean?
|
||||
var handleMouseWheel: Boolean?
|
||||
var alwaysConsumeMouseWheel: Boolean?
|
||||
var horizontalScrollbarSize: Number?
|
||||
var verticalScrollbarSize: Number?
|
||||
var verticalSliderSize: Number?
|
||||
var horizontalSliderSize: Number?
|
||||
}
|
||||
|
||||
external interface IEditorMinimapOptions {
|
||||
var enabled: Boolean?
|
||||
var side: String? /* 'right' | 'left' */
|
||||
var size: String? /* 'proportional' | 'fill' | 'fit' */
|
||||
var showSlider: String? /* 'always' | 'mouseover' */
|
||||
var renderCharacters: Boolean?
|
||||
var maxColumn: Number?
|
||||
var scale: Number?
|
||||
}
|
||||
|
||||
external interface IEditorFindOptions {
|
||||
var cursorMoveOnType: Boolean?
|
||||
var seedSearchStringFromSelection: Boolean?
|
||||
var autoFindInSelection: String? /* 'never' | 'always' | 'multiline' */
|
||||
var addExtraSpaceOnTop: Boolean?
|
||||
var loop: Boolean?
|
||||
}
|
||||
|
||||
external interface IEditorOptions {
|
||||
var inDiffEditor: Boolean?
|
||||
var ariaLabel: String?
|
||||
var tabIndex: Number?
|
||||
var rulers: Array<dynamic /* Number | IRulerOption */>?
|
||||
var wordSeparators: String?
|
||||
var selectionClipboard: Boolean?
|
||||
var lineNumbers: dynamic /* String | String | String | String | ((lineNumber: Number) -> String)? */
|
||||
var cursorSurroundingLines: Number?
|
||||
var cursorSurroundingLinesStyle: String? /* 'default' | 'all' */
|
||||
var renderFinalNewline: Boolean?
|
||||
var unusualLineTerminators: String? /* 'off' | 'prompt' | 'auto' */
|
||||
var selectOnLineNumbers: Boolean?
|
||||
var lineNumbersMinChars: Number?
|
||||
var glyphMargin: Boolean?
|
||||
var lineDecorationsWidth: dynamic /* Number? | String? */
|
||||
var revealHorizontalRightPadding: Number?
|
||||
var roundedSelection: Boolean?
|
||||
var extraEditorClassName: String?
|
||||
var readOnly: Boolean?
|
||||
var renameOnType: Boolean?
|
||||
var renderValidationDecorations: String? /* 'editable' | 'on' | 'off' */
|
||||
var scrollbar: IEditorScrollbarOptions?
|
||||
var minimap: IEditorMinimapOptions?
|
||||
var find: IEditorFindOptions?
|
||||
var fixedOverflowWidgets: Boolean?
|
||||
var overviewRulerLanes: Number?
|
||||
var overviewRulerBorder: Boolean?
|
||||
var cursorBlinking: String? /* 'blink' | 'smooth' | 'phase' | 'expand' | 'solid' */
|
||||
var mouseWheelZoom: Boolean?
|
||||
var mouseStyle: String? /* 'text' | 'default' | 'copy' */
|
||||
var cursorSmoothCaretAnimation: Boolean?
|
||||
var cursorStyle: String? /* 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin' */
|
||||
var cursorWidth: Number?
|
||||
var fontLigatures: dynamic /* Boolean? | String? */
|
||||
var disableLayerHinting: Boolean?
|
||||
var disableMonospaceOptimizations: Boolean?
|
||||
var hideCursorInOverviewRuler: Boolean?
|
||||
var scrollBeyondLastLine: Boolean?
|
||||
var scrollBeyondLastColumn: Number?
|
||||
var smoothScrolling: Boolean?
|
||||
var automaticLayout: Boolean?
|
||||
var wordWrap: String? /* 'off' | 'on' | 'wordWrapColumn' | 'bounded' */
|
||||
var wordWrapColumn: Number?
|
||||
var wordWrapMinified: Boolean?
|
||||
var wrappingIndent: String? /* 'none' | 'same' | 'indent' | 'deepIndent' */
|
||||
var wrappingStrategy: String? /* 'simple' | 'advanced' */
|
||||
var wordWrapBreakBeforeCharacters: String?
|
||||
var wordWrapBreakAfterCharacters: String?
|
||||
var stopRenderingLineAfter: Number?
|
||||
var links: Boolean?
|
||||
var colorDecorators: Boolean?
|
||||
var contextmenu: Boolean?
|
||||
var mouseWheelScrollSensitivity: Number?
|
||||
var fastScrollSensitivity: Number?
|
||||
var scrollPredominantAxis: Boolean?
|
||||
var columnSelection: Boolean?
|
||||
var multiCursorModifier: String? /* 'ctrlCmd' | 'alt' */
|
||||
var multiCursorMergeOverlapping: Boolean?
|
||||
var multiCursorPaste: String? /* 'spread' | 'full' */
|
||||
var accessibilitySupport: String? /* 'auto' | 'off' | 'on' */
|
||||
var accessibilityPageSize: Number?
|
||||
var quickSuggestions: dynamic /* Boolean? | IQuickSuggestionsOptions? */
|
||||
var quickSuggestionsDelay: Number?
|
||||
var autoClosingBrackets: String? /* 'always' | 'languageDefined' | 'beforeWhitespace' | 'never' */
|
||||
var autoClosingQuotes: String? /* 'always' | 'languageDefined' | 'beforeWhitespace' | 'never' */
|
||||
var autoClosingOvertype: String? /* 'always' | 'auto' | 'never' */
|
||||
var autoSurround: String? /* 'languageDefined' | 'quotes' | 'brackets' | 'never' */
|
||||
var autoIndent: String? /* 'none' | 'keep' | 'brackets' | 'advanced' | 'full' */
|
||||
var formatOnType: Boolean?
|
||||
var formatOnPaste: Boolean?
|
||||
var dragAndDrop: Boolean?
|
||||
var suggestOnTriggerCharacters: Boolean?
|
||||
var acceptSuggestionOnEnter: String? /* 'on' | 'smart' | 'off' */
|
||||
var acceptSuggestionOnCommitCharacter: Boolean?
|
||||
var snippetSuggestions: String? /* 'top' | 'bottom' | 'inline' | 'none' */
|
||||
var emptySelectionClipboard: Boolean?
|
||||
var copyWithSyntaxHighlighting: Boolean?
|
||||
var suggestSelection: String? /* 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix' */
|
||||
var suggestFontSize: Number?
|
||||
var suggestLineHeight: Number?
|
||||
var tabCompletion: String? /* 'on' | 'off' | 'onlySnippets' */
|
||||
var selectionHighlight: Boolean?
|
||||
var occurrencesHighlight: Boolean?
|
||||
var codeLens: Boolean?
|
||||
var codeActionsOnSaveTimeout: Number?
|
||||
var folding: Boolean?
|
||||
var foldingStrategy: String? /* 'auto' | 'indentation' */
|
||||
var foldingHighlight: Boolean?
|
||||
var showFoldingControls: String? /* 'always' | 'mouseover' */
|
||||
var unfoldOnClickAfterEndOfLine: Boolean?
|
||||
var matchBrackets: String? /* 'never' | 'near' | 'always' */
|
||||
var renderWhitespace: String? /* 'none' | 'boundary' | 'selection' | 'trailing' | 'all' */
|
||||
var renderControlCharacters: Boolean?
|
||||
var renderIndentGuides: Boolean?
|
||||
var highlightActiveIndentGuide: Boolean?
|
||||
var renderLineHighlight: String? /* 'none' | 'gutter' | 'line' | 'all' */
|
||||
var renderLineHighlightOnlyWhenFocus: Boolean?
|
||||
var useTabStops: Boolean?
|
||||
var fontFamily: String?
|
||||
var fontWeight: String?
|
||||
var fontSize: Number?
|
||||
var lineHeight: Number?
|
||||
var letterSpacing: Number?
|
||||
var showUnused: Boolean?
|
||||
var peekWidgetDefaultFocus: String? /* 'tree' | 'editor' */
|
||||
var definitionLinkOpensInPeek: Boolean?
|
||||
var showDeprecated: Boolean?
|
||||
}
|
||||
|
||||
external interface IEditorConstructionOptions : IEditorOptions {
|
||||
var overflowWidgetsDomNode: HTMLElement?
|
||||
}
|
||||
|
||||
external interface IStandaloneEditorConstructionOptions : IEditorConstructionOptions,
|
||||
IGlobalEditorOptions {
|
||||
var model: ITextModel?
|
||||
var value: String?
|
||||
var language: String?
|
||||
override var theme: String?
|
||||
var accessibilityHelpUrl: String?
|
||||
}
|
||||
|
||||
external interface IMarker {
|
||||
var owner: String
|
||||
var resource: Uri
|
||||
var severity: MarkerSeverity
|
||||
var code: dynamic /* String? | `T$5`? */
|
||||
var message: String
|
||||
var source: String?
|
||||
var startLineNumber: Number
|
||||
var startColumn: Number
|
||||
var endLineNumber: Number
|
||||
var endColumn: Number
|
||||
var relatedInformation: Array<IRelatedInformation>?
|
||||
var tags: Array<MarkerTag>?
|
||||
}
|
||||
|
||||
external interface IMarkerData {
|
||||
var code: dynamic /* String? | `T$5`? */
|
||||
var severity: MarkerSeverity
|
||||
var message: String
|
||||
var source: String?
|
||||
var startLineNumber: Number
|
||||
var startColumn: Number
|
||||
var endLineNumber: Number
|
||||
var endColumn: Number
|
||||
var relatedInformation: Array<IRelatedInformation>?
|
||||
var tags: Array<MarkerTag>?
|
||||
}
|
||||
|
||||
external interface IRelatedInformation {
|
||||
var resource: Uri
|
||||
var message: String
|
||||
var startLineNumber: Number
|
||||
var startColumn: Number
|
||||
var endLineNumber: Number
|
||||
var endColumn: Number
|
||||
}
|
||||
|
||||
external interface IColorizerOptions {
|
||||
var tabSize: Number?
|
||||
}
|
||||
|
||||
external interface IColorizerElementOptions : IColorizerOptions {
|
||||
var theme: String?
|
||||
var mimeType: String?
|
||||
}
|
||||
|
||||
external enum class ScrollbarVisibility {
|
||||
Auto /* = 1 */,
|
||||
Hidden /* = 2 */,
|
||||
Visible /* = 3 */
|
||||
}
|
||||
|
||||
external interface ThemeColor {
|
||||
var id: String
|
||||
}
|
||||
|
||||
external enum class OverviewRulerLane {
|
||||
Left /* = 1 */,
|
||||
Center /* = 2 */,
|
||||
Right /* = 4 */,
|
||||
Full /* = 7 */
|
||||
}
|
||||
|
||||
external enum class MinimapPosition {
|
||||
Inline /* = 1 */,
|
||||
Gutter /* = 2 */
|
||||
}
|
||||
|
||||
external interface IDecorationOptions {
|
||||
var color: dynamic /* String? | ThemeColor? */
|
||||
var darkColor: dynamic /* String? | ThemeColor? */
|
||||
}
|
||||
|
||||
external interface IModelDecorationOverviewRulerOptions : IDecorationOptions {
|
||||
var position: OverviewRulerLane
|
||||
}
|
||||
|
||||
external interface IModelDecorationMinimapOptions : IDecorationOptions {
|
||||
var position: MinimapPosition
|
||||
}
|
||||
|
||||
external interface IModelDecorationOptions {
|
||||
var stickiness: TrackedRangeStickiness?
|
||||
var className: String?
|
||||
var glyphMarginHoverMessage: dynamic /* IMarkdownString? | Array<IMarkdownString>? */
|
||||
var hoverMessage: dynamic /* IMarkdownString? | Array<IMarkdownString>? */
|
||||
var isWholeLine: Boolean?
|
||||
var zIndex: Number?
|
||||
var overviewRuler: IModelDecorationOverviewRulerOptions?
|
||||
var minimap: IModelDecorationMinimapOptions?
|
||||
var glyphMarginClassName: String?
|
||||
var linesDecorationsClassName: String?
|
||||
var firstLineDecorationClassName: String?
|
||||
var marginClassName: String?
|
||||
var inlineClassName: String?
|
||||
var inlineClassNameAffectsLetterSpacing: Boolean?
|
||||
var beforeContentClassName: String?
|
||||
var afterContentClassName: String
|
||||
}
|
||||
|
||||
external interface IModelDeltaDecoration {
|
||||
var range: IRange
|
||||
var options: IModelDecorationOptions
|
||||
}
|
||||
|
||||
external interface IModelDecoration {
|
||||
var id: String
|
||||
var ownerId: Number
|
||||
var range: Range
|
||||
var options: IModelDecorationOptions
|
||||
}
|
||||
|
||||
external interface IWordAtPosition {
|
||||
var word: String
|
||||
var startColumn: Number
|
||||
var endColumn: Number
|
||||
}
|
||||
|
||||
external enum class EndOfLinePreference {
|
||||
TextDefined /* = 0 */,
|
||||
LF /* = 1 */,
|
||||
CRLF /* = 2 */
|
||||
}
|
||||
|
||||
external enum class DefaultEndOfLine {
|
||||
LF /* = 1 */,
|
||||
CRLF /* = 2 */
|
||||
}
|
||||
|
||||
external enum class EndOfLineSequence {
|
||||
LF /* = 0 */,
|
||||
CRLF /* = 1 */
|
||||
}
|
||||
|
||||
external interface ISingleEditOperation {
|
||||
var range: IRange
|
||||
var text: String?
|
||||
var forceMoveMarkers: Boolean?
|
||||
}
|
||||
|
||||
external interface IIdentifiedSingleEditOperation {
|
||||
var range: IRange
|
||||
var text: String?
|
||||
var forceMoveMarkers: Boolean?
|
||||
}
|
||||
|
||||
external interface IValidEditOperation {
|
||||
var range: Range
|
||||
var text: String
|
||||
}
|
||||
|
||||
external interface ICursorStateComputer
|
||||
|
||||
open external class TextModelResolvedOptions {
|
||||
open var _textModelResolvedOptionsBrand: Unit
|
||||
open var tabSize: Number
|
||||
open var indentSize: Number
|
||||
open var insertSpaces: Boolean
|
||||
open var defaultEOL: DefaultEndOfLine
|
||||
open var trimAutoWhitespace: Boolean
|
||||
}
|
||||
|
||||
external interface ITextModelUpdateOptions {
|
||||
var tabSize: Number?
|
||||
var indentSize: Number?
|
||||
var insertSpaces: Boolean?
|
||||
var trimAutoWhitespace: Boolean?
|
||||
}
|
||||
|
||||
open external class FindMatch {
|
||||
open var _findMatchBrand: Unit
|
||||
open var range: Range
|
||||
open var matches: Array<String>?
|
||||
}
|
||||
|
||||
external enum class TrackedRangeStickiness {
|
||||
AlwaysGrowsWhenTypingAtEdges /* = 0 */,
|
||||
NeverGrowsWhenTypingAtEdges /* = 1 */,
|
||||
GrowsOnlyWhenTypingBefore /* = 2 */,
|
||||
GrowsOnlyWhenTypingAfter /* = 3 */
|
||||
}
|
||||
|
||||
external interface ITextModel {
|
||||
var uri: Uri
|
||||
var id: String
|
||||
fun getOptions(): TextModelResolvedOptions
|
||||
fun getVersionId(): Number
|
||||
fun getAlternativeVersionId(): Number
|
||||
fun setValue(newValue: String)
|
||||
fun getValue(
|
||||
eol: EndOfLinePreference = definedExternally,
|
||||
preserveBOM: Boolean = definedExternally,
|
||||
): String
|
||||
|
||||
fun getValueLength(
|
||||
eol: EndOfLinePreference = definedExternally,
|
||||
preserveBOM: Boolean = definedExternally,
|
||||
): Number
|
||||
|
||||
fun getValueInRange(range: IRange, eol: EndOfLinePreference = definedExternally): String
|
||||
fun getValueLengthInRange(range: IRange): Number
|
||||
fun getCharacterCountInRange(range: IRange): Number
|
||||
fun getLineCount(): Number
|
||||
fun getLineContent(lineNumber: Number): String
|
||||
fun getLineLength(lineNumber: Number): Number
|
||||
fun getLinesContent(): Array<String>
|
||||
fun getEOL(): String
|
||||
fun getLineMinColumn(lineNumber: Number): Number
|
||||
fun getLineMaxColumn(lineNumber: Number): Number
|
||||
fun getLineFirstNonWhitespaceColumn(lineNumber: Number): Number
|
||||
fun getLineLastNonWhitespaceColumn(lineNumber: Number): Number
|
||||
fun validatePosition(position: IPosition): Position
|
||||
fun modifyPosition(position: IPosition, offset: Number): Position
|
||||
fun validateRange(range: IRange): Range
|
||||
fun getOffsetAt(position: IPosition): Number
|
||||
fun getPositionAt(offset: Number): Position
|
||||
fun getFullModelRange(): Range
|
||||
fun isDisposed(): Boolean
|
||||
fun findMatches(
|
||||
searchString: String,
|
||||
searchOnlyEditableRange: Boolean,
|
||||
isRegex: Boolean,
|
||||
matchCase: Boolean,
|
||||
wordSeparators: String?,
|
||||
captureMatches: Boolean,
|
||||
limitResultCount: Number = definedExternally,
|
||||
): Array<FindMatch>
|
||||
|
||||
fun findMatches(
|
||||
searchString: String,
|
||||
searchScope: IRange,
|
||||
isRegex: Boolean,
|
||||
matchCase: Boolean,
|
||||
wordSeparators: String?,
|
||||
captureMatches: Boolean,
|
||||
limitResultCount: Number = definedExternally,
|
||||
): Array<FindMatch>
|
||||
|
||||
fun findMatches(
|
||||
searchString: String,
|
||||
searchScope: Array<IRange>,
|
||||
isRegex: Boolean,
|
||||
matchCase: Boolean,
|
||||
wordSeparators: String?,
|
||||
captureMatches: Boolean,
|
||||
limitResultCount: Number = definedExternally,
|
||||
): Array<FindMatch>
|
||||
|
||||
fun findNextMatch(
|
||||
searchString: String,
|
||||
searchStart: IPosition,
|
||||
isRegex: Boolean,
|
||||
matchCase: Boolean,
|
||||
wordSeparators: String?,
|
||||
captureMatches: Boolean,
|
||||
): FindMatch?
|
||||
|
||||
fun findPreviousMatch(
|
||||
searchString: String,
|
||||
searchStart: IPosition,
|
||||
isRegex: Boolean,
|
||||
matchCase: Boolean,
|
||||
wordSeparators: String?,
|
||||
captureMatches: Boolean,
|
||||
): FindMatch?
|
||||
|
||||
fun getModeId(): String
|
||||
fun getWordAtPosition(position: IPosition): IWordAtPosition?
|
||||
fun getWordUntilPosition(position: IPosition): IWordAtPosition
|
||||
fun deltaDecorations(
|
||||
oldDecorations: Array<String>,
|
||||
newDecorations: Array<IModelDeltaDecoration>,
|
||||
ownerId: Number = definedExternally,
|
||||
): Array<String>
|
||||
|
||||
fun getDecorationOptions(id: String): IModelDecorationOptions?
|
||||
fun getDecorationRange(id: String): Range?
|
||||
fun getLineDecorations(
|
||||
lineNumber: Number,
|
||||
ownerId: Number = definedExternally,
|
||||
filterOutValidation: Boolean = definedExternally,
|
||||
): Array<IModelDecoration>
|
||||
|
||||
fun getLinesDecorations(
|
||||
startLineNumber: Number,
|
||||
endLineNumber: Number,
|
||||
ownerId: Number = definedExternally,
|
||||
filterOutValidation: Boolean = definedExternally,
|
||||
): Array<IModelDecoration>
|
||||
|
||||
fun getDecorationsInRange(
|
||||
range: IRange,
|
||||
ownerId: Number = definedExternally,
|
||||
filterOutValidation: Boolean = definedExternally,
|
||||
): Array<IModelDecoration>
|
||||
|
||||
fun getAllDecorations(
|
||||
ownerId: Number = definedExternally,
|
||||
filterOutValidation: Boolean = definedExternally,
|
||||
): Array<IModelDecoration>
|
||||
|
||||
fun getOverviewRulerDecorations(
|
||||
ownerId: Number = definedExternally,
|
||||
filterOutValidation: Boolean = definedExternally,
|
||||
): Array<IModelDecoration>
|
||||
|
||||
fun normalizeIndentation(str: String): String
|
||||
fun updateOptions(newOpts: ITextModelUpdateOptions)
|
||||
fun detectIndentation(defaultInsertSpaces: Boolean, defaultTabSize: Number)
|
||||
fun pushStackElement()
|
||||
fun pushEditOperations(
|
||||
beforeCursorState: Array<Selection>?,
|
||||
editOperations: Array<IIdentifiedSingleEditOperation>,
|
||||
cursorStateComputer: ICursorStateComputer,
|
||||
): Array<Selection>?
|
||||
|
||||
fun pushEOL(eol: EndOfLineSequence)
|
||||
fun applyEdits(operations: Array<IIdentifiedSingleEditOperation>)
|
||||
fun applyEdits(
|
||||
operations: Array<IIdentifiedSingleEditOperation>,
|
||||
computeUndoEdits: Boolean,
|
||||
): dynamic /* Unit | Array */
|
||||
|
||||
fun setEOL(eol: EndOfLineSequence)
|
||||
fun onDidChangeContent(listener: (e: IModelContentChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeDecorations(listener: (e: IModelDecorationsChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeOptions(listener: (e: IModelOptionsChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeLanguage(listener: (e: IModelLanguageChangedEvent) -> Unit): IDisposable
|
||||
fun onDidChangeLanguageConfiguration(listener: (e: IModelLanguageConfigurationChangedEvent) -> Unit): IDisposable
|
||||
fun onWillDispose(listener: () -> Unit): IDisposable
|
||||
fun dispose()
|
||||
}
|
||||
|
||||
external object EditorType {
|
||||
var ICodeEditor: String
|
||||
var IDiffEditor: String
|
||||
}
|
||||
|
||||
external interface IModelLanguageChangedEvent {
|
||||
var oldLanguage: String
|
||||
var newLanguage: String
|
||||
}
|
||||
|
||||
external interface IModelLanguageConfigurationChangedEvent
|
||||
|
||||
external interface IModelContentChange {
|
||||
var range: IRange
|
||||
var rangeOffset: Number
|
||||
var rangeLength: Number
|
||||
var text: String
|
||||
}
|
||||
|
||||
external interface IModelContentChangedEvent {
|
||||
var changes: Array<IModelContentChange>
|
||||
var eol: String
|
||||
var versionId: Number
|
||||
var isUndoing: Boolean
|
||||
var isRedoing: Boolean
|
||||
var isFlush: Boolean
|
||||
}
|
||||
|
||||
external interface IModelDecorationsChangedEvent {
|
||||
var affectsMinimap: Boolean
|
||||
var affectsOverviewRuler: Boolean
|
||||
}
|
||||
|
||||
external interface IModelOptionsChangedEvent {
|
||||
var tabSize: Boolean
|
||||
var indentSize: Boolean
|
||||
var insertSpaces: Boolean
|
||||
var trimAutoWhitespace: Boolean
|
||||
}
|
||||
|
||||
external enum class CursorChangeReason {
|
||||
NotSet /* = 0 */,
|
||||
ContentFlush /* = 1 */,
|
||||
RecoverFromMarkers /* = 2 */,
|
||||
Explicit /* = 3 */,
|
||||
Paste /* = 4 */,
|
||||
Undo /* = 5 */,
|
||||
Redo /* = 6 */
|
||||
}
|
||||
|
||||
external interface ICursorPositionChangedEvent {
|
||||
var position: Position
|
||||
var secondaryPositions: Array<Position>
|
||||
var reason: CursorChangeReason
|
||||
var source: String
|
||||
}
|
||||
|
||||
external interface ICursorSelectionChangedEvent {
|
||||
var selection: Selection
|
||||
var secondarySelections: Array<Selection>
|
||||
var modelVersionId: Number
|
||||
var oldSelections: Array<Selection>?
|
||||
var oldModelVersionId: Number
|
||||
var source: String
|
||||
var reason: CursorChangeReason
|
||||
}
|
||||
|
||||
external enum class AccessibilitySupport {
|
||||
Unknown /* = 0 */,
|
||||
Disabled /* = 1 */,
|
||||
Enabled /* = 2 */
|
||||
}
|
||||
|
||||
external enum class EditorAutoIndentStrategy {
|
||||
None /* = 0 */,
|
||||
Keep /* = 1 */,
|
||||
Brackets /* = 2 */,
|
||||
Advanced /* = 3 */,
|
||||
Full /* = 4 */
|
||||
}
|
8
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/editorExtensions.kt
vendored
Normal file
8
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/editorExtensions.kt
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package world.phantasmal.web.externals.monacoEditor
|
||||
|
||||
inline operator fun IColors.get(name: String): String =
|
||||
asDynamic()[name].unsafeCast<String>()
|
||||
|
||||
inline operator fun IColors.set(name: String, value: String) {
|
||||
asDynamic()[name] = value
|
||||
}
|
220
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/languages.kt
vendored
Normal file
220
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/languages.kt
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
@file:JsModule("monaco-editor")
|
||||
@file:JsNonModule
|
||||
@file:JsQualifier("languages")
|
||||
|
||||
package world.phantasmal.web.externals.monacoEditor
|
||||
|
||||
import kotlin.js.RegExp
|
||||
|
||||
external fun register(language: ILanguageExtensionPoint)
|
||||
|
||||
external fun setLanguageConfiguration(
|
||||
languageId: String,
|
||||
configuration: LanguageConfiguration,
|
||||
): IDisposable
|
||||
|
||||
external fun setMonarchTokensProvider(
|
||||
languageId: String,
|
||||
languageDef: IMonarchLanguage,
|
||||
): IDisposable
|
||||
|
||||
external interface CommentRule {
|
||||
var lineComment: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var blockComment: dynamic /* JsTuple<String, String> */
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface LanguageConfiguration {
|
||||
var comments: CommentRule?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var brackets: Array<dynamic /* JsTuple<String, String> */>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var wordPattern: RegExp?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var indentationRules: IndentationRule?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var onEnterRules: Array<OnEnterRule>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var autoClosingPairs: Array<IAutoClosingPairConditional>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var surroundingPairs: Array<IAutoClosingPair>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var autoCloseBefore: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var folding: FoldingRules?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface IndentationRule {
|
||||
var decreaseIndentPattern: RegExp
|
||||
var increaseIndentPattern: RegExp
|
||||
var indentNextLinePattern: RegExp?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var unIndentedLinePattern: RegExp?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface FoldingMarkers {
|
||||
var start: RegExp
|
||||
var end: RegExp
|
||||
}
|
||||
|
||||
external interface FoldingRules {
|
||||
var offSide: Boolean?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var markers: FoldingMarkers?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface OnEnterRule {
|
||||
var beforeText: RegExp
|
||||
var afterText: RegExp?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var oneLineAboveText: RegExp?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var action: EnterAction
|
||||
}
|
||||
|
||||
external interface IDocComment {
|
||||
var open: String
|
||||
var close: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface IAutoClosingPair {
|
||||
var open: String
|
||||
var close: String
|
||||
}
|
||||
|
||||
external interface IAutoClosingPairConditional : IAutoClosingPair {
|
||||
var notIn: Array<String>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external enum class IndentAction {
|
||||
None /* = 0 */,
|
||||
Indent /* = 1 */,
|
||||
IndentOutdent /* = 2 */,
|
||||
Outdent /* = 3 */
|
||||
}
|
||||
|
||||
external interface EnterAction {
|
||||
var indentAction: IndentAction
|
||||
var appendText: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var removeText: Number?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface ILanguageExtensionPoint {
|
||||
var id: String
|
||||
var extensions: Array<String>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var filenames: Array<String>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var filenamePatterns: Array<String>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var firstLine: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var aliases: Array<String>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var mimetypes: Array<String>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var configuration: Uri?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface IMonarchLanguageTokenizer
|
||||
|
||||
external interface IMonarchLanguage {
|
||||
var tokenizer: IMonarchLanguageTokenizer
|
||||
var ignoreCase: Boolean?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var unicode: Boolean?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var defaultToken: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var brackets: Array<IMonarchLanguageBracket>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var start: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var tokenPostfix: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface IExpandedMonarchLanguageRule {
|
||||
var regex: RegExp
|
||||
var action: IExpandedMonarchLanguageAction
|
||||
var include: String
|
||||
}
|
||||
|
||||
external interface IExpandedMonarchLanguageAction {
|
||||
var group: Array<dynamic /* IShortMonarchLanguageAction | IExpandedMonarchLanguageAction | Array<IShortMonarchLanguageAction> | Array<IExpandedMonarchLanguageAction> */>?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var cases: Any?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var token: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var next: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var switchTo: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var goBack: Number?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var bracket: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var nextEmbedded: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
var log: String?
|
||||
get() = definedExternally
|
||||
set(value) = definedExternally
|
||||
}
|
||||
|
||||
external interface IMonarchLanguageBracket {
|
||||
var open: String
|
||||
var close: String
|
||||
var token: String
|
||||
}
|
10
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/languagesExtensions.kt
vendored
Normal file
10
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/languagesExtensions.kt
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
package world.phantasmal.web.externals.monacoEditor
|
||||
|
||||
typealias IMonarchLanguageRule = IExpandedMonarchLanguageRule
|
||||
|
||||
inline operator fun IMonarchLanguageTokenizer.get(name: String): Array<IMonarchLanguageRule> =
|
||||
asDynamic()[name].unsafeCast<Array<IMonarchLanguageRule>>()
|
||||
|
||||
inline operator fun IMonarchLanguageTokenizer.set(name: String, value: Array<IMonarchLanguageRule>) {
|
||||
asDynamic()[name] = value
|
||||
}
|
183
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/monacoEditor.kt
vendored
Normal file
183
web/src/main/kotlin/world/phantasmal/web/externals/monacoEditor/monacoEditor.kt
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
@file:JsModule("monaco-editor")
|
||||
@file:JsNonModule
|
||||
@file:Suppress("CovariantEquals", "unused")
|
||||
|
||||
package world.phantasmal.web.externals.monacoEditor
|
||||
|
||||
external interface IDisposable {
|
||||
fun dispose()
|
||||
}
|
||||
|
||||
external enum class MarkerTag {
|
||||
Unnecessary /* = 1 */,
|
||||
Deprecated /* = 2 */
|
||||
}
|
||||
|
||||
external enum class MarkerSeverity {
|
||||
Hint /* = 1 */,
|
||||
Info /* = 2 */,
|
||||
Warning /* = 4 */,
|
||||
Error /* = 8 */
|
||||
}
|
||||
|
||||
external interface IRange {
|
||||
var startLineNumber: Number
|
||||
var startColumn: Number
|
||||
var endLineNumber: Number
|
||||
var endColumn: Number
|
||||
}
|
||||
|
||||
open external class Range(
|
||||
startLineNumber: Number,
|
||||
startColumn: Number,
|
||||
endLineNumber: Number,
|
||||
endColumn: Number,
|
||||
) {
|
||||
open var startLineNumber: Number
|
||||
open var startColumn: Number
|
||||
open var endLineNumber: Number
|
||||
open var endColumn: Number
|
||||
open fun isEmpty(): Boolean
|
||||
open fun containsPosition(position: IPosition): Boolean
|
||||
open fun containsRange(range: IRange): Boolean
|
||||
open fun strictContainsRange(range: IRange): Boolean
|
||||
open fun plusRange(range: IRange): Range
|
||||
open fun intersectRanges(range: IRange): Range?
|
||||
open fun equalsRange(other: IRange?): Boolean
|
||||
open fun getEndPosition(): Position
|
||||
open fun getStartPosition(): Position
|
||||
override fun toString(): String
|
||||
open fun setEndPosition(endLineNumber: Number, endColumn: Number): Range
|
||||
open fun setStartPosition(startLineNumber: Number, startColumn: Number): Range
|
||||
open fun collapseToStart(): Range
|
||||
|
||||
companion object {
|
||||
fun isEmpty(range: IRange): Boolean
|
||||
fun containsPosition(range: IRange, position: IPosition): Boolean
|
||||
fun containsRange(range: IRange, otherRange: IRange): Boolean
|
||||
fun strictContainsRange(range: IRange, otherRange: IRange): Boolean
|
||||
fun plusRange(a: IRange, b: IRange): Range
|
||||
fun intersectRanges(a: IRange, b: IRange): Range?
|
||||
fun equalsRange(a: IRange?, b: IRange?): Boolean
|
||||
fun getEndPosition(range: IRange): Position
|
||||
fun getStartPosition(range: IRange): Position
|
||||
fun collapseToStart(range: IRange): Range
|
||||
fun fromPositions(start: IPosition, end: IPosition = definedExternally): Range
|
||||
fun lift(range: Nothing?): Nothing?
|
||||
fun lift(range: IRange): Range
|
||||
fun isIRange(obj: Any): Boolean
|
||||
fun areIntersectingOrTouching(a: IRange, b: IRange): Boolean
|
||||
fun areIntersecting(a: IRange, b: IRange): Boolean
|
||||
fun compareRangesUsingStarts(a: IRange?, b: IRange?): Number
|
||||
fun compareRangesUsingEnds(a: IRange, b: IRange): Number
|
||||
fun spansMultipleLines(range: IRange): Boolean
|
||||
}
|
||||
}
|
||||
|
||||
external interface ISelection {
|
||||
var selectionStartLineNumber: Number
|
||||
var selectionStartColumn: Number
|
||||
var positionLineNumber: Number
|
||||
var positionColumn: Number
|
||||
}
|
||||
|
||||
open external class Selection(
|
||||
selectionStartLineNumber: Number,
|
||||
selectionStartColumn: Number,
|
||||
positionLineNumber: Number,
|
||||
positionColumn: Number,
|
||||
) : Range {
|
||||
open var selectionStartLineNumber: Number
|
||||
open var selectionStartColumn: Number
|
||||
open var positionLineNumber: Number
|
||||
open var positionColumn: Number
|
||||
override fun toString(): String
|
||||
open fun equalsSelection(other: ISelection): Boolean
|
||||
open fun getDirection(): SelectionDirection
|
||||
override fun setEndPosition(endLineNumber: Number, endColumn: Number): Selection
|
||||
open fun getPosition(): Position
|
||||
override fun setStartPosition(startLineNumber: Number, startColumn: Number): Selection
|
||||
|
||||
companion object {
|
||||
fun selectionsEqual(a: ISelection, b: ISelection): Boolean
|
||||
fun fromPositions(start: IPosition, end: IPosition = definedExternally): Selection
|
||||
fun liftSelection(sel: ISelection): Selection
|
||||
fun selectionsArrEqual(a: Array<ISelection>, b: Array<ISelection>): Boolean
|
||||
fun isISelection(obj: Any): Boolean
|
||||
fun createWithDirection(
|
||||
startLineNumber: Number,
|
||||
startColumn: Number,
|
||||
endLineNumber: Number,
|
||||
endColumn: Number,
|
||||
direction: SelectionDirection,
|
||||
): Selection
|
||||
}
|
||||
}
|
||||
|
||||
external enum class SelectionDirection {
|
||||
LTR /* = 0 */,
|
||||
RTL /* = 1 */
|
||||
}
|
||||
|
||||
external interface IPosition {
|
||||
var lineNumber: Number
|
||||
var column: Number
|
||||
}
|
||||
|
||||
open external class Position(lineNumber: Number, column: Number) {
|
||||
open var lineNumber: Number
|
||||
open var column: Number
|
||||
open fun with(
|
||||
newLineNumber: Number = definedExternally,
|
||||
newColumn: Number = definedExternally,
|
||||
): Position
|
||||
|
||||
open fun delta(
|
||||
deltaLineNumber: Number = definedExternally,
|
||||
deltaColumn: Number = definedExternally,
|
||||
): Position
|
||||
|
||||
open fun equals(other: IPosition): Boolean
|
||||
open fun isBefore(other: IPosition): Boolean
|
||||
open fun isBeforeOrEqual(other: IPosition): Boolean
|
||||
open fun clone(): Position
|
||||
override fun toString(): String
|
||||
|
||||
companion object {
|
||||
fun equals(a: IPosition?, b: IPosition?): Boolean
|
||||
fun isBefore(a: IPosition, b: IPosition): Boolean
|
||||
fun isBeforeOrEqual(a: IPosition, b: IPosition): Boolean
|
||||
fun compare(a: IPosition, b: IPosition): Number
|
||||
fun lift(pos: IPosition): Position
|
||||
fun isIPosition(obj: Any): Boolean
|
||||
}
|
||||
}
|
||||
|
||||
external interface UriComponents {
|
||||
var scheme: String
|
||||
var authority: String
|
||||
var path: String
|
||||
var query: String
|
||||
var fragment: String
|
||||
}
|
||||
|
||||
open external class Uri : UriComponents {
|
||||
override var scheme: String
|
||||
override var authority: String
|
||||
override var path: String
|
||||
override var query: String
|
||||
override var fragment: String
|
||||
open fun toString(skipEncoding: Boolean = definedExternally): String
|
||||
open fun toJSON(): UriComponents
|
||||
|
||||
companion object {
|
||||
fun isUri(thing: Any): Boolean
|
||||
fun parse(value: String, _strict: Boolean = definedExternally): Uri
|
||||
fun file(path: String): Uri
|
||||
fun joinPath(uri: Uri, vararg pathFragment: String): Uri
|
||||
fun revive(data: UriComponents): Uri
|
||||
fun revive(data: Uri): Uri
|
||||
fun revive(data: UriComponents? = definedExternally): Uri?
|
||||
fun revive(data: Uri? = definedExternally): Uri?
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import world.phantasmal.lib.fileFormats.quest.Episode
|
||||
import world.phantasmal.lib.fileFormats.quest.NpcType
|
||||
import world.phantasmal.observable.value.Val
|
||||
import world.phantasmal.observable.value.mutableVal
|
||||
import world.phantasmal.observable.value.orElse
|
||||
import kotlin.time.Duration
|
||||
|
||||
class HuntMethodModel(
|
||||
@ -26,7 +27,7 @@ class HuntMethodModel(
|
||||
*/
|
||||
val userTime: Val<Duration?> = _userTime
|
||||
|
||||
val time: Val<Duration> = userTime.map { it ?: defaultTime }
|
||||
val time: Val<Duration> = userTime.orElse { defaultTime }
|
||||
|
||||
fun setUserTime(userTime: Duration?): HuntMethodModel {
|
||||
_userTime.value = userTime
|
||||
|
@ -8,6 +8,7 @@ import world.phantasmal.web.core.PwToolType
|
||||
import world.phantasmal.web.core.loading.AssetLoader
|
||||
import world.phantasmal.web.core.stores.UiStore
|
||||
import world.phantasmal.web.externals.babylon.Engine
|
||||
import world.phantasmal.web.questEditor.controllers.AssemblyEditorController
|
||||
import world.phantasmal.web.questEditor.controllers.NpcCountsController
|
||||
import world.phantasmal.web.questEditor.controllers.QuestEditorToolbarController
|
||||
import world.phantasmal.web.questEditor.controllers.QuestInfoController
|
||||
@ -18,6 +19,7 @@ import world.phantasmal.web.questEditor.rendering.QuestEditorMeshManager
|
||||
import world.phantasmal.web.questEditor.rendering.QuestRenderer
|
||||
import world.phantasmal.web.questEditor.rendering.UserInputManager
|
||||
import world.phantasmal.web.questEditor.stores.AreaStore
|
||||
import world.phantasmal.web.questEditor.stores.AssemblyEditorStore
|
||||
import world.phantasmal.web.questEditor.stores.QuestEditorStore
|
||||
import world.phantasmal.web.questEditor.widgets.*
|
||||
import world.phantasmal.webui.DisposableContainer
|
||||
@ -33,6 +35,7 @@ class QuestEditor(
|
||||
override fun initialize(scope: CoroutineScope): Widget {
|
||||
// Renderer
|
||||
val canvas = document.createElement("CANVAS") as HTMLCanvasElement
|
||||
canvas.style.outline = "none"
|
||||
val renderer = addDisposable(QuestRenderer(canvas, createEngine(canvas)))
|
||||
|
||||
// Asset Loaders
|
||||
@ -43,6 +46,7 @@ class QuestEditor(
|
||||
// Stores
|
||||
val areaStore = addDisposable(AreaStore(scope, areaAssetLoader))
|
||||
val questEditorStore = addDisposable(QuestEditorStore(scope, uiStore, areaStore))
|
||||
val assemblyEditorStore = addDisposable(AssemblyEditorStore(scope, questEditorStore))
|
||||
|
||||
// Controllers
|
||||
val toolbarController = addDisposable(QuestEditorToolbarController(
|
||||
@ -52,6 +56,7 @@ class QuestEditor(
|
||||
))
|
||||
val questInfoController = addDisposable(QuestInfoController(questEditorStore))
|
||||
val npcCountsController = addDisposable(NpcCountsController(questEditorStore))
|
||||
val assemblyEditorController = addDisposable(AssemblyEditorController(assemblyEditorStore))
|
||||
|
||||
// Rendering
|
||||
addDisposables(
|
||||
@ -71,7 +76,8 @@ class QuestEditor(
|
||||
{ s -> QuestEditorToolbarWidget(s, toolbarController) },
|
||||
{ s -> QuestInfoWidget(s, questInfoController) },
|
||||
{ s -> NpcCountsWidget(s, npcCountsController) },
|
||||
{ s -> QuestEditorRendererWidget(s, canvas, renderer) }
|
||||
{ s -> QuestEditorRendererWidget(s, canvas, renderer) },
|
||||
{ s -> AssemblyEditorWidget(s, assemblyEditorController) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
package world.phantasmal.web.questEditor.assembly
|
||||
|
||||
import world.phantasmal.core.disposable.TrackedDisposable
|
||||
|
||||
class AssemblyAnalyser : TrackedDisposable() {
|
||||
fun setAssembly(assembly: List<String>) {
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package world.phantasmal.web.questEditor.controllers
|
||||
|
||||
import world.phantasmal.observable.value.*
|
||||
import world.phantasmal.web.externals.monacoEditor.ITextModel
|
||||
import world.phantasmal.web.externals.monacoEditor.createModel
|
||||
import world.phantasmal.web.questEditor.stores.AssemblyEditorStore
|
||||
import world.phantasmal.webui.controllers.Controller
|
||||
|
||||
class AssemblyEditorController(assemblyEditorStore: AssemblyEditorStore) : Controller() {
|
||||
val textModel: Val<ITextModel> = assemblyEditorStore.textModel.orElse { EMPTY_MODEL }
|
||||
val enabled: Val<Boolean> = assemblyEditorStore.editingEnabled
|
||||
val readOnly: Val<Boolean> = enabled.not() or assemblyEditorStore.textModel.isNull()
|
||||
|
||||
companion object {
|
||||
private val EMPTY_MODEL = createModel("", AssemblyEditorStore.ASM_LANG_ID)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package world.phantasmal.web.questEditor.models
|
||||
|
||||
import world.phantasmal.lib.assembly.Segment
|
||||
import world.phantasmal.lib.fileFormats.quest.Episode
|
||||
import world.phantasmal.observable.value.Val
|
||||
import world.phantasmal.observable.value.list.ListVal
|
||||
@ -16,6 +17,7 @@ class QuestModel(
|
||||
mapDesignations: Map<Int, Int>,
|
||||
npcs: MutableList<QuestNpcModel>,
|
||||
objects: MutableList<QuestObjectModel>,
|
||||
val byteCodeIr: List<Segment>,
|
||||
getVariant: (Episode, areaId: Int, variantId: Int) -> AreaVariantModel?,
|
||||
) {
|
||||
private val _id = mutableVal(0)
|
||||
|
@ -0,0 +1,178 @@
|
||||
package world.phantasmal.web.questEditor.stores
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import world.phantasmal.lib.assembly.disassemble
|
||||
import world.phantasmal.observable.value.Val
|
||||
import world.phantasmal.observable.value.trueVal
|
||||
import world.phantasmal.web.externals.monacoEditor.*
|
||||
import world.phantasmal.webui.obj
|
||||
import world.phantasmal.webui.stores.Store
|
||||
import kotlin.js.RegExp
|
||||
|
||||
class AssemblyEditorStore(
|
||||
scope: CoroutineScope,
|
||||
questEditorStore: QuestEditorStore,
|
||||
) : Store(scope) {
|
||||
private var _textModel: ITextModel? = null
|
||||
|
||||
val inlineStackArgs: Val<Boolean> = trueVal()
|
||||
|
||||
val textModel: Val<ITextModel?> =
|
||||
questEditorStore.currentQuest.map(inlineStackArgs) { quest, inlineArgs ->
|
||||
_textModel?.dispose()
|
||||
|
||||
_textModel =
|
||||
if (quest == null) null
|
||||
else {
|
||||
val assembly = disassemble(quest.byteCodeIr, inlineArgs)
|
||||
createModel(assembly.joinToString("\n"), ASM_LANG_ID)
|
||||
}
|
||||
|
||||
_textModel
|
||||
}
|
||||
|
||||
val editingEnabled: Val<Boolean> = questEditorStore.questEditingEnabled
|
||||
|
||||
companion object {
|
||||
const val ASM_LANG_ID = "psoasm"
|
||||
|
||||
init {
|
||||
register(obj { id = ASM_LANG_ID })
|
||||
|
||||
setMonarchTokensProvider(ASM_LANG_ID, obj {
|
||||
defaultToken = "invalid"
|
||||
|
||||
tokenizer = obj {
|
||||
this["root"] = arrayOf(
|
||||
// Strings.
|
||||
obj {
|
||||
// Unterminated string.
|
||||
regex = RegExp('"' + """([^"\\]|\.)*$""")
|
||||
action = obj { token = "string.invalid" }
|
||||
},
|
||||
obj {
|
||||
regex = RegExp("\"")
|
||||
action = obj {
|
||||
token = "string.quote"
|
||||
bracket = "@open"
|
||||
next = "@string"
|
||||
}
|
||||
},
|
||||
|
||||
// Registers.
|
||||
obj {
|
||||
regex = RegExp("""r\d+""")
|
||||
action = obj { token = "predefined" }
|
||||
},
|
||||
|
||||
// Labels.
|
||||
obj {
|
||||
regex = RegExp("""[^\s]+:""")
|
||||
action = obj { token = "tag" }
|
||||
},
|
||||
|
||||
// Numbers.
|
||||
obj {
|
||||
regex = RegExp("""0x[0-9a-fA-F]+""")
|
||||
action = obj { token = "number.hex" }
|
||||
},
|
||||
obj {
|
||||
regex = RegExp("""-?\d+(\.\d+)?(e-?\d+)?""")
|
||||
action = obj { token = "number.float" }
|
||||
},
|
||||
obj {
|
||||
regex = RegExp("""-?[0-9]+""")
|
||||
action = obj { token = "number" }
|
||||
},
|
||||
|
||||
// Section markers.
|
||||
obj {
|
||||
regex = RegExp("""\.[^\s]+""")
|
||||
action = obj { token = "keyword" }
|
||||
},
|
||||
|
||||
// Identifiers.
|
||||
obj {
|
||||
regex = RegExp("""[a-z][a-z0-9_=<>!]*""")
|
||||
action = obj { token = "identifier" }
|
||||
},
|
||||
|
||||
// Whitespace.
|
||||
obj {
|
||||
regex = RegExp("""[ \t\r\n]+""")
|
||||
action = obj { token = "white" }
|
||||
},
|
||||
// obj {
|
||||
// regex = RegExp("""\/\*""")
|
||||
// action = obj { token = "comment"; next = "@comment" }
|
||||
// },
|
||||
obj {
|
||||
regex = RegExp("\\/\\/.*$")
|
||||
action = obj { token = "comment" }
|
||||
},
|
||||
|
||||
// Delimiters.
|
||||
obj {
|
||||
regex = RegExp(",")
|
||||
action = obj { token = "delimiter" }
|
||||
},
|
||||
)
|
||||
|
||||
// this["comment"] = arrayOf(
|
||||
// obj {
|
||||
// regex = RegExp("""[^/*]+""")
|
||||
// action = obj { token = "comment" }
|
||||
// },
|
||||
// obj {
|
||||
// // Nested comment.
|
||||
// regex = RegExp("""\/\*""")
|
||||
// action = obj { token = "comment"; next = "@push" }
|
||||
// },
|
||||
// obj {
|
||||
// // Nested comment end.
|
||||
// regex = RegExp("""\*/""")
|
||||
// action = obj { token = "comment"; next = "@pop" }
|
||||
// },
|
||||
// obj {
|
||||
// regex = RegExp("""[/*]""")
|
||||
// action = obj { token = "comment" }
|
||||
// },
|
||||
// )
|
||||
|
||||
this["string"] = arrayOf(
|
||||
obj {
|
||||
regex = RegExp("""[^\\"]+""")
|
||||
action = obj { token = "string" }
|
||||
},
|
||||
obj {
|
||||
regex = RegExp("""\\(?:[n\\"])""")
|
||||
action = obj { token = "string.escape" }
|
||||
},
|
||||
obj {
|
||||
regex = RegExp("""\\.""")
|
||||
action = obj { token = "string.escape.invalid" }
|
||||
},
|
||||
obj {
|
||||
regex = RegExp("\"")
|
||||
action = obj {
|
||||
token = "string.quote"
|
||||
bracket = "@close"
|
||||
next = "@pop"
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
setLanguageConfiguration(ASM_LANG_ID, obj {
|
||||
indentationRules = obj<IndentationRule> {
|
||||
increaseIndentPattern = RegExp("^\\s*\\d+:")
|
||||
decreaseIndentPattern = RegExp("^\\s*(\\d+|\\.)")
|
||||
}
|
||||
autoClosingPairs = arrayOf(obj { open = "\""; close = "\"" })
|
||||
surroundingPairs = arrayOf(obj { open = "\""; close = "\"" })
|
||||
comments = obj<CommentRule> { lineComment = "//" }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ fun convertQuestToModel(
|
||||
// TODO: Add WaveModel to QuestNpcModel
|
||||
quest.npcs.mapTo(mutableListOf()) { QuestNpcModel(it, null) },
|
||||
quest.objects.mapTo(mutableListOf()) { QuestObjectModel(it) },
|
||||
quest.byteCodeIr,
|
||||
getVariant
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
package world.phantasmal.web.questEditor.widgets
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.w3c.dom.Node
|
||||
import world.phantasmal.core.disposable.disposable
|
||||
import world.phantasmal.web.externals.monacoEditor.IStandaloneCodeEditor
|
||||
import world.phantasmal.web.externals.monacoEditor.create
|
||||
import world.phantasmal.web.externals.monacoEditor.defineTheme
|
||||
import world.phantasmal.web.externals.monacoEditor.set
|
||||
import world.phantasmal.web.questEditor.controllers.AssemblyEditorController
|
||||
import world.phantasmal.webui.dom.div
|
||||
import world.phantasmal.webui.obj
|
||||
import world.phantasmal.webui.widgets.Widget
|
||||
|
||||
class AssemblyEditorWidget(
|
||||
scope: CoroutineScope,
|
||||
private val ctrl: AssemblyEditorController,
|
||||
) : Widget(scope) {
|
||||
private lateinit var editor: IStandaloneCodeEditor
|
||||
|
||||
override fun Node.createElement() =
|
||||
div {
|
||||
editor = create(this, obj {
|
||||
theme = "phantasmal-world"
|
||||
scrollBeyondLastLine = false
|
||||
autoIndent = "full"
|
||||
fontSize = 13
|
||||
wordWrap = "on"
|
||||
wrappingIndent = "indent"
|
||||
renderIndentGuides = false
|
||||
folding = false
|
||||
})
|
||||
|
||||
addDisposable(disposable { editor.dispose() })
|
||||
|
||||
observe(ctrl.textModel) { editor.setModel(it) }
|
||||
|
||||
observe(ctrl.readOnly) { editor.updateOptions(obj { readOnly = it }) }
|
||||
|
||||
addDisposable(size.observe { (size) ->
|
||||
editor.layout(obj {
|
||||
width = size.width
|
||||
height = size.height
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
defineTheme("phantasmal-world", obj {
|
||||
base = "vs-dark"
|
||||
inherit = true
|
||||
rules = arrayOf(
|
||||
obj { token = ""; foreground = "E0E0E0"; background = "#181818" },
|
||||
obj { token = "tag"; foreground = "99BBFF" },
|
||||
obj { token = "keyword"; foreground = "D0A0FF"; fontStyle = "bold" },
|
||||
obj { token = "predefined"; foreground = "BBFFBB" },
|
||||
obj { token = "number"; foreground = "FFFFAA" },
|
||||
obj { token = "number.hex"; foreground = "FFFFAA" },
|
||||
obj { token = "string"; foreground = "88FFFF" },
|
||||
obj { token = "string.escape"; foreground = "8888FF" },
|
||||
)
|
||||
colors = obj {
|
||||
this["editor.background"] = "#181818"
|
||||
this["editor.lineHighlightBackground"] = "#202020"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ class QuestEditorWidget(
|
||||
private val createQuestInfoWidget: (CoroutineScope) -> Widget,
|
||||
private val createNpcCountsWidget: (CoroutineScope) -> Widget,
|
||||
private val createQuestRendererWidget: (CoroutineScope) -> Widget,
|
||||
private val createAssemblyEditorWidget: (CoroutineScope) -> Widget,
|
||||
) : Widget(scope) {
|
||||
override fun Node.createElement() =
|
||||
div {
|
||||
@ -60,7 +61,7 @@ class QuestEditorWidget(
|
||||
),
|
||||
)
|
||||
),
|
||||
DockedStack(
|
||||
DockedRow(
|
||||
flex = 9,
|
||||
items = listOf(
|
||||
DockedWidget(
|
||||
@ -71,7 +72,7 @@ class QuestEditorWidget(
|
||||
DockedWidget(
|
||||
title = "Script",
|
||||
id = "asm_editor",
|
||||
createWidget = ::TestWidget
|
||||
createWidget = createAssemblyEditorWidget
|
||||
),
|
||||
)
|
||||
),
|
||||
|
@ -1,5 +1,6 @@
|
||||
package world.phantasmal.web.test
|
||||
|
||||
import world.phantasmal.lib.assembly.Segment
|
||||
import world.phantasmal.lib.fileFormats.quest.Episode
|
||||
import world.phantasmal.lib.fileFormats.quest.NpcType
|
||||
import world.phantasmal.lib.fileFormats.quest.QuestNpc
|
||||
@ -15,6 +16,7 @@ fun createQuestModel(
|
||||
episode: Episode = Episode.I,
|
||||
npcs: List<QuestNpcModel> = emptyList(),
|
||||
objects: List<QuestObjectModel> = emptyList(),
|
||||
byteCodeIr: List<Segment> = emptyList(),
|
||||
): QuestModel =
|
||||
QuestModel(
|
||||
id,
|
||||
@ -26,6 +28,7 @@ fun createQuestModel(
|
||||
emptyMap(),
|
||||
npcs.toMutableList(),
|
||||
objects.toMutableList(),
|
||||
byteCodeIr,
|
||||
) { _, _, _ -> null }
|
||||
|
||||
fun createQuestNpcModel(type: NpcType, episode: Episode): QuestNpcModel =
|
||||
|
4
web/webpack.config.d/webpack.config.js
Normal file
4
web/webpack.config.d/webpack.config.js
Normal file
@ -0,0 +1,4 @@
|
||||
config.module.rules.push({
|
||||
test: /\.(gif|jpg|png|svg|ttf)$/,
|
||||
loader: "file-loader",
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
package world.phantasmal.webui
|
||||
|
||||
fun <T> obj(block: T.() -> Unit): T =
|
||||
inline fun <T> obj(block: T.() -> Unit): T =
|
||||
js("{}").unsafeCast<T>().apply(block)
|
||||
|
@ -0,0 +1,94 @@
|
||||
package world.phantasmal.webui.dom
|
||||
|
||||
import org.w3c.dom.HTMLElement
|
||||
import world.phantasmal.core.disposable.Disposable
|
||||
import world.phantasmal.core.disposable.disposable
|
||||
import world.phantasmal.core.unsafeToNonNull
|
||||
import world.phantasmal.observable.Observer
|
||||
import world.phantasmal.observable.value.AbstractVal
|
||||
|
||||
data class Size(val width: Double, val height: Double)
|
||||
|
||||
class HTMLElementSizeVal(element: HTMLElement? = null) : AbstractVal<Size>() {
|
||||
private var resizeObserver: dynamic = null
|
||||
|
||||
/**
|
||||
* Set to true right before actual observers are added.
|
||||
*/
|
||||
private var hasObservers = false
|
||||
|
||||
private var _value: Size? = null
|
||||
|
||||
var element: HTMLElement? = null
|
||||
set(element) {
|
||||
if (resizeObserver != null) {
|
||||
if (field != null) {
|
||||
resizeObserver.unobserve(field)
|
||||
}
|
||||
|
||||
if (element != null) {
|
||||
resizeObserver.observe(element)
|
||||
}
|
||||
}
|
||||
|
||||
field = element
|
||||
}
|
||||
|
||||
init {
|
||||
// Ensure we call the setter with element.
|
||||
this.element = element
|
||||
}
|
||||
|
||||
override val value: Size
|
||||
get() {
|
||||
if (!hasObservers) {
|
||||
_value = getSize()
|
||||
}
|
||||
|
||||
return _value.unsafeToNonNull()
|
||||
}
|
||||
|
||||
override fun observe(callNow: Boolean, observer: Observer<Size>): Disposable {
|
||||
if (!hasObservers) {
|
||||
hasObservers = true
|
||||
|
||||
if (resizeObserver == null) {
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val resize = ::resizeCallback
|
||||
resizeObserver = js("new ResizeObserver(resize);")
|
||||
}
|
||||
|
||||
if (element != null) {
|
||||
resizeObserver.observe(element)
|
||||
}
|
||||
|
||||
_value = getSize()
|
||||
}
|
||||
|
||||
val superDisposable = super.observe(callNow, observer)
|
||||
|
||||
return disposable {
|
||||
superDisposable.dispose()
|
||||
|
||||
if (observers.isEmpty()) {
|
||||
hasObservers = false
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSize(): Size =
|
||||
element
|
||||
?.let { Size(it.offsetWidth.toDouble(), it.offsetHeight.toDouble()) }
|
||||
?: Size(0.0, 0.0)
|
||||
|
||||
private fun resizeCallback(entries: Array<dynamic>) {
|
||||
entries.forEach { entry ->
|
||||
_value = Size(
|
||||
entry.contentRect.width.unsafeCast<Double>(),
|
||||
entry.contentRect.height.unsafeCast<Double>()
|
||||
)
|
||||
emit()
|
||||
}
|
||||
}
|
||||
}
|
@ -3,12 +3,13 @@ package world.phantasmal.webui.widgets
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.w3c.dom.*
|
||||
import world.phantasmal.core.disposable.disposable
|
||||
import world.phantasmal.observable.Observable
|
||||
import world.phantasmal.observable.value.*
|
||||
import world.phantasmal.observable.value.list.ListVal
|
||||
import world.phantasmal.observable.value.list.ListValChangeEvent
|
||||
import world.phantasmal.webui.DisposableContainer
|
||||
import world.phantasmal.webui.dom.HTMLElementSizeVal
|
||||
import world.phantasmal.webui.dom.Size
|
||||
|
||||
abstract class Widget(
|
||||
protected val scope: CoroutineScope,
|
||||
@ -25,8 +26,7 @@ abstract class Widget(
|
||||
) : DisposableContainer() {
|
||||
private val _ancestorVisible = mutableVal(true)
|
||||
private val _children = mutableListOf<Widget>()
|
||||
private var initResizeObserverRequested = false
|
||||
private var resizeObserverInitialized = false
|
||||
private val _size = HTMLElementSizeVal()
|
||||
|
||||
private val elementDelegate = lazy {
|
||||
val el = document.createDocumentFragment().createElement()
|
||||
@ -54,9 +54,7 @@ abstract class Widget(
|
||||
}
|
||||
}
|
||||
|
||||
if (initResizeObserverRequested) {
|
||||
initResizeObserver(el)
|
||||
}
|
||||
_size.element = el
|
||||
|
||||
interceptElement(el)
|
||||
el
|
||||
@ -77,6 +75,8 @@ abstract class Widget(
|
||||
*/
|
||||
val selfOrAncestorVisible: Val<Boolean> = visible and ancestorVisible
|
||||
|
||||
val size: Val<Size> = _size
|
||||
|
||||
val children: List<Widget> = _children
|
||||
|
||||
open fun focus() {
|
||||
@ -189,40 +189,6 @@ abstract class Widget(
|
||||
spliceChildren(0, 0, list.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever [element] is resized.
|
||||
* Must be initialized with [observeResize].
|
||||
*/
|
||||
protected open fun resized(width: Double, height: Double) {}
|
||||
|
||||
protected fun observeResize() {
|
||||
if (elementDelegate.isInitialized()) {
|
||||
initResizeObserver(element)
|
||||
} else {
|
||||
initResizeObserverRequested = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun initResizeObserver(element: Element) {
|
||||
if (resizeObserverInitialized) return
|
||||
|
||||
resizeObserverInitialized = true
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val resize = ::resizeCallback
|
||||
val observer = js("new ResizeObserver(resize);")
|
||||
observer.observe(element)
|
||||
addDisposable(disposable { observer.disconnect().unsafeCast<Unit>() })
|
||||
}
|
||||
|
||||
private fun resizeCallback(entries: Array<dynamic>) {
|
||||
entries.forEach { entry ->
|
||||
resized(
|
||||
entry.contentRect.width.unsafeCast<Double>(),
|
||||
entry.contentRect.height.unsafeCast<Double>()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val STYLE_EL by lazy {
|
||||
val el = document.createElement("style") as HTMLStyleElement
|
||||
|
Loading…
Reference in New Issue
Block a user