Compare commits
No commits in common. "8b94432f66058dc7d6114e4bd71a7bc0a3ff3b69" and "5dc2159024a2cc1aa3c7cd9c40e07a4175a628ce" have entirely different histories.
8b94432f66
...
5dc2159024
@ -94,7 +94,7 @@ m += "c" => 3
|
|||||||
val [first, middle..., last] = [1, 2, 3, 4, 5]
|
val [first, middle..., last] = [1, 2, 3, 4, 5]
|
||||||
|
|
||||||
// Safe Navigation and Elvis
|
// Safe Navigation and Elvis
|
||||||
val companyName = person?.job?.company?.name ?: "Freelancer"
|
val companyName = person?.job?.company?.name ?? "Freelancer"
|
||||||
```
|
```
|
||||||
|
|
||||||
## 8. Standard Library Discovery
|
## 8. Standard Library Discovery
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#!/bin/env lyng
|
#!/bin/env lyng
|
||||||
|
|
||||||
import lyng.io.fs
|
import lyng.io.fs
|
||||||
|
import lyng.stdlib
|
||||||
|
|
||||||
val files = Path("../..").list().toList()
|
val files = Path("../..").list().toList()
|
||||||
// most long is longest?
|
// most long is longest?
|
||||||
|
|||||||
12
docs/time.md
12
docs/time.md
@ -21,11 +21,7 @@ Represent some moment of time not depending on the calendar. It is similar to `T
|
|||||||
val t2 = Instant(1704110400) // 2024-01-01T12:00:00Z
|
val t2 = Instant(1704110400) // 2024-01-01T12:00:00Z
|
||||||
|
|
||||||
// from RFC3339 string:
|
// from RFC3339 string:
|
||||||
val t3 = Instant("2024-01-01T12:00:00.123456Z")
|
val t3 = Instant("2024-01-01T12:00:00Z")
|
||||||
|
|
||||||
// truncation:
|
|
||||||
val t4 = t3.truncateToMinute
|
|
||||||
assertEquals(t4.toRFC3339(), "2024-01-01T12:00:00Z")
|
|
||||||
|
|
||||||
// to localized DateTime (uses system default TZ if not specified):
|
// to localized DateTime (uses system default TZ if not specified):
|
||||||
val dt = t3.toDateTime("+02:00")
|
val dt = t3.toDateTime("+02:00")
|
||||||
@ -40,7 +36,6 @@ Represent some moment of time not depending on the calendar. It is similar to `T
|
|||||||
| nanosecondsOfSecond: Int | offset from epochWholeSeconds in nanos |
|
| nanosecondsOfSecond: Int | offset from epochWholeSeconds in nanos |
|
||||||
| isDistantFuture: Bool | true if it `Instant.distantFuture` |
|
| isDistantFuture: Bool | true if it `Instant.distantFuture` |
|
||||||
| isDistantPast: Bool | true if it `Instant.distantPast` |
|
| isDistantPast: Bool | true if it `Instant.distantPast` |
|
||||||
| truncateToMinute: Instant | create new instance truncated to minute |
|
|
||||||
| truncateToSecond: Instant | create new instance truncated to second |
|
| truncateToSecond: Instant | create new instance truncated to second |
|
||||||
| truncateToMillisecond: Instant | truncate new instance to millisecond |
|
| truncateToMillisecond: Instant | truncate new instance to millisecond |
|
||||||
| truncateToMicrosecond: Instant | truncate new instance to microsecond |
|
| truncateToMicrosecond: Instant | truncate new instance to microsecond |
|
||||||
@ -49,8 +44,7 @@ Represent some moment of time not depending on the calendar. It is similar to `T
|
|||||||
|
|
||||||
## Calendar time: `DateTime`
|
## Calendar time: `DateTime`
|
||||||
|
|
||||||
`DateTime` represents a point in time in a specific timezone. It provides access to calendar components like year,
|
`DateTime` represents a point in time in a specific timezone. It provides access to calendar components like year, month, and day.
|
||||||
month, and day.
|
|
||||||
|
|
||||||
### Constructing
|
### Constructing
|
||||||
|
|
||||||
@ -74,7 +68,7 @@ month, and day.
|
|||||||
### DateTime members
|
### DateTime members
|
||||||
|
|
||||||
| member | description |
|
| member | description |
|
||||||
|----------------------------------|-----------------------------------------------|
|
|-------------------|------------------------------------------------------|
|
||||||
| year: Int | year component |
|
| year: Int | year component |
|
||||||
| month: Int | month component (1..12) |
|
| month: Int | month component (1..12) |
|
||||||
| day: Int | day of month (alias `dayOfMonth`) |
|
| day: Int | day of month (alias `dayOfMonth`) |
|
||||||
|
|||||||
@ -17,7 +17,10 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import kotlinx.datetime.*
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
import kotlinx.datetime.UtcOffset
|
||||||
|
import kotlinx.datetime.asTimeZone
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
@ -190,18 +193,6 @@ class ObjInstant(val instant: Instant,val truncateMode: LynonSettings.InstantTru
|
|||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjInt(thisAs<ObjInstant>().instant.nanosecondsOfSecond.toLong()) }
|
getter = { ObjInt(thisAs<ObjInstant>().instant.nanosecondsOfSecond.toLong()) }
|
||||||
)
|
)
|
||||||
addFnDoc(
|
|
||||||
name = "truncateToMinute",
|
|
||||||
doc = "Truncate this instant to the nearest minute.",
|
|
||||||
returns = type("lyng.Instant"),
|
|
||||||
moduleName = "lyng.time"
|
|
||||||
) {
|
|
||||||
val t = thisAs<ObjInstant>().instant
|
|
||||||
val tz = TimeZone.UTC
|
|
||||||
val dt = t.toLocalDateTime(tz)
|
|
||||||
val truncated = LocalDateTime(dt.year, dt.month, dt.dayOfMonth, dt.hour, dt.minute, 0, 0)
|
|
||||||
ObjInstant(truncated.toInstant(tz), LynonSettings.InstantTruncateMode.Second)
|
|
||||||
}
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "truncateToSecond",
|
name = "truncateToSecond",
|
||||||
doc = "Truncate this instant to the nearest second.",
|
doc = "Truncate this instant to the nearest second.",
|
||||||
|
|||||||
@ -868,39 +868,4 @@ class OOTest {
|
|||||||
assertEquals(44L, fn.invoke(scope, fn).toKotlin(s2))
|
assertEquals(44L, fn.invoke(scope, fn).toKotlin(s2))
|
||||||
assertEquals(45L, fn.invoke(s2, fn).toKotlin(s2))
|
assertEquals(45L, fn.invoke(s2, fn).toKotlin(s2))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testToStringWithTransients() = runTest {
|
|
||||||
eval("""
|
|
||||||
class C(amount,@Transient transient=0) {
|
|
||||||
val l by lazy { transient + amount }
|
|
||||||
fun lock() {
|
|
||||||
if( transient < 10 )
|
|
||||||
C(amount).also { it.transient = transient + 10 }
|
|
||||||
else
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println(C(1))
|
|
||||||
println(C(1).lock().amount)
|
|
||||||
println(C(1).lock().lock().amount)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
@Test
|
|
||||||
fun testToJsonString() = runTest {
|
|
||||||
eval("""
|
|
||||||
class C(amount,@Transient transient=0) {
|
|
||||||
val l by lazy { transient + amount }
|
|
||||||
fun lock() {
|
|
||||||
if( transient < 10 )
|
|
||||||
C(amount).also { it.transient = transient + 10 }
|
|
||||||
else
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println(C(1))
|
|
||||||
println(C(1).lock().amount)
|
|
||||||
println(C(1).lock().lock().amount)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -3169,19 +3169,6 @@ class ScriptTest {
|
|||||||
assertEquals( n1 - now, 7.seconds )
|
assertEquals( n1 - now, 7.seconds )
|
||||||
assertEquals( now - n1, -7.seconds )
|
assertEquals( now - n1, -7.seconds )
|
||||||
|
|
||||||
val t3 = Instant("2024-01-01T12:00:00.123456Z")
|
|
||||||
val t4 = t3.truncateToMinute
|
|
||||||
assertEquals(t4.toRFC3339(), "2024-01-01T12:00:00Z")
|
|
||||||
assertEquals(
|
|
||||||
"2024-01-01T12:00:00Z",
|
|
||||||
Instant("2024-01-01T12:00:59.999Z").truncateToMinute().toRFC3339()
|
|
||||||
)
|
|
||||||
|
|
||||||
val t5 = t3.truncateToSecond
|
|
||||||
assertEquals(t5.toRFC3339(), "2024-01-01T12:00:00Z")
|
|
||||||
|
|
||||||
val t6 = t3.truncateToMillisecond
|
|
||||||
assertEquals(t6.toRFC3339(), "2024-01-01T12:00:00.123Z")
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
delay(1000)
|
delay(1000)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -118,7 +118,7 @@ fun App() {
|
|||||||
Div({ classes("col-12", if (isDocsRoute) "col-lg-9" else "col-lg-12") }) {
|
Div({ classes("col-12", if (isDocsRoute) "col-lg-9" else "col-lg-12") }) {
|
||||||
when {
|
when {
|
||||||
route.isBlank() -> HomePage()
|
route.isBlank() -> HomePage()
|
||||||
route.startsWith("tryling") -> TryLyngPage(route)
|
route == "tryling" -> TryLyngPage()
|
||||||
route.startsWith("search") -> SearchPage(route)
|
route.startsWith("search") -> SearchPage(route)
|
||||||
!isDocsRoute -> ReferencePage()
|
!isDocsRoute -> ReferencePage()
|
||||||
else -> DocsPage(
|
else -> DocsPage(
|
||||||
|
|||||||
@ -15,9 +15,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.Composable
|
||||||
import kotlinx.browser.window
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import net.sergeych.lyngweb.ensureBootstrapCodeBlocks
|
import net.sergeych.lyngweb.ensureBootstrapCodeBlocks
|
||||||
import net.sergeych.lyngweb.highlightLyngHtml
|
import net.sergeych.lyngweb.highlightLyngHtml
|
||||||
import net.sergeych.lyngweb.htmlEscape
|
import net.sergeych.lyngweb.htmlEscape
|
||||||
@ -25,141 +23,6 @@ import org.jetbrains.compose.web.dom.*
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomePage() {
|
fun HomePage() {
|
||||||
val samples = remember {
|
|
||||||
listOf(
|
|
||||||
"""
|
|
||||||
// Everything is an expression
|
|
||||||
val x = 10
|
|
||||||
val status = if (x > 0) "Positive" else "Zero or Negative"
|
|
||||||
|
|
||||||
// Even loops return values!
|
|
||||||
val result = for (i in 1..5) {
|
|
||||||
if (i == 3) break "Found 3!"
|
|
||||||
} else "Not found"
|
|
||||||
|
|
||||||
println("Result: " + result)
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Functional power with ranges and collections
|
|
||||||
val squares = (1..10)
|
|
||||||
.filter { it % 2 == 0 }
|
|
||||||
.map { it * it }
|
|
||||||
|
|
||||||
println("Even squares: " + squares)
|
|
||||||
// Output: [4, 16, 36, 64, 100]
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Flexible map literals and shorthands
|
|
||||||
val id = 101
|
|
||||||
val name = "Lyng"
|
|
||||||
val base = { id:, name: } // Shorthand for id: id, name: name
|
|
||||||
|
|
||||||
val full = { ...base, version: "1.0", status: "active" }
|
|
||||||
println(full)
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Modern null safety
|
|
||||||
var config = null
|
|
||||||
config ?= { timeout: 30 } // Assign only if null
|
|
||||||
|
|
||||||
val timeout = config?.timeout ?: 60
|
|
||||||
println("Timeout is: " + timeout)
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Destructuring with splat operator
|
|
||||||
val [first, middle..., last] = [1, 2, 3, 4, 5, 6]
|
|
||||||
|
|
||||||
println("First: " + first)
|
|
||||||
println("Middle: " + middle)
|
|
||||||
println("Last: " + last)
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Diamond-safe Multiple Inheritance (C3 MRO)
|
|
||||||
interface Logger {
|
|
||||||
fun log(m) = println("[LOG] " + m)
|
|
||||||
}
|
|
||||||
interface Auth {
|
|
||||||
fun login(u) = println("Login: " + u)
|
|
||||||
}
|
|
||||||
|
|
||||||
class App() : Logger, Auth {
|
|
||||||
fun run() {
|
|
||||||
log("Starting...")
|
|
||||||
login("admin")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
App().run()
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Extension functions and properties
|
|
||||||
fun String.shout() = this.toUpperCase() + "!!!"
|
|
||||||
|
|
||||||
println("hello".shout())
|
|
||||||
|
|
||||||
val List.second get = this[1]
|
|
||||||
println([10, 20, 30].second)
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Non-local returns from closures
|
|
||||||
fun findFirst(list, predicate) {
|
|
||||||
list.forEach {
|
|
||||||
if (predicate(it)) return@findFirst it
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val found = findFirst([1, 5, 8, 12]) { it > 10 }
|
|
||||||
println("Found: " + found)
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Easy operator overloading
|
|
||||||
class Vector(val x, val y) {
|
|
||||||
override fun plus(other) = Vector(x + other.x, y + other.y)
|
|
||||||
override fun toString() = "Vector(%g, %g)"(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
val v1 = Vector(1, 2)
|
|
||||||
val v2 = Vector(3, 4)
|
|
||||||
println(v1 + v2)
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Property delegation to Map
|
|
||||||
class User() {
|
|
||||||
var name by Map()
|
|
||||||
}
|
|
||||||
|
|
||||||
val u = User()
|
|
||||||
u.name = "Sergey"
|
|
||||||
println("User name: " + u.name)
|
|
||||||
""".trimIndent(),
|
|
||||||
"""
|
|
||||||
// Implicit coroutines: parallelism without ceremony
|
|
||||||
import lyng.time
|
|
||||||
|
|
||||||
val d1 = launch {
|
|
||||||
delay(100.milliseconds)
|
|
||||||
"Task A finished"
|
|
||||||
}
|
|
||||||
val d2 = launch {
|
|
||||||
delay(50.milliseconds)
|
|
||||||
"Task B finished"
|
|
||||||
}
|
|
||||||
|
|
||||||
println(d1.await())
|
|
||||||
println(d2.await())
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentSlide by remember { mutableStateOf((samples.indices).random()) }
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
ensureSlideshowStyles()
|
|
||||||
while (true) {
|
|
||||||
delay(7000)
|
|
||||||
currentSlide = (currentSlide + 1) % samples.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Hero section
|
// Hero section
|
||||||
Section({ classes("py-4", "py-lg-5") }) {
|
Section({ classes("py-4", "py-lg-5") }) {
|
||||||
Div({ classes("text-center") }) {
|
Div({ classes("text-center") }) {
|
||||||
@ -181,7 +44,7 @@ fun HomePage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CTA buttons
|
// CTA buttons
|
||||||
Div({ classes("d-flex", "justify-content-center", "gap-2", "mb-2") }) {
|
Div({ classes("d-flex", "justify-content-center", "gap-2", "mb-4") }) {
|
||||||
A(attrs = {
|
A(attrs = {
|
||||||
classes("btn", "btn-primary", "btn-lg")
|
classes("btn", "btn-primary", "btn-lg")
|
||||||
attr("href", "#/docs/tutorial.md")
|
attr("href", "#/docs/tutorial.md")
|
||||||
@ -209,26 +72,48 @@ fun HomePage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code sample slideshow
|
// Code sample
|
||||||
Div({
|
val code = """
|
||||||
classes("markdown-body", "mt-0", "slide-container", "position-relative")
|
// Create, transform, and verify — the Lyng way
|
||||||
onClick {
|
import lyng.stdlib
|
||||||
window.location.hash = "#tryling?code=" + encodeURIComponent(samples[currentSlide])
|
|
||||||
|
fun findFirstPositive(list) {
|
||||||
|
list.forEach {
|
||||||
|
if (it > 0) return@findFirstPositive it
|
||||||
}
|
}
|
||||||
}) {
|
null
|
||||||
Small({
|
}
|
||||||
classes("position-absolute", "top-0", "end-0", "m-2", "text-muted", "fw-light", "try-hint")
|
assertEquals(42, findFirstPositive([-1, 42, -5]))
|
||||||
}) {
|
|
||||||
Text("click to try")
|
val data = 1..5 // or [1,2,3,4,5]
|
||||||
}
|
val evens2 = data.filter { it % 2 == 0 }.map { it * it }
|
||||||
key(currentSlide) {
|
assertEquals([4, 16], evens2)
|
||||||
val slideCode = samples[currentSlide]
|
|
||||||
val mapHtml = "<pre><code>" + htmlEscape(slideCode) + "</code></pre>"
|
// Map literal with identifier keys, shorthand, and spread
|
||||||
Div({ classes("slide-animation") }) {
|
val base = { a: 1, b: 2 }
|
||||||
|
val patch = { b: 3, c: }
|
||||||
|
val m = { "a": 0, ...base, ...patch, d: 4 }
|
||||||
|
assertEquals(1, m["a"]) // base overwrites 0
|
||||||
|
|
||||||
|
// Object expressions: anonymous classes on the fly
|
||||||
|
val worker = object : Runnable {
|
||||||
|
override fun run() = println("Working...")
|
||||||
|
}
|
||||||
|
worker.run()
|
||||||
|
|
||||||
|
// User-defined exceptions: real classes with custom fields
|
||||||
|
class MyError(val code, m) : Exception(m)
|
||||||
|
try {
|
||||||
|
throw MyError(500, "Something failed")
|
||||||
|
} catch (e: MyError) {
|
||||||
|
println("Error " + e.code + ": " + e.message)
|
||||||
|
}
|
||||||
|
>>> void
|
||||||
|
""".trimIndent()
|
||||||
|
val mapHtml = "<pre><code>" + htmlEscape(code) + "</code></pre>"
|
||||||
|
Div({ classes("markdown-body", "mt-3") }) {
|
||||||
UnsafeRawHtml(highlightLyngHtml(ensureBootstrapCodeBlocks(mapHtml)))
|
UnsafeRawHtml(highlightLyngHtml(ensureBootstrapCodeBlocks(mapHtml)))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short features list
|
// Short features list
|
||||||
Div({ classes("row", "g-4", "mt-1") }) {
|
Div({ classes("row", "g-4", "mt-1") }) {
|
||||||
@ -262,36 +147,3 @@ fun HomePage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ensureSlideshowStyles() {
|
|
||||||
if (window.document.getElementById("slideshow-styles") != null) return
|
|
||||||
val style = window.document.createElement("style") as org.w3c.dom.HTMLStyleElement
|
|
||||||
style.id = "slideshow-styles"
|
|
||||||
style.textContent = """
|
|
||||||
@keyframes slideIn {
|
|
||||||
from { opacity: 0; transform: translateX(30px); }
|
|
||||||
to { opacity: 1; transform: translateX(0); }
|
|
||||||
}
|
|
||||||
.slide-animation {
|
|
||||||
animation: slideIn 0.4s ease-out;
|
|
||||||
}
|
|
||||||
.slide-container {
|
|
||||||
min-height: 320px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: transform 0.2s;
|
|
||||||
background-color: var(--bs-body-bg);
|
|
||||||
}
|
|
||||||
.slide-container:hover {
|
|
||||||
transform: scale(1.005);
|
|
||||||
}
|
|
||||||
.try-hint {
|
|
||||||
opacity: 0.5;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
.slide-container:hover .try-hint {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
window.document.head!!.appendChild(style)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -18,30 +18,20 @@
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.sergeych.lyng.LyngVersion
|
import net.sergeych.lyng.LyngVersion
|
||||||
import net.sergeych.lyng.Script
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.ScriptError
|
import net.sergeych.lyng.ScriptError
|
||||||
import net.sergeych.lyngweb.EditorWithOverlay
|
import net.sergeych.lyngweb.EditorWithOverlay
|
||||||
import org.jetbrains.compose.web.dom.*
|
import org.jetbrains.compose.web.dom.*
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TryLyngPage(route: String) {
|
fun TryLyngPage() {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val initialCode = remember(route) {
|
var code by remember {
|
||||||
val params = route.substringAfter('?', "")
|
|
||||||
val codeParam = params.split('&').find { it.startsWith("code=") }?.substringAfter('=')
|
|
||||||
if (codeParam != null) {
|
|
||||||
try {
|
|
||||||
decodeURIComponent(codeParam)
|
|
||||||
} catch (_: Throwable) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
var code by remember(initialCode) {
|
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
initialCode ?: """
|
"""
|
||||||
// Welcome to Lyng! Edit and run.
|
// Welcome to Lyng! Edit and run.
|
||||||
// Try changing the data and press Ctrl+Enter or click Run.
|
// Try changing the data and press Ctrl+Enter or click Run.
|
||||||
|
import lyng.stdlib
|
||||||
|
|
||||||
val data = 1..5 // or [1, 2, 3, 4, 5]
|
val data = 1..5 // or [1, 2, 3, 4, 5]
|
||||||
data.filter { it % 2 == 0 }.map { it * it }
|
data.filter { it % 2 == 0 }.map { it * it }
|
||||||
@ -64,16 +54,16 @@ fun TryLyngPage(route: String) {
|
|||||||
val printed = StringBuilder()
|
val printed = StringBuilder()
|
||||||
try {
|
try {
|
||||||
// Create a fresh module scope each run so imports and vars are clean
|
// Create a fresh module scope each run so imports and vars are clean
|
||||||
val s = Script.newScope()
|
val s = Scope.new()
|
||||||
// Capture printed output from Lyng `print`/`println` into the UI result window
|
// Capture printed output from Lyng `print`/`println` into the UI result window
|
||||||
s.addVoidFn("print") {
|
s.addVoidFn("print") {
|
||||||
for ((i, a) in this.args.withIndex()) {
|
for ((i, a) in args.withIndex()) {
|
||||||
if (i > 0) printed.append(' ')
|
if (i > 0) printed.append(' ')
|
||||||
printed.append(a.toString(this).value)
|
printed.append(a.toString(this).value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.addVoidFn("println") {
|
s.addVoidFn("println") {
|
||||||
for ((i, a) in this.args.withIndex()) {
|
for ((i, a) in args.withIndex()) {
|
||||||
if (i > 0) printed.append(' ')
|
if (i > 0) printed.append(' ')
|
||||||
printed.append(a.toString(this).value)
|
printed.append(a.toString(this).value)
|
||||||
}
|
}
|
||||||
@ -128,8 +118,9 @@ fun TryLyngPage(route: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun resetCode() {
|
fun resetCode() {
|
||||||
code = initialCode ?: """
|
code = """
|
||||||
// Welcome to Lyng! Edit and run.
|
// Welcome to Lyng! Edit and run.
|
||||||
|
import lyng.stdlib
|
||||||
[1,2,3].map { it * 10 }
|
[1,2,3].map { it * 10 }
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
output = null
|
output = null
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user