firefly-compiler 0.6.11 → 0.6.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/Release.ff +1 -1
- package/lux/Lux.ff +181 -115
- package/package.json +1 -1
- package/vscode/package.json +1 -1
package/bin/Release.ff
CHANGED
|
@@ -16,7 +16,7 @@ release(
|
|
|
16
16
|
system.writeErrorLine("You need to be in the directory of Release.ff")
|
|
17
17
|
system.exit(1)
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
requireNpmLoggedIn(system, system.path(".."))
|
|
20
20
|
requireVsceLoggedIn(system, system.path("../vscode"))
|
|
21
21
|
runSuccessful(system, "node", ["output/js/ff/compiler/Main.run.mjs", "bootstrap"], system.path(".."))
|
|
22
22
|
runSuccessful(system, "node", ["output/js/ff/compiler/Main.run.mjs", "bootstrap"], system.path(".."))
|
package/lux/Lux.ff
CHANGED
|
@@ -169,7 +169,7 @@ extend self: Lux {
|
|
|
169
169
|
} finally {
|
|
170
170
|
patchText(self)
|
|
171
171
|
if(!self.element.keepChildren) {
|
|
172
|
-
doWhile {removeCurrentChild(self)}
|
|
172
|
+
doWhile {removeCurrentChild(self, self.depth)}
|
|
173
173
|
}
|
|
174
174
|
patchAttributes(self)
|
|
175
175
|
self.depth -= 1
|
|
@@ -268,10 +268,24 @@ extend self: Lux {
|
|
|
268
268
|
onClick(handler: LuxEvent => Unit) {self.on("click", handler)}
|
|
269
269
|
onInput(handler: LuxEvent => Unit) {self.on("input", handler)}
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
self.
|
|
271
|
+
useHook[T](body: LuxHook[T] => Unit) {
|
|
272
|
+
// if(!self.canHook) {throw(LuxHookException)}
|
|
273
|
+
// self.canHook = False
|
|
273
274
|
self.depth += 1
|
|
274
|
-
|
|
275
|
+
try {
|
|
276
|
+
if(self.dry.isEmpty()) {
|
|
277
|
+
self.element.element.set("_lux$C" + self.depth, {}!)
|
|
278
|
+
}
|
|
279
|
+
body(LuxHook(self.Lux(element = self.element.LuxElement())))
|
|
280
|
+
} finally {
|
|
281
|
+
self.depth -= 1
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
useState[T: HasAnyTag](initialValue: T, body: (T, T => Unit) => Unit) {
|
|
286
|
+
self.useHook: hook =>
|
|
287
|
+
if(hook.dry()) {body(initialValue, {_ => })} else:
|
|
288
|
+
let value = if(hook.hasValue()) {hook.grabValue()} else {initialValue}
|
|
275
289
|
mutable i = 0
|
|
276
290
|
while {i < self.renderQueue.size()} {
|
|
277
291
|
let item = self.renderQueue.grab(i)
|
|
@@ -281,109 +295,79 @@ extend self: Lux {
|
|
|
281
295
|
i += 1
|
|
282
296
|
}
|
|
283
297
|
}
|
|
284
|
-
let luxCopy = self.Lux(element = self.element.LuxElement())
|
|
285
298
|
function setState(newValue: T): Unit {
|
|
286
|
-
|
|
299
|
+
hook.setValue(newValue)
|
|
287
300
|
self.renderQueue.push(RenderQueueItem(
|
|
288
|
-
luxCopy = luxCopy
|
|
301
|
+
luxCopy = hook.luxCopy
|
|
289
302
|
render = {
|
|
290
|
-
let child = luxCopy.element.child
|
|
291
|
-
let element = luxCopy.element.childAt(child)
|
|
292
303
|
body(newValue, {setState(_)})
|
|
293
|
-
if(!element.equals(luxCopy.element.childAt(child))) {
|
|
294
|
-
removeCurrentChild(luxCopy.Lux(
|
|
295
|
-
element = luxCopy.element.LuxElement(child = luxCopy.element.child + 1)
|
|
296
|
-
))
|
|
297
|
-
}
|
|
298
304
|
}
|
|
299
305
|
))
|
|
300
306
|
}
|
|
301
|
-
|
|
302
|
-
body(value, {setState(_)})
|
|
303
|
-
} finally {
|
|
304
|
-
self.depth -= 1
|
|
305
|
-
}
|
|
307
|
+
body(value, {setState(_)})
|
|
306
308
|
}
|
|
307
309
|
|
|
308
|
-
useCallback1[A1
|
|
309
|
-
self.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
body {a1 =>
|
|
316
|
-
let latestCallback = getStateOnElement(element, depth, callback)
|
|
310
|
+
useCallback1[A1](callback: A1 => Unit, body: (A1 => Unit) => Unit) {
|
|
311
|
+
self.useHook: hook =>
|
|
312
|
+
if(hook.dry()) {body(callback)} else:
|
|
313
|
+
hook.setValue(callback)
|
|
314
|
+
body {a1 =>
|
|
315
|
+
let latestCallback = hook.grabValue()
|
|
316
|
+
self.renderLock.do {
|
|
317
317
|
latestCallback(a1)
|
|
318
|
+
processRenderQueue(self)
|
|
318
319
|
}
|
|
319
|
-
} finally {
|
|
320
|
-
self.depth -= 1
|
|
321
320
|
}
|
|
322
321
|
}
|
|
323
322
|
|
|
324
323
|
useLazy1[A1: Equal: HasAnyTag](a1: A1, body: A1 => Unit = {_ => }) {
|
|
325
|
-
self.
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
| _ =>
|
|
333
|
-
setStateOnElement(self.element.element, self.depth, Some(a1))
|
|
334
|
-
body(a1)
|
|
335
|
-
}
|
|
336
|
-
} finally {
|
|
337
|
-
self.depth -= 1
|
|
324
|
+
self.useHook: hook =>
|
|
325
|
+
if(hook.dry()) {body(a1)} else:
|
|
326
|
+
if(!hook.hasValue() || hook.grabValue() != a1) {
|
|
327
|
+
hook.setValue(a1)
|
|
328
|
+
body(a1)
|
|
329
|
+
} else {
|
|
330
|
+
self.element.keepChildren = True
|
|
338
331
|
}
|
|
339
332
|
}
|
|
340
333
|
|
|
341
334
|
useMemo1[A1: Equal: HasAnyTag, T: HasAnyTag](a1: A1, compute: A1 => T, body: T => Unit) {
|
|
342
|
-
self.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
let
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
v
|
|
353
|
-
| Pair(_, None) =>
|
|
354
|
-
let v = compute(a1)
|
|
355
|
-
setStateOnElement(self.element.element, self.depth, Pair(a1, Some(v)))
|
|
356
|
-
v
|
|
335
|
+
self.useHook: hook =>
|
|
336
|
+
if(hook.dry()) {body(compute(a1))} else:
|
|
337
|
+
if(hook.hasValue()) {
|
|
338
|
+
let pair: Pair[A1, T] = hook.grabValue()
|
|
339
|
+
if(pair.first == a1) {
|
|
340
|
+
body(pair.second)
|
|
341
|
+
} else {
|
|
342
|
+
let value = compute(a1)
|
|
343
|
+
hook.setValue(Pair(a1, value))
|
|
344
|
+
body(value)
|
|
357
345
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
346
|
+
} else {
|
|
347
|
+
let value = compute(a1)
|
|
348
|
+
hook.setValue(Pair(a1, value))
|
|
349
|
+
body(value)
|
|
361
350
|
}
|
|
362
351
|
}
|
|
363
|
-
|
|
352
|
+
|
|
364
353
|
useSuspense(suspense: () => Unit, body: Lux => Unit) {
|
|
365
|
-
self.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
let luxCopy = self.Lux(element = self.element.LuxElement())
|
|
372
|
-
let subtask = self.task.spawn {task =>
|
|
373
|
-
let lux = luxCopy.Lux(
|
|
354
|
+
self.useHook: hook =>
|
|
355
|
+
if(hook.dry()) {suspense()} else:
|
|
356
|
+
hook.cleanup()
|
|
357
|
+
let fragment = hook.luxCopy.document.createFragment()
|
|
358
|
+
let subtask = hook.luxCopy.task.spawn {task =>
|
|
359
|
+
let lux = hook.luxCopy.Lux(
|
|
374
360
|
task = task
|
|
375
361
|
renderLock = task.lock()
|
|
376
362
|
renderQueue = Array.new()
|
|
377
|
-
element = luxCopy.element.LuxElement(element = fragment)
|
|
363
|
+
element = hook.luxCopy.element.LuxElement(element = fragment)
|
|
378
364
|
)
|
|
379
365
|
try {
|
|
380
366
|
body(lux)
|
|
381
367
|
task.throwIfAborted()
|
|
382
|
-
lux.copyFrom(luxCopy)
|
|
368
|
+
lux.copyFrom(hook.luxCopy)
|
|
383
369
|
lux.renderLock.do {
|
|
384
|
-
|
|
385
|
-
lux.element.removeAt(lux.element.child)
|
|
386
|
-
lux.element.insertBefore(fragment, lux.jsSystem.null())
|
|
370
|
+
replaceWithFragment(lux.element.element, fragment, lux.depth)
|
|
387
371
|
}
|
|
388
372
|
} catchAny {error =>
|
|
389
373
|
if(error.name() != "AbortError") {
|
|
@@ -391,10 +375,128 @@ extend self: Lux {
|
|
|
391
375
|
}
|
|
392
376
|
}
|
|
393
377
|
}
|
|
378
|
+
hook.setCleanup {subtask.abort()}
|
|
394
379
|
suspense()
|
|
395
|
-
setTaskOnElement(self.element.childAt(self.element.child - 1), Some(subtask))
|
|
396
380
|
}
|
|
397
381
|
|
|
382
|
+
useWebSocket(
|
|
383
|
+
url: String
|
|
384
|
+
onMessage: Buffer => Unit
|
|
385
|
+
body: (Buffer => Unit) => Unit
|
|
386
|
+
) {
|
|
387
|
+
self.useHook: hook =>
|
|
388
|
+
if(hook.dry()) {} else:
|
|
389
|
+
self.useCallback1 {buffer =>
|
|
390
|
+
onMessage(buffer)
|
|
391
|
+
}: onMessage =>
|
|
392
|
+
function createWebSocket(): JsValue {
|
|
393
|
+
let socket = Js->WebSocket->(url)
|
|
394
|
+
hook.setValue(Pair(socket, url))
|
|
395
|
+
hook.setCleanup {
|
|
396
|
+
socket->close()
|
|
397
|
+
}
|
|
398
|
+
socket->binaryType = "arraybuffer"
|
|
399
|
+
Js.awaitCancellablePromise {resolve, reject, onSettle =>
|
|
400
|
+
socket->onopen = Js->{resolve(Unit)}
|
|
401
|
+
socket->onclose = Js->{reject(_?)}
|
|
402
|
+
}
|
|
403
|
+
socket->onopen = Js.null()
|
|
404
|
+
socket->onclose = Js.null()
|
|
405
|
+
socket->addEventListener("close", Js->{
|
|
406
|
+
Log.debug("Closed")
|
|
407
|
+
})
|
|
408
|
+
socket->addEventListener("message", Js->{m =>
|
|
409
|
+
let buffer = Js->DataView->(m->data).grabBuffer() // TODO: Handle text
|
|
410
|
+
onMessage(buffer)
|
|
411
|
+
})
|
|
412
|
+
socket->addEventListener("error", Js->{e =>
|
|
413
|
+
Log.debug("Error")
|
|
414
|
+
})
|
|
415
|
+
socket
|
|
416
|
+
}
|
|
417
|
+
let socket = if(!hook.hasValue()) {
|
|
418
|
+
createWebSocket()
|
|
419
|
+
} else {
|
|
420
|
+
let pair = hook.grabValue()
|
|
421
|
+
let oldSocket: JsValue = pair.first
|
|
422
|
+
let oldUrl: String = pair.second
|
|
423
|
+
if(oldUrl == url) {
|
|
424
|
+
oldSocket
|
|
425
|
+
} else {
|
|
426
|
+
hook.cleanup()
|
|
427
|
+
createWebSocket()
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
let send: Buffer => Unit = {buffer => socket->send(buffer)}
|
|
431
|
+
body(send)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
capability LuxHook[T](luxCopy: Lux)
|
|
437
|
+
|
|
438
|
+
extend self[T]: LuxHook[T] {
|
|
439
|
+
dry(): Bool {
|
|
440
|
+
!self.luxCopy.dry.isEmpty()
|
|
441
|
+
}
|
|
442
|
+
setValue(value: T) {
|
|
443
|
+
self.luxCopy.element.element.set("_lux$S" + self.luxCopy.depth, value!)
|
|
444
|
+
}
|
|
445
|
+
hasValue(): Bool {
|
|
446
|
+
self.luxCopy.element.element.hasOwn("_lux$S" + self.luxCopy.depth)
|
|
447
|
+
}
|
|
448
|
+
grabValue(): T {
|
|
449
|
+
let value = self.luxCopy.element.element.get("_lux$S" + self.luxCopy.depth)
|
|
450
|
+
if(value.isUndefined() && !self.hasValue()) {
|
|
451
|
+
throw(GrabException)
|
|
452
|
+
}
|
|
453
|
+
value? // If value is an async function, will the effect type system see through this?
|
|
454
|
+
}
|
|
455
|
+
setCleanup(body: () => Unit) {
|
|
456
|
+
self.luxCopy.element.element.set("_lux$C" + self.luxCopy.depth, body!)
|
|
457
|
+
}
|
|
458
|
+
cleanup() {
|
|
459
|
+
let key = "_lux$C" + self.luxCopy.depth
|
|
460
|
+
let cleanupFunction = self.luxCopy.element.element.get(key)
|
|
461
|
+
self.luxCopy.element.element.set(key, {}!)
|
|
462
|
+
cleanupFunction?() // Async cleanup is not waited for. Is that ok?
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
cleanupElement(element: JsValue, depth: Int) {
|
|
467
|
+
mutable d = depth
|
|
468
|
+
doWhile {
|
|
469
|
+
let cleanupFunction = element.get("_lux$C" + d)
|
|
470
|
+
if(cleanupFunction.isUndefined()) {
|
|
471
|
+
False
|
|
472
|
+
} else {
|
|
473
|
+
Js->Reflect->deleteProperty(element, "_lux$C" + d)
|
|
474
|
+
Js->Reflect->deleteProperty(element, "_lux$S" + d)
|
|
475
|
+
cleanupFunction?() // Async cleanup is not waited for. Is that ok?
|
|
476
|
+
d += 1
|
|
477
|
+
True
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
element->children.each {child =>
|
|
481
|
+
cleanupElement(child, d + 1)
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
replaceWithFragment(element: JsValue, fragment: JsValue, depth: Int) {
|
|
486
|
+
cleanupElement(element, depth) // Assumes that hooks have no siblings
|
|
487
|
+
mutable d = depth
|
|
488
|
+
doWhile {
|
|
489
|
+
let cleanupFunction = fragment.get("_lux$C" + d)
|
|
490
|
+
if(cleanupFunction.isUndefined()) {
|
|
491
|
+
False
|
|
492
|
+
} else {
|
|
493
|
+
element.set("_lux$C" + d, cleanupFunction)
|
|
494
|
+
if(fragment.hasOwn("_lux$S" + d)) {element.set("_lux$S" + d, fragment.get("_lux$S" + d))}
|
|
495
|
+
d += 1
|
|
496
|
+
True
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
element->replaceChildren(fragment)
|
|
398
500
|
}
|
|
399
501
|
|
|
400
502
|
processRenderQueue(self: Lux) {
|
|
@@ -411,50 +513,14 @@ processRenderQueue(self: Lux) {
|
|
|
411
513
|
}
|
|
412
514
|
}
|
|
413
515
|
|
|
414
|
-
removeCurrentChild(self: Lux): Bool {
|
|
516
|
+
removeCurrentChild(self: Lux, depth: Int): Bool {
|
|
415
517
|
let child = self.element.childAt(self.element.child)
|
|
416
518
|
if(!child.isNullOrUndefined() && !child->children.isNullOrUndefined()) {
|
|
417
|
-
|
|
519
|
+
cleanupElement(child, depth)
|
|
418
520
|
}
|
|
419
521
|
self.element.removeAt(self.element.child)
|
|
420
522
|
}
|
|
421
523
|
|
|
422
|
-
getStateOnElement[T /*: HasAnyTag*/](element: JsValue, depth: Int, fallback: T): T {
|
|
423
|
-
let value = element.get("lux" + depth)
|
|
424
|
-
if(value.isNullOrUndefined()) {fallback} else {
|
|
425
|
-
value?
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
setStateOnElement[T /*: HasAnyTag*/](element: JsValue, depth: Int, value: T): Unit {
|
|
430
|
-
element.set("lux" + depth, value!)
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
getTaskOnElement(element: JsValue): Option[Task] {
|
|
435
|
-
let value = element->luxTask
|
|
436
|
-
if(value.isNullOrUndefined()) {None} else {
|
|
437
|
-
value?
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
setTaskOnElement(element: JsValue, task: Option[Task]): Unit {
|
|
442
|
-
element->luxTask = task!
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
abortTasksOnElement(lux: Lux, element: JsValue, onlyChildren: Bool): Unit {
|
|
446
|
-
if(!onlyChildren) {
|
|
447
|
-
getTaskOnElement(element).each {task => forceAsyncAbort(lux, task)}
|
|
448
|
-
}
|
|
449
|
-
element->children.each {child =>
|
|
450
|
-
abortTasksOnElement(lux, child, False)
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
forceAsyncAbort(self: Lux, task: Task): Unit {
|
|
455
|
-
task.abort()
|
|
456
|
-
}
|
|
457
|
-
|
|
458
524
|
patchText(self: Lux) {
|
|
459
525
|
if(!self.texts.isEmpty()):
|
|
460
526
|
let value = self.texts.drain().join()
|
package/package.json
CHANGED