Start process of implementing Lua to frame connection.

This commit is contained in:
Condorra 2024-08-16 21:39:39 +10:00
parent 1fbcfefe1e
commit 1b14973b1d
13 changed files with 1490 additions and 153 deletions

3
.cargo/config.toml Normal file
View File

@ -0,0 +1,3 @@
[unstable]
# Remove once Rust makes this the default.
wasm_c_abi="spec"

546
Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
dependencies = [
"gimli",
]
@ -17,6 +17,37 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aliasable"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "anyhow"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "anymap2"
version = "0.13.0"
@ -25,15 +56,15 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
[[package]]
name = "autocfg"
version = "1.1.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "backtrace"
version = "0.3.69"
version = "0.3.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
dependencies = [
"addr2line",
"cc",
@ -53,6 +84,12 @@ dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bitmaps"
version = "2.1.0"
@ -70,24 +107,27 @@ checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
[[package]]
name = "bumpalo"
version = "3.14.0"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.5.0"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
name = "cc"
version = "1.0.83"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
[[package]]
name = "cfg-if"
@ -105,6 +145,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "equivalent"
version = "1.0.1"
@ -119,18 +165,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
@ -142,9 +188,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
@ -152,44 +198,44 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-io"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
name = "futures-sink"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
@ -204,10 +250,34 @@ dependencies = [
]
[[package]]
name = "getrandom"
version = "0.2.11"
name = "gc-arena"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
checksum = "3cd70cf88a32937834aae9614ff2569b5d9467fa0c42c5d7762fd94a8de88266"
dependencies = [
"allocator-api2",
"gc-arena-derive",
"hashbrown",
"sptr",
]
[[package]]
name = "gc-arena-derive"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c612a69f5557a11046b77a7408d2836fe77077f842171cd211c5ef504bd3cddd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.73",
"synstructure",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"js-sys",
@ -218,9 +288,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.28.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "gloo"
@ -251,7 +321,7 @@ dependencies = [
"gloo-dialogs 0.2.0",
"gloo-events 0.2.0",
"gloo-file 0.3.0",
"gloo-history 0.2.1",
"gloo-history 0.2.2",
"gloo-net 0.4.0",
"gloo-render 0.2.0",
"gloo-storage 0.3.0",
@ -368,15 +438,15 @@ dependencies = [
[[package]]
name = "gloo-history"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4022e82f5f9e03cb1251b13c0a967e0600e97aa179c617f6519bac40640160"
checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6"
dependencies = [
"getrandom",
"gloo-events 0.2.0",
"gloo-utils 0.2.0",
"serde",
"serde-wasm-bindgen 0.6.1",
"serde-wasm-bindgen 0.6.5",
"serde_urlencoded",
"thiserror",
"wasm-bindgen",
@ -566,26 +636,36 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
name = "hashbrown"
version = "0.14.2"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.3"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "http"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
@ -608,9 +688,9 @@ dependencies = [
[[package]]
name = "implicit-clone"
version = "0.4.7"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16c5448a864f9abf124ef8bf2a3cc37eb9fd99fe2e804a8b3235d7357bca2c25"
checksum = "f8a9aa791c7b5a71b636b7a68207fdebf171ddfc593d9c8506ec4cbc527b6a84"
dependencies = [
"implicit-clone-derive",
"indexmap",
@ -623,24 +703,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9311685eb9a34808bbb0608ad2fcab9ae216266beca5848613e95553ac914e3b"
dependencies = [
"quote",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
name = "indexmap"
version = "2.1.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.9"
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
@ -653,31 +751,55 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "log"
version = "0.4.20"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "memchr"
version = "2.6.4"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "minicrossterm"
version = "0.28.1"
source = "git+https://git.blastmud.org/blasthavers/minicrossterm.git?rev=494f89daef41162fbd89d5266e261018ed5ff6dc#494f89daef41162fbd89d5266e261018ed5ff6dc"
dependencies = [
"bitflags",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
@ -690,50 +812,90 @@ dependencies = [
[[package]]
name = "object"
version = "0.32.1"
version = "0.36.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "ouroboros"
version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67"
dependencies = [
"aliasable",
"ouroboros_macro",
"static_assertions",
]
[[package]]
name = "ouroboros_macro"
version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd"
dependencies = [
"heck",
"itertools 0.12.1",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.73",
]
[[package]]
name = "percent-encoding"
version = "2.3.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "piccolo"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "003bf52de285e1ff1adcbc6572588db3849988ea660a2d55af3a2ffbc81f597f"
dependencies = [
"ahash",
"allocator-api2",
"anyhow",
"gc-arena",
"hashbrown",
"rand",
"thiserror",
]
[[package]]
name = "pin-project"
version = "1.1.3"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.3"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
@ -753,13 +915,22 @@ dependencies = [
]
[[package]]
name = "prettyplease"
version = "0.2.15"
name = "ppv-lite86"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
dependencies = [
"proc-macro2",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
@ -798,13 +969,26 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "proc-macro2-diagnostics"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.73",
"version_check",
"yansi",
]
[[package]]
name = "prokio"
version = "0.1.0"
@ -824,18 +1008,42 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.33"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_xoshiro"
@ -848,27 +1056,27 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustversion"
version = "1.0.14"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.192"
version = "1.0.206"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284"
dependencies = [
"serde_derive",
]
@ -886,9 +1094,9 @@ dependencies = [
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.1"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ba92964781421b6cef36bf0d7da26d201e96d84e1b10e7ae6ed416e516906d"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
dependencies = [
"js-sys",
"serde",
@ -897,22 +1105,23 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.192"
version = "1.0.206"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
name = "serde_json"
version = "1.0.108"
version = "1.0.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
@ -948,6 +1157,18 @@ dependencies = [
"autocfg",
]
[[package]]
name = "sptr"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "1.0.109"
@ -960,9 +1181,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "837a7e8026c6ce912ff01cefbe8cafc2f8010ac49682e2a3d9decc3bce1ecaaf"
dependencies = [
"proc-macro2",
"quote",
@ -970,30 +1191,41 @@ dependencies = [
]
[[package]]
name = "thiserror"
version = "1.0.50"
name = "synstructure"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.73",
]
[[package]]
name = "thiserror"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
name = "tokio"
version = "1.34.0"
version = "1.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
dependencies = [
"backtrace",
"pin-project-lite",
@ -1001,9 +1233,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
version = "0.1.14"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
dependencies = [
"futures-core",
"pin-project-lite",
@ -1012,9 +1244,9 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.5"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
[[package]]
name = "toml_edit"
@ -1046,7 +1278,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
@ -1071,10 +1303,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
@ -1103,15 +1347,15 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.38"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [
"cfg-if",
"js-sys",
@ -1137,7 +1381,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1160,13 +1404,38 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.5.19"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "worldwideportal"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"im",
"itertools 0.13.0",
"minicrossterm",
"nom",
"ouroboros",
"piccolo",
"thiserror",
"unicode-segmentation",
"unicode-width",
"wasm-bindgen",
"web-sys",
"yew",
]
[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yew"
version = "0.21.0"
@ -1204,15 +1473,26 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.73",
]
[[package]]
name = "yew-test"
version = "0.1.0"
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"im",
"wasm-bindgen",
"web-sys",
"yew",
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.73",
]

View File

@ -7,6 +7,15 @@ edition = "2021"
[dependencies]
im = "15.1.0"
itertools = "0.13.0"
nom = "7.1.3"
piccolo = "0.3.3"
unicode-segmentation = "1.11.0"
unicode-width = "0.1.13"
wasm-bindgen = "0.2.92"
web-sys = "0.3.69"
yew = { version = "0.21.0", features = ["csr"] }
minicrossterm = { git = "https://git.blastmud.org/blasthavers/minicrossterm.git", rev = "494f89daef41162fbd89d5266e261018ed5ff6dc" }
thiserror = "1.0.63"
console_error_panic_hook = "0.1.7"
ouroboros = "0.18.4"

57
src/command_handler.rs Normal file
View File

@ -0,0 +1,57 @@
use itertools::join;
use std::{cell::RefCell, rc::Rc};
use yew::Callback;
use crate::{
echo_to_term_frame, lua_state::LuaState, parsing::parse_commands, RegisteredTermFrameLens,
TermFrame,
};
fn reentrant_command_handler(
lua_state: &mut LuaState,
frames: &RegisteredTermFrameLens,
term_frame: &TermFrame,
command_in: &str,
) {
web_sys::console::log_1(&"Inside command handler".into());
echo_to_term_frame(frames, term_frame, "Hello World!\n");
for command in parse_commands(command_in).commands {
match command.split_out_command() {
None => (),
Some((cmd, rest)) => {
if cmd == "##" {
match lua_state.execute(&join(rest.arguments.iter(), " ")) {
Ok(msg) => echo_to_term_frame(frames, term_frame, &msg).unwrap_or(()),
Err(msg) => echo_to_term_frame(frames, term_frame, &msg).unwrap_or(()),
}
}
}
}
}
}
fn command_handler(
lua_state: &RefCell<LuaState>,
frames: &RegisteredTermFrameLens,
term_frame: &TermFrame,
command_in: String,
) {
match lua_state.try_borrow_mut() {
Err(_) => echo_to_term_frame(
frames,
term_frame,
"Attempt to re-enter command handler during processing.\n",
)
.unwrap_or(()), // Ignore error handling error.
Ok(mut lua_state_m) => {
reentrant_command_handler(&mut lua_state_m, frames, term_frame, &command_in)
}
}
}
pub fn make_command_handler_callback(
lua_state: Rc<RefCell<LuaState>>,
frames: RegisteredTermFrameLens,
) -> Callback<(TermFrame, String), ()> {
Callback::from(move |(term, cmd)| command_handler(&lua_state, &frames, &term, cmd))
}

2
src/lineengine.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod history;
pub mod line;

10
src/lineengine/README.md Normal file
View File

@ -0,0 +1,10 @@
The code is this directory is a hacked and cut back version of
https://github.com/zyansheep/rustyline-async
In particular, we only use the history and line state machine part
of it, not the intended async interface of that crate (which is
not a good fit for our needs, and uses standard input).
rustyline-async was released into the public domain under
the [Unlicense](https://unlicense.org/). Thank you to the original
authors!

46
src/lineengine/history.rs Normal file
View File

@ -0,0 +1,46 @@
use std::collections::VecDeque;
pub struct History {
pub entries: VecDeque<String>,
pub max_size: usize,
current_position: Option<usize>,
}
impl Default for History {
fn default() -> Self {
Self {
entries: Default::default(),
max_size: 1000,
current_position: Default::default(),
}
}
}
impl History {
// Find next history that matches a given string from an index
pub fn search_next(&mut self, _current: &str) -> Option<&str> {
if let Some(index) = &mut self.current_position {
if *index < self.entries.len() - 1 {
*index += 1;
}
Some(&self.entries[*index])
} else if !self.entries.is_empty() {
self.current_position = Some(0);
Some(&self.entries[0])
} else {
None
}
}
// Find previous history item that matches a given string from an index
pub fn search_previous(&mut self, _current: &str) -> Option<&str> {
if let Some(index) = &mut self.current_position {
if *index == 0 {
self.current_position = None;
return Some("");
}
*index -= 1;
Some(&self.entries[*index])
} else {
None
}
}
}

509
src/lineengine/line.rs Normal file
View File

@ -0,0 +1,509 @@
use std::io::{self, Write};
use minicrossterm::{
cursor,
event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, TerminalState},
terminal::{Clear, ClearType::*},
QueueableCommand,
};
use thiserror::Error;
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use crate::lineengine::history::History;
/// Error returned from [`readline()`][Readline::readline]. Such errors
/// generally require specific procedures to recover from.
#[derive(Debug, Error)]
pub enum ReadlineError {
/// An internal I/O error occurred
#[error(transparent)]
IO(#[from] io::Error),
/// `readline()` was called after the [`SharedWriter`] was dropped and
/// everything written to the `SharedWriter` was already output
#[error("line writers closed")]
Closed,
}
/// Events emitted by [`Readline::readline()`]
#[derive(Debug)]
pub enum ReadlineEvent {
/// The user entered a line of text
Line(String),
/// The user pressed Ctrl-D
Eof,
/// The user pressed Ctrl-C
Interrupted,
}
#[derive(Default)]
pub struct LineState {
// Unicode Line
line: String,
// Index of grapheme in line
line_cursor_grapheme: usize,
// Column of grapheme in line
current_column: u16,
cluster_buffer: String, // buffer for holding partial grapheme clusters as they come in
prompt: String,
pub should_print_line_on_enter: bool, // After pressing enter, should we print the line just submitted?
pub should_print_line_on_control_c: bool, // After pressing control_c should we print the line just cancelled?
last_line_length: usize,
last_line_completed: bool,
term_size: (u16, u16),
pub history: History,
}
impl LineState {
pub fn new(prompt: String, term_size: (u16, u16)) -> Self {
let current_column = prompt.len() as u16;
Self {
prompt,
last_line_completed: true,
term_size,
current_column,
should_print_line_on_enter: true,
should_print_line_on_control_c: true,
..Default::default()
}
}
fn line_height(&self, pos: u16) -> u16 {
pos / self.term_size.0 // Gets the number of lines wrapped
}
/// Move from a position on the line to the start
fn move_to_beginning(&self, term: &mut impl Write, from: u16) -> io::Result<()> {
let move_up = self.line_height(from.saturating_sub(1));
term.queue(cursor::MoveToColumn(0))?;
if move_up != 0 {
term.queue(cursor::MoveUp(move_up))?;
}
Ok(())
}
/// Move from the start of the line to some position
fn move_from_beginning(&self, term: &mut impl Write, to: u16) -> io::Result<()> {
let line_height = self.line_height(to.saturating_sub(1));
let line_remaining_len = to % self.term_size.0; // Get the remaining length
if line_height != 0 {
term.queue(cursor::MoveDown(line_height))?;
}
term.queue(cursor::MoveRight(line_remaining_len))?;
Ok(())
}
/// Move cursor by one unicode grapheme either left (negative) or right (positive)
fn move_cursor(&mut self, change: isize) -> io::Result<()> {
if change > 0 {
let count = self.line.graphemes(true).count();
self.line_cursor_grapheme =
usize::min(self.line_cursor_grapheme + change as usize, count);
} else {
self.line_cursor_grapheme =
self.line_cursor_grapheme.saturating_sub((-change) as usize);
}
let (pos, str) = self.current_grapheme().unwrap_or((0, ""));
let pos = pos + str.len();
self.current_column =
(self.prompt.len() + UnicodeWidthStr::width(&self.line[0..pos])) as u16;
Ok(())
}
fn current_grapheme(&self) -> Option<(usize, &str)> {
self.line
.grapheme_indices(true)
.take(self.line_cursor_grapheme)
.last()
}
fn next_grapheme(&self) -> Option<(usize, &str)> {
let total = self.line.grapheme_indices(true).count();
if self.line_cursor_grapheme == total {
return None;
}
self.line
.grapheme_indices(true)
.take(self.line_cursor_grapheme + 1)
.last()
}
fn reset_cursor(&self, term: &mut impl Write) -> io::Result<()> {
self.move_to_beginning(term, self.current_column)
}
fn set_cursor(&self, term: &mut impl Write) -> io::Result<()> {
self.move_from_beginning(term, self.current_column)
}
/// Clear current line
pub fn clear(&self, term: &mut impl Write) -> io::Result<()> {
self.move_to_beginning(term, self.current_column)?;
term.queue(Clear(FromCursorDown))?;
Ok(())
}
/// Render line
pub fn render(&self, term: &mut impl Write) -> io::Result<()> {
write!(term, "{}{}", self.prompt, self.line)?;
let line_len = self.prompt.len() + UnicodeWidthStr::width(&self.line[..]);
self.move_to_beginning(term, line_len as u16)?;
self.move_from_beginning(term, self.current_column)?;
Ok(())
}
/// Clear line and render
pub fn clear_and_render(&self, term: &mut impl Write) -> io::Result<()> {
self.clear(term)?;
self.render(term)?;
Ok(())
}
pub fn print_data(&mut self, data: &[u8], term: &mut impl Write) -> Result<(), ReadlineError> {
self.clear(term)?;
// If last written data was not newline, restore the cursor
if !self.last_line_completed {
term.queue(cursor::MoveUp(1))?
.queue(cursor::MoveToColumn(0))?
.queue(cursor::MoveRight(self.last_line_length as u16))?;
}
// Write data in a way that newlines also act as carriage returns
for line in data.split_inclusive(|b| *b == b'\n') {
term.write_all(line)?;
term.queue(cursor::MoveToColumn(0))?;
}
self.last_line_completed = data.ends_with(b"\n"); // Set whether data ends with newline
// If data does not end with newline, save the cursor and write newline for prompt
// Usually data does end in newline due to the buffering of SharedWriter, but sometimes it may not (i.e. if .flush() is called)
if !self.last_line_completed {
self.last_line_length += data.len();
// Make sure that last_line_length wraps around when doing multiple writes
if self.last_line_length >= self.term_size.0 as usize {
self.last_line_length %= self.term_size.0 as usize;
writeln!(term)?;
}
writeln!(term)?; // Move to beginning of line and make new line
} else {
self.last_line_length = 0;
}
term.queue(cursor::MoveToColumn(0))?;
self.render(term)?;
Ok(())
}
pub fn print(&mut self, string: &str, term: &mut impl Write) -> Result<(), ReadlineError> {
self.print_data(string.as_bytes(), term)?;
Ok(())
}
pub fn update_prompt(
&mut self,
prompt: &str,
term: &mut impl Write,
) -> Result<(), ReadlineError> {
self.clear(term)?;
self.prompt.clear();
self.prompt.push_str(prompt);
// recalculates column
self.move_cursor(0)?;
self.render(term)?;
term.flush()?;
Ok(())
}
pub fn handle_event(
&mut self,
event: Event,
term: &mut impl Write,
) -> Result<Option<ReadlineEvent>, ReadlineError> {
match event {
// Control Keys
Event::Key(KeyEvent {
code,
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
..
}) => match code {
// End of transmission (CTRL-D)
KeyCode::Char('d') => {
writeln!(term)?;
self.clear(term)?;
return Ok(Some(ReadlineEvent::Eof));
}
// End of text (CTRL-C)
KeyCode::Char('c') => {
if self.should_print_line_on_control_c {
self.print(&format!("{}{}", self.prompt, self.line), term)?;
}
self.line.clear();
self.move_cursor(-10000)?;
self.clear_and_render(term)?;
return Ok(Some(ReadlineEvent::Interrupted));
}
// Clear all
KeyCode::Char('l') => {
term.queue(Clear(All))?.queue(cursor::MoveTo(0, 0))?;
self.clear_and_render(term)?;
}
// Clear to start
KeyCode::Char('u') => {
if let Some((pos, str)) = self.current_grapheme() {
let pos = pos + str.len();
self.line.drain(0..pos);
self.move_cursor(-100000)?;
self.clear_and_render(term)?;
}
}
// Clear last word
KeyCode::Char('w') => {
let count = self.line.graphemes(true).count();
let skip_count = count - self.line_cursor_grapheme;
let start = self
.line
.grapheme_indices(true)
.rev()
.skip(skip_count)
.skip_while(|(_, str)| *str == " ")
.find_map(|(pos, str)| if str == " " { Some(pos + 1) } else { None })
.unwrap_or(0);
let end = self
.line
.grapheme_indices(true)
.nth(self.line_cursor_grapheme)
.map(|(end, _)| end);
let change = start as isize - self.line_cursor_grapheme as isize;
self.move_cursor(change)?;
if let Some(end) = end {
self.line.drain(start..end);
} else {
self.line.drain(start..);
}
self.clear_and_render(term)?;
}
// Move to beginning
#[cfg(feature = "emacs")]
KeyCode::Char('a') => {
self.reset_cursor(term)?;
self.move_cursor(-100000)?;
self.set_cursor(term)?;
}
// Move to end
#[cfg(feature = "emacs")]
KeyCode::Char('e') => {
self.reset_cursor(term)?;
self.move_cursor(100000)?;
self.set_cursor(term)?;
}
// Move cursor left to previous word
KeyCode::Left => {
self.reset_cursor(term)?;
let count = self.line.graphemes(true).count();
let skip_count = count - self.line_cursor_grapheme;
if let Some((pos, _)) = self
.line
.grapheme_indices(true)
.rev()
.skip(skip_count)
.skip_while(|(_, str)| *str == " ")
.find(|(_, str)| *str == " ")
{
let change = pos as isize - self.line_cursor_grapheme as isize;
self.move_cursor(change + 1)?;
} else {
self.move_cursor(-100000)?
}
self.set_cursor(term)?;
}
// Move cursor right to next word
KeyCode::Right => {
self.reset_cursor(term)?;
if let Some((pos, _)) = self
.line
.grapheme_indices(true)
.skip(self.line_cursor_grapheme)
.skip_while(|(_, c)| *c == " ")
.find(|(_, c)| *c == " ")
{
let change = pos as isize - self.line_cursor_grapheme as isize;
self.move_cursor(change)?;
} else {
self.move_cursor(10000)?;
};
self.set_cursor(term)?;
}
_ => {}
},
// Other Modifiers (None, Shift, Control+Alt)
// All other modifiers must be considered because the match expression cannot match
// combined KeyModifiers. Control+Alt is used to reach certain special symbols on a lot
// of international keyboard layouts.
Event::Key(KeyEvent {
code,
modifiers: _,
kind: KeyEventKind::Press,
..
}) => match code {
KeyCode::Enter => {
// Print line so you can see what commands you've typed
if self.should_print_line_on_enter {
self.print(&format!("{}{}\n", self.prompt, self.line), term)?;
}
// Take line
let line = std::mem::take(&mut self.line);
// Render new line from beginning
self.move_cursor(-100000)?;
self.clear_and_render(term)?;
// Return line
return Ok(Some(ReadlineEvent::Line(line)));
}
// Delete character from line
KeyCode::Backspace => {
if let Some((pos, str)) = self.current_grapheme() {
self.clear(term)?;
let len = pos + str.len();
self.line.replace_range(pos..len, "");
self.move_cursor(-1)?;
self.render(term)?;
}
}
KeyCode::Delete => {
if let Some((pos, str)) = self.next_grapheme() {
self.clear(term)?;
let len = pos + str.len();
self.line.replace_range(pos..len, "");
self.render(term)?;
}
}
KeyCode::Left => {
self.reset_cursor(term)?;
self.move_cursor(-1)?;
self.set_cursor(term)?;
}
KeyCode::Right => {
self.reset_cursor(term)?;
self.move_cursor(1)?;
self.set_cursor(term)?;
}
KeyCode::Home => {
self.reset_cursor(term)?;
self.move_cursor(-100000)?;
self.set_cursor(term)?;
}
KeyCode::End => {
self.reset_cursor(term)?;
self.move_cursor(100000)?;
self.set_cursor(term)?;
}
KeyCode::Up => {
// search for next history item, replace line if found.
if let Some(line) = self.history.search_next(&self.line) {
self.line.clear();
self.line += line;
self.clear(term)?;
self.move_cursor(100000)?;
self.render(term)?;
}
}
KeyCode::Down => {
// search for next history item, replace line if found.
if let Some(line) = self.history.search_previous(&self.line) {
self.line.clear();
self.line += line;
self.clear(term)?;
self.move_cursor(100000)?;
self.render(term)?;
}
}
// Add character to line and output
KeyCode::Char(c) => {
self.clear(term)?;
let prev_len = self.cluster_buffer.graphemes(true).count();
self.cluster_buffer.push(c);
let new_len = self.cluster_buffer.graphemes(true).count();
let (g_pos, g_str) = self.current_grapheme().unwrap_or((0, ""));
let pos = g_pos + g_str.len();
self.line.insert(pos, c);
if prev_len != new_len {
self.move_cursor(1)?;
if prev_len > 0 {
if let Some((pos, str)) =
self.cluster_buffer.grapheme_indices(true).next()
{
let len = str.len();
self.cluster_buffer.replace_range(pos..len, "");
}
}
}
self.render(term)?;
}
_ => {}
},
Event::Resize(x, y) => {
self.term_size = (x, y);
self.clear_and_render(term)?;
}
_ => {}
}
Ok(None)
}
}
pub struct WritetermFunction {
f: Box<dyn FnMut(&[u8])>,
}
pub struct Readline {
term: TerminalState,
line_state: LineState,
write_term: WritetermFunction,
}
impl Write for WritetermFunction {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(self.f)(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Readline {
pub fn new(prompt: String, write_fn: Box<dyn FnMut(&[u8])>, term_size: (u16, u16)) -> Self {
Self {
term: TerminalState::new(),
line_state: LineState::new(prompt, term_size),
write_term: WritetermFunction { f: write_fn },
}
}
pub fn handle_resize(&mut self, term_size: (u16, u16)) -> Result<(), ReadlineError> {
self.line_state.handle_event(
Event::Resize(term_size.0, term_size.1),
&mut self.write_term,
)?;
Ok(())
}
pub fn readline(&mut self, input: &[u8]) -> Result<Vec<ReadlineEvent>, ReadlineError> {
let evs = self.term.events_for_input(input)?;
let mut results: Vec<ReadlineEvent> = Vec::new();
for ev in evs {
results.extend(self.line_state.handle_event(ev, &mut self.write_term)?);
}
Ok(results)
}
}

@ -0,0 +1 @@
Subproject commit d5894639257d1f0c881366b0cd7152f023fdcc76

55
src/lua_state.rs Normal file
View File

@ -0,0 +1,55 @@
use piccolo::{Callback, Closure, Context, Executor, IntoValue, Lua, StashedExecutor, Table};
use crate::{echo_to_term_frame, RegisteredTermFrameLens, TermFrame};
use std::str;
pub struct LuaState {
pub interp: Lua,
pub exec: StashedExecutor,
}
impl LuaState {
pub fn setup(frames: RegisteredTermFrameLens) -> Result<LuaState, String> {
let mut interp = Lua::core();
let exec: StashedExecutor = interp.enter(|ctx| {
let cmd_table = Table::new(&ctx);
cmd_table
.set(
ctx,
ctx.intern_static(b"echo_frame"),
echo_frame(ctx, frames),
)
.map_err(|_| "Can't add command")?;
ctx.set_global(ctx.intern_static(b"commands").into_value(ctx), cmd_table)
.map(|_| ())
.map_err(|_| "Can't set commands key".to_owned())?;
Ok::<StashedExecutor, String>(ctx.stash(Executor::new(ctx)))
})?;
Ok(LuaState { interp, exec })
}
pub fn execute(&mut self, command: &str) -> Result<String, String> {
self.interp
.try_enter(|ctx| {
let closure = Closure::load(ctx, None, format!("return ({})", command).as_bytes())
.or_else(|_| Closure::load(ctx, None, command.as_bytes()))?;
ctx.fetch(&self.exec).restart(ctx, closure.into(), ());
Ok(())
})
.map_err(|err| format!("{}", err))?;
Ok("Blah".to_owned())
}
}
fn echo_frame(ctx: Context, frames: RegisteredTermFrameLens) -> Callback {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let frame_no: u64 = stack.consume(ctx)?;
let message: piccolo::String = stack.consume(ctx)?;
let message_str = str::from_utf8(message.as_bytes())
.map_err(|_| "Expected message to echo to be UTF-8.".into_value(ctx))?;
echo_to_term_frame(&frames, &TermFrame(frame_no), message_str)
.map_err(|m| m.into_value(ctx))?;
Ok(piccolo::CallbackReturn::Return)
})
}

View File

@ -1,30 +1,49 @@
use std::cell::RefCell;
use std::rc::Rc;
use yew::prelude::*;
pub mod command_handler;
pub mod lineengine;
pub mod lua_state;
pub mod parsing;
pub mod term_view;
use crate::command_handler::*;
use crate::lua_state::LuaState;
use crate::term_view::*;
#[derive(Properties)]
struct GlobalState {
frame_registry: RegisteredTermFrames,
lua_engine: LuaState,
}
// Used only for yew. Always equal since we don't want it to
// actually look into GlobalState, changes there should never
// cause a re-render.
impl PartialEq for GlobalState {
fn eq(&self, _other: &Self) -> bool {
true
}
}
#[function_component(App)]
fn app() -> Html {
let frames_handle: UseStateHandle<Rc<RegisteredTermFrames>> =
use_state(|| RegisteredTermFrames::new().into());
let frames = RegisteredTermFrameLens {
get: (*frames_handle).clone(),
set: Callback::from(move |s| frames_handle.set(s)),
};
let global = use_mut_ref(|| Global {
frame_registry: RegisteredTermFrames::new().into(),
lua_engine: LuaState::setup(frames.clone()).expect("Can create interpreter"),
});
html! {
<div class="vpane toplevel">
<TermView terminal={TermFrame(0)} frames={frames.clone()}/>
<TermView terminal={TermFrame(1)} frames={frames.clone()}/>
<div class="hpane">
<TermView terminal={TermFrame(2)} frames={frames.clone()}/>
<TermView terminal={TermFrame(3)} {frames}/>
</div>
<TermView terminal={TermFrame(0)} frames={frames.clone()} handler={command_handler.clone()}/>
<TermView terminal={TermFrame(1)} frames={frames.clone()} handler={command_handler.clone()}/>
</div>
}
}
fn main() {
console_error_panic_hook::set_once();
yew::Renderer::<App>::new().render();
}

231
src/parsing.rs Normal file
View File

@ -0,0 +1,231 @@
use std::collections::VecDeque;
use nom::{
branch::alt,
character::complete::{anychar, char, none_of},
combinator::{eof, map, recognize, value},
multi::{many0_count, separated_list0},
sequence::{preceded, separated_pair},
IResult,
};
#[derive(PartialEq, Eq, Debug)]
pub struct ParseResult<'l> {
pub commands: Vec<ParsedCommand<'l>>,
}
#[derive(PartialEq, Eq, Debug)]
pub struct ParsedCommand<'l> {
pub arguments: VecDeque<&'l str>,
}
impl ParsedCommand<'_> {
pub fn split_out_command(&self) -> Option<(&str, Self)> {
let mut tmp_arguments = self.arguments.clone();
loop {
match tmp_arguments.pop_front() {
None => return None,
Some(head) => {
let trimmed_cmd = head.trim();
if !trimmed_cmd.is_empty() {
return Some((
trimmed_cmd,
Self {
arguments: tmp_arguments,
},
));
}
}
}
}
}
}
fn parse_string(input: &str) -> IResult<&str, ()> {
value(
(),
many0_count(alt((
value((), preceded(char('\\'), anychar)),
value((), none_of("\"")),
))),
)(input)
}
fn parse_parenthetical(input: &str) -> IResult<&str, ()> {
value(
(),
many0_count(alt((
value((), preceded(char('\\'), anychar)),
value(
(),
separated_pair(
char('{'),
parse_parenthetical,
alt((value((), eof), value((), char('}')))),
),
),
value(
(),
separated_pair(
char('"'),
parse_string,
alt((value((), eof), value((), char('"')))),
),
),
value((), none_of("}")),
))),
)(input)
}
fn parse_argument(input: &str) -> IResult<&str, ()> {
value(
(),
many0_count(alt((
value((), preceded(char('\\'), anychar)),
value(
(),
separated_pair(
char('{'),
parse_parenthetical,
alt((value((), eof), value((), char('}')))),
),
),
value(
(),
separated_pair(
char('"'),
parse_string,
alt((value((), eof), value((), char('"')))),
),
),
value((), none_of(" ;")),
))),
)(input)
}
fn parse_command(input: &str) -> IResult<&str, ParsedCommand> {
map(
separated_list0(char(' '), recognize(parse_argument)),
|arguments| ParsedCommand {
arguments: arguments.into(),
},
)(input)
}
pub fn parse_commands(input: &str) -> ParseResult {
// Note that the core parser doesn't do things like skipping multiple whitespace,
separated_list0(preceded(char(';'), many0_count(char(' '))), parse_command)(input)
.map(|(_, commands)| ParseResult { commands })
.unwrap_or_else(|_| ParseResult { commands: vec![] })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_commands() {
assert_eq!(
parse_commands(""),
ParseResult {
commands: vec![ParsedCommand {
arguments: vec![""]
}]
}
);
assert_eq!(
parse_commands("north"),
ParseResult {
commands: vec![ParsedCommand {
arguments: vec!["north"]
}]
}
);
assert_eq!(
parse_commands("north "),
ParseResult {
commands: vec![ParsedCommand {
// This is deliberate, ensures we can reconstruct the input.
arguments: vec!["north", ""]
}]
}
);
assert_eq!(
parse_commands("#blah {x = 1 + 2; y = 3}; #home"),
ParseResult {
commands: vec![
ParsedCommand {
arguments: vec!["#blah", "{x = 1 + 2; y = 3}"]
},
ParsedCommand {
arguments: vec!["#home"]
}
]
}
);
assert_eq!(
parse_commands("#blah {x = 1 + 2"),
ParseResult {
commands: vec![ParsedCommand {
arguments: vec!["#blah", "{x = 1 + 2"]
},]
}
);
assert_eq!(
parse_commands("#blah {x = 1} {y = 1}"),
ParseResult {
commands: vec![ParsedCommand {
arguments: vec!["#blah", "{x = 1}", "{y = 1}"]
}]
}
);
assert_eq!(
parse_commands("#blah \"hello\" \"world\""),
ParseResult {
commands: vec![ParsedCommand {
arguments: vec!["#blah", "\"hello\"", "\"world\""]
}]
}
);
assert_eq!(
parse_commands("#blah {x = \"}\"} {y = 1}"),
ParseResult {
commands: vec![ParsedCommand {
arguments: vec!["#blah", "{x = \"}\"}", "{y = 1}"]
}]
}
);
assert_eq!(
parse_commands("#blah {x = \"}\"; a = \"{\"; y = {}; z = 1;} { q = 5 };"),
ParseResult {
commands: vec![
ParsedCommand {
arguments: vec![
"#blah",
"{x = \"}\"; a = \"{\"; y = {}; z = 1;}",
"{ q = 5 }"
]
},
ParsedCommand {
arguments: vec![""]
}
]
}
);
assert_eq!(
parse_commands("#blah {\\}\\}\\}} {y = 1}"),
ParseResult {
commands: vec![ParsedCommand {
arguments: vec!["#blah", "{\\}\\}\\}}", "{y = 1}"]
}]
}
);
assert_eq!(
parse_commands("#blah \"This is a \\\"test\\\"\""),
ParseResult {
commands: vec![ParsedCommand {
arguments: vec!["#blah", "\"This is a \\\"test\\\"\""]
}]
}
);
}
}

View File

@ -1,23 +1,57 @@
use std::rc::Rc;
use std::{
cell::RefCell,
rc::{Rc, Weak},
};
use im::hashmap::*;
use wasm_bindgen::prelude::*;
use web_sys::{Element, Node};
use yew::prelude::*;
use crate::lineengine::line::{Readline, ReadlineEvent};
#[wasm_bindgen]
extern "C" {
#[derive(PartialEq)]
pub type Terminal;
// Sadly, can't do type parameters with wasm_bindgen.
pub type IEventString;
pub type IEventDims;
pub type IDisposable;
#[wasm_bindgen(constructor)]
fn new() -> Terminal;
#[wasm_bindgen(method)]
fn open(this: &Terminal, element: &Element);
#[wasm_bindgen(method)]
fn write(this: &Terminal, data: &str);
pub fn write(this: &Terminal, data: &str);
// Todo: Can we do this with interfaces somehow?
#[wasm_bindgen(method)]
fn loadAddon(this: &Terminal, addon: &FitAddon);
#[wasm_bindgen(method)]
fn onData(this: &Terminal, listener: &Closure<dyn FnMut(String)>) -> IDisposable;
#[wasm_bindgen(method)]
fn onResize(this: &Terminal, listener: &Closure<dyn FnMut(Dims)>) -> IDisposable;
#[wasm_bindgen(method, getter)]
fn rows(this: &Terminal) -> u16;
#[wasm_bindgen(method, getter)]
fn cols(this: &Terminal) -> u16;
pub type Dims;
#[wasm_bindgen(method, getter)]
fn rows(this: &Dims) -> u16;
#[wasm_bindgen(method, getter)]
fn cols(this: &Dims) -> u16;
#[wasm_bindgen(method, setter)]
fn set_listener(this: &IEventString, handler: &Closure<dyn FnMut(String)>) -> IDisposable;
#[wasm_bindgen(method, setter)]
fn set_listener(this: &IEventDims, handler: &Closure<dyn FnMut(Dims)>) -> IDisposable;
#[wasm_bindgen(method)]
fn dispose(this: &IDisposable);
#[derive(PartialEq)]
pub type FitAddon;
}
@ -33,30 +67,40 @@ extern "C" {
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone)]
pub struct TermFrame(pub u64);
#[derive(Properties, PartialEq)]
#[derive(Properties)]
pub struct TermFrameData {
pub id: TermFrame,
pub term: Terminal,
pub fit: FitAddon,
pub node: Node,
pub readline: RefCell<Readline>,
pub retained_closures: RefCell<Option<(Closure<dyn FnMut(String)>, Closure<dyn FnMut(Dims)>)>>,
}
impl PartialEq for TermFrameData {
fn eq(&self, other: &Self) -> bool {
// Only the ID matters, the rest are just reference data.
self.id == other.id
}
}
pub type RegisteredTermFrames = HashMap<TermFrame, Rc<TermFrameData>>;
#[derive(Properties, PartialEq, Clone)]
pub struct RegisteredTermFrameLens {
pub get: Rc<RegisteredTermFrames>,
pub get: Callback<(), Rc<RegisteredTermFrames>>,
pub set: Callback<Rc<RegisteredTermFrames>, ()>,
}
fn get_or_make_term_frame(
frame: &TermFrame,
frames: &RegisteredTermFrameLens,
handler: &Callback<(TermFrame, String), ()>,
) -> Rc<TermFrameData> {
if let Some(tfd) = frames.get.get(frame) {
if let Some(tfd) = frames.get.emit(()).get(frame) {
return tfd.clone();
}
let mut new_frames: RegisteredTermFrames = (*frames.get).clone();
let mut new_frames: RegisteredTermFrames = (*frames.get.emit(())).clone();
let term = Terminal::new();
let fit = FitAddon::new();
let element = web_sys::window()
@ -66,17 +110,69 @@ fn get_or_make_term_frame(
element.set_class_name("hterminal");
term.open(&element);
term.loadAddon(&fit);
fit.fit();
for i in 0..100 {
term.write(&format!("{} Hello world\r\n", i));
}
let term_for_readline: Terminal = Terminal { obj: term.clone() };
let initial_size = (term.cols(), term.rows());
let new_data: Rc<TermFrameData> = TermFrameData {
id: frame.clone(),
term,
term: Terminal { obj: term.clone() },
fit,
node: element.into(),
readline: Readline::new(
"".to_owned(),
Box::new(move |dat| {
term_for_readline
.write(std::str::from_utf8(dat).expect("readline tried to emit invalid UTF-8"))
}),
initial_size,
)
.into(),
retained_closures: RefCell::new(None),
}
.into();
let data_for_resize: Weak<TermFrameData> = Rc::downgrade(&new_data);
let resize_closure = Closure::new(move |dims: Dims| match Weak::upgrade(&data_for_resize) {
None => {}
Some(r) => match r.readline.try_borrow_mut() {
Err(_) => {}
Ok(mut v) => v.handle_resize((dims.cols(), dims.rows())).unwrap_or(()),
},
});
term.onResize(&resize_closure);
let cloned_handler = (*handler).clone();
let emit_frame = frame.clone();
let data_for_on_data: Weak<TermFrameData> = Rc::downgrade(&new_data);
let data_closure = Closure::new(move |d: String| match Weak::upgrade(&data_for_on_data) {
None => {}
Some(r) => match r.readline.try_borrow_mut() {
Err(_) => {}
Ok(mut v) => {
for ev in v.readline(d.as_bytes()).expect("Readline failed") {
match ev {
ReadlineEvent::Line(l) => cloned_handler.emit((emit_frame.clone(), l)),
_ => {}
}
}
}
},
});
term.onData(&data_closure);
new_data
.retained_closures
.replace(Some((data_closure, resize_closure)));
new_frames.insert(frame.clone(), new_data.clone());
web_sys::console::log_2(
&"Setting frames to have length: ".into(),
&new_frames.iter().count().into(),
);
frames.set.emit(new_frames.into());
new_data
}
@ -85,13 +181,32 @@ fn get_or_make_term_frame(
pub struct TermViewProps {
pub terminal: TermFrame,
pub frames: RegisteredTermFrameLens,
pub handler: Callback<(TermFrame, String), ()>,
}
#[function_component(TermView)]
pub fn term_view(props: &TermViewProps) -> Html {
let term = get_or_make_term_frame(&props.terminal, &props.frames);
let term = get_or_make_term_frame(&props.terminal, &props.frames, &props.handler);
term.fit.fit();
html! {
{Html::VRef(term.node.clone())}
}
}
pub fn echo_to_term_frame(
frames: &RegisteredTermFrameLens,
frame_id: &TermFrame,
message: &str,
) -> Result<(), &'static str> {
let frame_val = frames.get.emit(());
let frame: &TermFrameData = frame_val.get(frame_id).ok_or_else(|| {
web_sys::console::log_3(
&"Attempt to echo to frame that doesn't exist.".into(),
&frame_id.0.into(),
&frame_val.iter().count().into(),
);
"Attempt to echo to frame that doesn't exist."
})?;
frame.term.write(message);
Ok(())
}