From 291b917970cd1d764258eeb22fbd1884dfac7cc1 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 19 Jul 2025 07:55:56 +0300 Subject: [PATCH 1/2] this one at least compiles --- Cargo.lock | 381 ++++++++++++++++++++++++++++----------- Cargo.toml | 2 +- dsl/src/lib.rs | 115 +++++++----- input/src/input_dsl.rs | 98 +++++----- proc/src/proc_command.rs | 4 +- proc/src/proc_expose.rs | 10 +- proc/src/proc_view.rs | 4 +- 7 files changed, 416 insertions(+), 198 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c5c30d..08f6979 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "allocator-api2" @@ -40,9 +40,9 @@ checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" @@ -56,7 +56,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -86,15 +86,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "by_address" @@ -110,18 +110,18 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "castaway" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" dependencies = [ "rustversion", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "compact_str" @@ -154,6 +154,31 @@ name = "const_panic" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" +dependencies = [ + "const_panic_proc_macros", + "typewit", +] + +[[package]] +name = "const_panic_proc_macros" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c5b80a80fb52c1a6ca02e3cd829a76b472ff0a15588196fd8da95221f0c1e4b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "crossbeam-utils" @@ -177,6 +202,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix 1.0.8", + "signal-hook", + "signal-hook-mio", + "winapi", +] + [[package]] name = "crossterm_winapi" version = "0.9.1" @@ -221,6 +264,36 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + [[package]] name = "either" version = "1.15.0" @@ -241,12 +314,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -281,7 +354,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -304,9 +377,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -333,9 +406,9 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" [[package]] name = "instability" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" dependencies = [ "darling", "indoc", @@ -413,9 +486,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "linux-raw-sys" @@ -430,10 +503,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] -name = "lock_api" -version = "0.4.12" +name = "litrs" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -456,29 +535,29 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -515,7 +594,7 @@ dependencies = [ "fast-srgb8", "palette_derive", "phf", - "rand", + "rand 0.8.5", ] [[package]] @@ -532,9 +611,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -542,15 +621,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -576,7 +655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -621,17 +700,17 @@ dependencies = [ [[package]] name = "proptest" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", "bitflags", "lazy_static", "num-traits", - "rand", - "rand_chacha", + "rand 0.9.1", + "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -652,15 +731,15 @@ dependencies = [ [[package]] name = "quanta" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" dependencies = [ "crossbeam-utils", "libc", "once_cell", "raw-cpuid", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -682,9 +761,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -693,8 +772,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -704,7 +793,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -717,12 +816,21 @@ dependencies = [ ] [[package]] -name = "rand_xorshift" -version = "0.3.0" +name = "rand_core" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "rand_core", + "getrandom 0.3.3", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", ] [[package]] @@ -734,7 +842,7 @@ dependencies = [ "bitflags", "cassowary", "compact_str", - "crossterm", + "crossterm 0.28.1", "indoc", "instability", "itertools 0.13.0", @@ -757,9 +865,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags", ] @@ -772,9 +880,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustix" @@ -791,22 +899,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rusty-fork" @@ -870,9 +978,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "static_assertions" @@ -910,9 +1018,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -921,14 +1029,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix 1.0.8", "windows-sys 0.59.0", ] @@ -936,7 +1044,7 @@ dependencies = [ name = "tengri" version = "0.13.0" dependencies = [ - "crossterm", + "crossterm 0.29.0", "tengri", "tengri_core", "tengri_dsl", @@ -954,6 +1062,7 @@ version = "0.13.0" name = "tengri_dsl" version = "0.13.0" dependencies = [ + "const_panic", "itertools 0.14.0", "konst", "proptest", @@ -1000,11 +1109,11 @@ version = "0.13.0" dependencies = [ "atomic_float", "better-panic", - "crossterm", + "crossterm 0.29.0", "konst", "palette", "quanta", - "rand", + "rand 0.8.5", "ratatui", "tengri", "tengri_core", @@ -1091,6 +1200,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "wait-timeout" version = "0.2.1" @@ -1102,9 +1217,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -1204,22 +1319,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", ] [[package]] @@ -1228,14 +1343,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -1244,48 +1375,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -1297,18 +1476,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index fa89dab..a204087 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ edition = "2024" atomic_float = { version = "1" } better-panic = { version = "0.3.0" } const_panic = { version = "0.2.12", features = [ "derive" ] } -crossterm = { version = "0.28.1" } +crossterm = { version = "0.29.0" } heck = { version = "0.5" } itertools = { version = "0.14.0" } konst = { version = "0.3.16", features = [ "rust_1_83" ] } diff --git a/dsl/src/lib.rs b/dsl/src/lib.rs index 437f4de..703f8ba 100644 --- a/dsl/src/lib.rs +++ b/dsl/src/lib.rs @@ -44,13 +44,16 @@ impl Val { T::Str: for<'a> From<&'a Str>, T::Exp: for<'a> From<&'a Exp> { - match self { Val::Nil => Val::Nil, - Val::Num(u) => Val::Num(*u), - Val::Sym(s) => Val::Sym(s.into()), - Val::Key(s) => Val::Key(s.into()), - Val::Str(s) => Val::Str(s.into()), - Val::Exp(d, x) => Val::Exp(*d, x.into()), - Val::Error(e) => Val::Error(*e) } } + match self { + Val::Nil => Val::Nil, + Val::Num(u) => Val::Num(*u), + Val::Sym(s) => Val::Sym(s.into()), + Val::Key(s) => Val::Key(s.into()), + Val::Str(s) => Val::Str(s.into()), + Val::Exp(d, x) => Val::Exp(*d, x.into()), + Val::Error(e) => Val::Error(*e) + } + } } /// The expression representation for a [Dsl] implementation. /// [Cst] uses [CstIter]. [Ast] uses [VecDeque]. @@ -96,22 +99,6 @@ impl Token { Self { value: self.value, ..*self } } } -impl<'s> CstToken<'s> { - pub const fn slice (&self) -> &str { - str_range(self.source, self.start, self.end()) } - pub const fn slice_exp (&self) -> &str { - str_range(self.source, self.start.saturating_add(1), self.end()) } - pub const fn grow (&mut self) -> &mut Self { - let max_length = self.source.len().saturating_sub(self.start); - self.length = self.length + 1; - if self.length > max_length { self.length = max_length } - self - } - pub const fn grow_exp (&'s mut self, depth: isize, source: &'s str) -> &mut Self { - self.value = Val::Exp(depth, Cst(CstConstIter(source))); - self - } -} /// To the [Dsl], a token is equivalent to its `value` field. impl Dsl for Token { type Str = Str; type Exp = Exp; @@ -126,16 +113,17 @@ pub trait Dsl: Clone + Debug { /// Request the top-level DSL [Val]ue. /// May perform cloning or parsing. fn dsl (&self) -> Val; - fn err (&self) -> Option {self.dsl().err()} - fn nil (&self) -> bool {self.dsl().nil()} - fn num (&self) -> Option {self.dsl().num()} - fn sym (&self) -> Option {self.dsl().sym()} - fn key (&self) -> Option {self.dsl().key()} - fn str (&self) -> Option {self.dsl().str()} - fn exp (&self) -> Option {self.dsl().exp()} + fn err (&self) -> Option {self.dsl().err()} + fn nil (&self) -> bool {self.dsl().nil()} + fn num (&self) -> Option {self.dsl().num()} + fn sym (&self) -> Option {self.dsl().sym()} + fn key (&self) -> Option {self.dsl().key()} + fn str (&self) -> Option {self.dsl().str()} + fn exp (&self) -> Option {self.dsl().exp()} fn exp_depth (&self) -> Option {self.dsl().exp_depth()} fn exp_head (&self) -> Val {self.dsl().exp_head()} - fn exp_tail (&self) -> Val {self.dsl().exp_tail()} + fn exp_tail (&self) -> Self::Exp {self.dsl().exp_tail()} + fn exp_each (&self, f: impl Fn(&Self) -> Usually<()>) -> Usually<()> { todo!() } } /// The most basic implementor of the [Dsl] trait. impl Dsl for Val { @@ -146,25 +134,71 @@ impl Dsl for Val { /// by cloning source slices into owned ([Arc]) string slices. #[derive(Debug, Clone, Default, PartialEq)] pub struct Ast(Arc, Ast>>>>); +pub type AstVal = Val, Ast>; +pub type AstToken = Token, Ast>; impl Dsl for Ast { type Str = Arc; type Exp = Ast; fn dsl (&self) -> Val, Ast> { Val::Exp(0, Ast(self.0.clone())) } } +impl<'s> From<&'s str> for Ast { + fn from (source: &'s str) -> Self { + let source: Arc = source.into(); + Self(CstIter(CstConstIter(source.as_ref())) + .map(|token|Arc::new(Token { + source: source.clone(), + start: token.start, + length: token.length, + value: match token.value { + Val::Nil => Val::Nil, + Val::Num(u) => Val::Num(u), + Val::Sym(s) => Val::Sym(s.into()), + Val::Key(s) => Val::Key(s.into()), + Val::Str(s) => Val::Str(s.into()), + Val::Exp(d, x) => Val::Exp(d, x.into()), + Val::Error(e) => Val::Error(e.into()) + }, + })) + .collect::>() + .into()) + } +} +impl<'s> From> for Ast { + fn from (cst: Cst<'s>) -> Self { + let mut tokens: VecDeque<_> = Default::default(); + Self(tokens.into()) + } +} /// The concrete syntax tree (CST) implements zero-copy /// parsing of the DSL from a string reference. CST items /// preserve info about their location in the source. /// CST stores strings as source references and expressions as [CstIter] instances. #[derive(Debug, Copy, Clone, Default, PartialEq)] -pub struct Cst<'s>(pub CstConstIter<'s>); +pub struct Cst<'s>(pub CstIter<'s>); pub type CstVal<'s> = Val<&'s str, Cst<'s>>; pub type CstToken<'s> = Token<&'s str, Cst<'s>>; +impl<'s> CstToken<'s> { + pub const fn slice (&self) -> &str { + str_range(self.source, self.start, self.end()) } + pub const fn slice_exp (&self) -> &str { + str_range(self.source, self.start.saturating_add(1), self.end()) } + pub const fn grow (&mut self) -> &mut Self { + let max_length = self.source.len().saturating_sub(self.start); + self.length = self.length + 1; + if self.length > max_length { self.length = max_length } + self + } + pub const fn grow_exp (&'s mut self, depth: isize, source: &'s str) -> &mut Self { + self.value = Val::Exp(depth, Cst(CstIter(CstConstIter(source)))); + self + } +} impl<'s> Dsl for Cst<'s> { type Str = &'s str; type Exp = Cst<'s>; fn dsl (&self) -> Val { Val::Exp(0, Cst(self.0)) } } impl<'s> From<&'s str> for Cst<'s> { fn from (source: &'s str) -> Self { - Self(CstConstIter(source)) + Self(CstIter(CstConstIter(source))) } } /// DSL-specific error codes. @@ -250,7 +284,7 @@ pub const fn peek <'s> (mut value: CstVal<'s>, source: &'s str) -> CstToken<'s> start = i; length = 1; if is_exp_start(c) { - value = Exp(1, Cst(CstConstIter(str_range(source, i, i+1)))); + value = Exp(1, Cst(CstIter(CstConstIter(str_range(source, i, i+1))))); } else if is_str_start(c) { value = Str(str_range(source, i, i+1)); } else if is_sym_start(c) { @@ -289,17 +323,14 @@ pub const fn peek <'s> (mut value: CstVal<'s>, source: &'s str) -> CstToken<'s> } } else if let Exp(depth, exp) = value { if depth == 0 { - value = Exp(0, Cst(CstConstIter(str_range(source, start, start + length)))); + value = Exp(0, Cst(CstIter(CstConstIter(str_range(source, start, start + length))))); break } length += 1; - if c == ')' { - value = Exp(depth-1, Cst(CstConstIter(str_range(source, start, start + length)))); - } else if c == '(' { - value = Exp(depth+1, Cst(CstConstIter(str_range(source, start, start + length)))); - } else { - value = Exp(depth, Cst(CstConstIter(str_range(source, start, start + length)))); - } + value = Exp( + if c == ')' { depth-1 } else if c == '(' { depth+1 } else { depth }, + Cst(CstIter(CstConstIter(str_range(source, start, start + length)))) + ); } else if let Num(m) = value { if is_num_end(c) { break diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index 49fb270..5032319 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -4,24 +4,64 @@ use crate::*; /// Each contained layer defines a mapping from input event to command invocation /// over a given state. Furthermore, each layer may have an associated cond, /// so that only certain layers are active at a given time depending on state. -#[derive(Debug)] pub struct InputMap( - /// Map of input event (key combination) to +/// +/// When a key is pressed, the bindings for it are checked in sequence. +/// When the first non-conditional or true conditional binding is executed, +/// that binding's value is returned. +#[derive(Debug)] pub struct EventMap( + /// Map of each event (e.g. key combination) to /// all command expressions bound to it by /// all loaded input layers. - pub BTreeMap>> + pub EventBindings ); -impl Default for InputMap { - fn default () -> Self { - Self(Default::default()) - } -} -#[derive(Debug, Default)] pub struct InputBinding { +type EventBindings = BTreeMap>>; +/// An input binding. +#[derive(Debug, Default)] pub struct Binding { condition: Option, command: D, description: Option>, source: Option>, } -impl<'s, I: Debug + Ord, D: Dsl + From>> InputMap { +impl Default for EventMap { + fn default () -> Self { + Self(Default::default()) + } +} +impl> + Clone> EventMap { + /// Create input layer collection from DSL. + pub fn new (dsl: Ast) -> Usually { + use Val::*; + let mut input_map: EventBindings = Default::default(); + match dsl.exp_head() { + Str(path) => { + let path = PathBuf::from(path.as_ref() as &str); + for (key, val) in Self::from_path(&path)?.0.into_iter() { + todo!("import {path:?} {key:?} {val:?}"); + let key: E = key.into(); + if !input_map.contains_key(&key) { + input_map.insert(key, vec![]); + } + } + }, + Sym(s) => { + let key: Arc = s.as_ref().into(); + let key: E = key.into(); + if !input_map.contains_key(&key) { + input_map.insert(key.clone(), vec![]); + } + todo!("binding {s:?} {:?}", dsl.exp_tail()); + }, + Key(k) if k.as_ref() == "if" => { + todo!("conditional binding {:?}", dsl.exp_tail()); + }, + _ => return Err(format!("invalid form in keymap: {dsl:?}").into()) + } + Ok(Self(input_map)) + } + /// Create input layer collection from string. + pub fn from_source (source: &str) -> Usually { + Self::new(Ast::from(source)) + } /// Create input layer collection from path to text file. pub fn from_path > (path: P) -> Usually { if !exists(path.as_ref())? { @@ -29,41 +69,9 @@ impl<'s, I: Debug + Ord, D: Dsl + From>> InputMap { } Self::from_source(read_and_leak(path)?) } - /// Create input layer collection from string. - pub fn from_source (source: &'s str) -> Usually { - Self::from_dsl(D::from(Cst(CstConstIter(source)))) - } - /// Create input layer collection from DSL. - pub fn from_dsl (dsl: D) -> Usually { - use Val::*; - let mut input_map: BTreeMap>> = Default::default(); - match dsl.exp_head() { - Str(path) => { - let path = PathBuf::from(path.as_ref()); - for (key, val) in InputMap::::from_path(&path)?.0.into_iter() { - todo!("import {path:?} {key:?} {val:?}"); - if !input_map.contains_key(&key) { - input_map.insert(key, vec![]); - } - } - }, - Sym(sym) => { - //let key: I = sym.into(); - //if !input_map.contains_key(&key) { - //input_map.insert(key, vec![]); - //} - todo!("binding {sym:?} {:?}", dsl.exp_tail()); - }, - Key(s) if s.as_ref() == "if" => { - todo!("conditional binding {:?}", dsl.exp_tail()); - }, - _ => return Err(format!("invalid form in keymap: {dsl:?}").into()) - } - Ok(Self(input_map)) - } /// Evaluate the active layers for a given state, /// returning the command to be executed, if any. - pub fn handle (&self, state: &mut S, input: I) -> Perhaps where + pub fn handle (&self, state: &mut S, event: Ast) -> Perhaps where S: DslInto + DslInto, O: Command { @@ -76,7 +84,7 @@ impl<'s, I: Debug + Ord, D: Dsl + From>> InputMap { //} //if matches //&& let Some(exp) = bind.val().exp_head() - //&& input.dsl_into(exp, ||format!("InputMap: input.eval(binding) failed").into())? + //&& input.dsl_into(exp, ||format!("EventMap: input.eval(binding) failed").into())? //&& let Some(command) = state.try_dsl_into(exp)? { //return Ok(Some(command)) //} @@ -112,7 +120,7 @@ impl<'s, I: Debug + Ord, D: Dsl + From>> InputMap { //let mut keys = iter.unwrap(); - //let mut map = InputMap::default(); + //let mut map = EventMap::default(); //while let Some(token) = keys.next() { //if let Value::Exp(_, mut exp) = token.value { //let next = exp.next(); diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index 132d867..36b25d5 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -93,7 +93,7 @@ impl ToTokens for CommandDef { out }; write_quote(quote! { - Some(::tengri::dsl::Token { value: ::tengri::dsl::DslVal::Key(#key), .. }) => { + Some(::tengri::dsl::Token { value: ::tengri::dsl::Val::Key(#key), .. }) => { let mut words = words.clone(); Some(#command_enum::#variant) }, @@ -154,7 +154,7 @@ impl ToTokens for CommandDef { state: &#state, value: &impl ::tengri::dsl::Dsl ) -> Perhaps { - use ::tengri::dsl::DslVal::*; + use ::tengri::dsl::Val::*; todo!()//Ok(match token { #(#matchers)* _ => None }) } } diff --git a/proc/src/proc_expose.rs b/proc/src/proc_expose.rs index ca5c9e7..3514696 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -81,7 +81,7 @@ impl ExposeImpl { /// Generated by [tengri_proc::expose]. impl ::tengri::dsl::FromDsl<#state> for #t { fn try_from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { - Ok(Some(match dsl.val() { + Ok(Some(match dsl.dsl() { #arms _ => { return Ok(None) } })) @@ -93,7 +93,7 @@ impl ExposeImpl { let formatted_type = format!("{}", quote! { #t }); if &formatted_type == "bool" { return quote! { - ::tengri::dsl::DslVal::Sym(s) => match s.as_ref() { + ::tengri::dsl::Val::Sym(s) => match s.as_ref() { ":true" => true, ":false" => false, #variants @@ -110,16 +110,16 @@ impl ExposeImpl { Span::call_site() ); return quote! { - ::tengri::dsl::DslVal::Num(n) => TryInto::<#t>::try_into(n) + ::tengri::dsl::Val::Num(n) => TryInto::<#t>::try_into(n) .unwrap_or_else(|_|panic!(#num_err)), - ::tengri::dsl::DslVal::Sym(s) => match s.as_ref() { + ::tengri::dsl::Val::Sym(s) => match s.as_ref() { #variants _ => { return Ok(None) } }, } } return quote! { - ::tengri::dsl::DslVal::Sym(s) => match s.as_ref() { + ::tengri::dsl::Val::Sym(s) => match s.as_ref() { #variants _ => { return Ok(None) } }, diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 0f377de..5e11c0f 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -76,7 +76,7 @@ impl ViewDef { fn builtins (&self) -> impl ToTokens { let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; let builtins = builtins_with_boxes_output(quote! { #output }).map(|builtin|quote! { - ::tengri::dsl::DslVal::Exp(_, expr) => return Ok(Some( + ::tengri::dsl::Val::Exp(_, expr) => return Ok(Some( #builtin::from_dsl(self, expr, ||Box::new("failed to load builtin".into()))? .boxed() )), @@ -89,7 +89,7 @@ impl ViewDef { let exposed = exposed.iter().map(|(key, value)|write_quote(quote! { #key => return Ok(Some(self.#value().boxed())), })); - quote! { ::tengri::dsl::DslVal::Sym(key) => match key.as_ref() { #(#exposed)* } } + quote! { ::tengri::dsl::Val::Sym(key) => match key.as_ref() { #(#exposed)* } } } } From 7b67d29c012f2d39a5d9d2fcd840417c90256503 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 19 Jul 2025 08:42:08 +0300 Subject: [PATCH 2/2] simplify --- dsl/src/lib.rs | 8 +- output/src/lib.rs | 3 + output/src/ops.rs | 383 ++++++++++++++++++++++++++++++-- output/src/ops/_collect.rs | 23 -- output/src/ops/_reduce.rs | 93 -------- output/src/ops/map.rs | 150 ------------- output/src/ops/memo.rs | 30 --- output/src/ops/stack.rs | 109 --------- output/src/ops/thunk.rs | 69 ------ output/src/space.rs | 279 ++++++++++++++++++++++- output/src/space/area.rs | 98 -------- output/src/space/coordinate.rs | 31 --- output/src/space/direction.rs | 22 -- output/src/space/measure.rs | 91 -------- output/src/space/size.rs | 40 ---- proc/src/proc_view.rs | 6 +- tui/src/tui_engine.rs | 4 +- tui/src/tui_engine/tui_input.rs | 136 ++++++++++-- tui/src/tui_engine/tui_keys.rs | 92 -------- 19 files changed, 769 insertions(+), 898 deletions(-) delete mode 100644 output/src/ops/_collect.rs delete mode 100644 output/src/ops/_reduce.rs delete mode 100644 output/src/ops/map.rs delete mode 100644 output/src/ops/memo.rs delete mode 100644 output/src/ops/stack.rs delete mode 100644 output/src/ops/thunk.rs delete mode 100644 output/src/space/area.rs delete mode 100644 output/src/space/coordinate.rs delete mode 100644 output/src/space/direction.rs delete mode 100644 output/src/space/measure.rs delete mode 100644 output/src/space/size.rs delete mode 100644 tui/src/tui_engine/tui_keys.rs diff --git a/dsl/src/lib.rs b/dsl/src/lib.rs index 703f8ba..36fc266 100644 --- a/dsl/src/lib.rs +++ b/dsl/src/lib.rs @@ -5,6 +5,7 @@ extern crate const_panic; use const_panic::{concat_panic, PanicFmt}; pub(crate) use ::tengri_core::*; +use std::ops::Deref; pub(crate) use std::error::Error; pub(crate) use std::fmt::Debug; pub(crate) use std::sync::Arc; @@ -61,8 +62,11 @@ pub trait DslExp: PartialEq + Clone + Default + Debug + Dsl {} impl DslExp for T {} /// The string representation for a [Dsl] implementation. /// [Cst] uses `&'s str`. [Ast] uses `Arc`. -pub trait DslStr: PartialEq + Clone + Default + Debug + AsRef + std::ops::Deref {} -impl + std::ops::Deref> DslStr for T {} +pub trait DslStr: PartialEq + Clone + Default + Debug + AsRef + Deref { + fn as_str (&self) -> &str { self.as_ref() } + fn as_arc (&self) -> Arc { self.as_ref().into() } +} +impl + Deref> DslStr for Str {} impl> Val { pub const fn err (&self) -> Option {match self{Val::Error(e)=>Some(*e), _=>None}} pub const fn nil (&self) -> bool {match self{Val::Nil=>true, _=>false}} diff --git a/output/src/lib.rs b/output/src/lib.rs index 45abdda..334c418 100644 --- a/output/src/lib.rs +++ b/output/src/lib.rs @@ -3,6 +3,9 @@ #![feature(impl_trait_in_assoc_type)] pub(crate) use std::marker::PhantomData; +pub(crate) use std::fmt::{Debug, Display}; +pub(crate) use std::ops::{Add, Sub, Mul, Div}; +pub(crate) use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}}; pub(crate) use tengri_core::*; #[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; diff --git a/output/src/ops.rs b/output/src/ops.rs index 976ab51..6b6f401 100644 --- a/output/src/ops.rs +++ b/output/src/ops.rs @@ -48,32 +48,21 @@ use crate::*; use Direction::*; +use std::marker::PhantomData; +use std::sync::{Arc, RwLock}; -mod map; pub use self::map::*; -mod memo; pub use self::memo::*; -mod stack; pub use self::stack::*; -mod thunk; pub use self::thunk::*; - -/// Renders multiple things on top of each other, -#[macro_export] macro_rules! lay { - ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }} -} - +/// Stack things on top of each other, +#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => + {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }}); /// Stack southward. -#[macro_export] macro_rules! col { - ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }}; -} - +#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) => + {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }}); /// Stack northward. -#[macro_export] macro_rules! col_up { - ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }} -} - +#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) => + {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }}); /// Stack eastward. -#[macro_export] macro_rules! row { - ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }}; -} - +#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => + {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }}); /// Show an item only when a condition is true. pub struct When(pub bool, pub A); impl When { @@ -686,3 +675,353 @@ transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{ //_ => None //}) } + +/// Lazily-evaluated [Render]able. +pub struct Thunk, F: Fn()->T>( + PhantomData, + F +); +impl, F: Fn()->T> Thunk { + pub const fn new (thunk: F) -> Self { + Self(PhantomData, thunk) + } +} +impl, F: Fn()->T> Content for Thunk { + fn content (&self) -> impl Render { (self.1)() } +} + +pub struct ThunkBox( + PhantomData, + BoxBox>>, +); +impl ThunkBox { + pub const fn new (thunk: BoxBox>>) -> Self { + Self(PhantomData, thunk) + } +} +impl Content for ThunkBox { + fn content (&self) -> impl Render { (&self.1)() } +} +impl FromBox>>> for ThunkBox { + fn from (f: BoxBox>>) -> Self { + Self(PhantomData, f) + } +} +//impl<'a, E: Output, F: Fn()->Box + 'a> + 'a> From for ThunkBox<'a, E> { + //fn from (f: F) -> Self { + //Self(Default::default(), Box::new(f)) + //} +//} + +pub struct ThunkRender(PhantomData, F); +impl ThunkRender { + pub fn new (render: F) -> Self { Self(PhantomData, render) } +} +impl Content for ThunkRender { + fn render (&self, to: &mut E) { (self.1)(to) } +} + +pub struct ThunkLayout< + E: Output, + F1: Fn(E::Area)->E::Area, + F2: Fn(&mut E) +>( + PhantomData, + F1, + F2 +); +implE::Area, F2: Fn(&mut E)> ThunkLayout { + pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) } +} +impl Content for ThunkLayout +where + E: Output, + F1: Fn(E::Area)->E::Area, + F2: Fn(&mut E) +{ + fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) } + fn render (&self, to: &mut E) { (self.2)(to) } +} + +#[derive(Debug, Default)] pub struct Memo { + pub value: T, + pub view: Arc> +} + +impl Memo { + pub fn new (value: T, view: U) -> Self { + Self { value, view: Arc::new(view.into()) } + } + pub fn update ( + &mut self, + newval: T, + render: impl Fn(&mut U, &T, &T)->R + ) -> Option { + if newval != self.value { + let result = render(&mut*self.view.write().unwrap(), &newval, &self.value); + self.value = newval; + return Some(result); + } + None + } +} + +/// Clear a pre-allocated buffer, then write into it. +#[macro_export] macro_rules! rewrite { + ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } +} + +pub struct Stack { + __: PhantomData, + direction: Direction, + callback: F +} +impl Stack { + pub fn new (direction: Direction, callback: F) -> Self { + Self { direction, callback, __: Default::default(), } + } + pub fn north (callback: F) -> Self { + Self::new(North, callback) + } + pub fn south (callback: F) -> Self { + Self::new(South, callback) + } + pub fn east (callback: F) -> Self { + Self::new(East, callback) + } + pub fn west (callback: F) -> Self { + Self::new(West, callback) + } +} +impl)->())->()> Content for Stack { + fn layout (&self, to: E::Area) -> E::Area { + let mut x = to.x(); + let mut y = to.y(); + let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); + let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); + (self.callback)(&mut move |component: &dyn Render|{ + let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); + match self.direction { + South => { + y = y.plus(h); + h_used = h_used.plus(h); + h_remaining = h_remaining.minus(h); + w_used = w_used.max(w); + }, + East => { + x = x.plus(w); + w_used = w_used.plus(w); + w_remaining = w_remaining.minus(w); + h_used = h_used.max(h); + }, + North | West => { + todo!() + }, + _ => unreachable!(), + } + }); + match self.direction { + North | West => { + todo!() + }, + South | East => { + [to.x(), to.y(), w_used.into(), h_used.into()].into() + }, + _ => unreachable!(), + } + } + fn render (&self, to: &mut E) { + let mut x = to.x(); + let mut y = to.y(); + let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); + let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); + (self.callback)(&mut move |component: &dyn Render|{ + let layout = component.layout([x, y, w_remaining, h_remaining].into()); + match self.direction { + South => { + y = y.plus(layout.h()); + h_remaining = h_remaining.minus(layout.h()); + h_used = h_used.plus(layout.h()); + to.place(layout, component); + }, + East => { + x = x.plus(layout.w()); + w_remaining = w_remaining.minus(layout.w()); + w_used = w_used.plus(layout.h()); + to.place(layout, component); + }, + North | West => { + todo!() + }, + _ => unreachable!() + } + }); + } +} + +/*Stack::down(|add|{ + let mut i = 0; + for (_, name) in self.dirs.iter() { + if i >= self.scroll { + add(&Tui::bold(i == self.index, name.as_str()))?; + } + i += 1; + } + for (_, name) in self.files.iter() { + if i >= self.scroll { + add(&Tui::bold(i == self.index, name.as_str()))?; + } + i += 1; + } + add(&format!("{}/{i}", self.index))?; + Ok(()) +}));*/ + +/// Renders items from an iterator. +pub struct Map +where + I: Iterator + Send + Sync, + F: Fn() -> I + Send + Sync, +{ + __: PhantomData<(E, B)>, + /// Function that returns iterator over stacked components + get_iter: F, + /// Function that returns each stacked component + get_item: G, +} + +impl<'a, E, A, B, I, F, G> Map where + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a, +{ + pub const fn new (get_iter: F, get_item: G) -> Self { + Self { + __: PhantomData, + get_iter, + get_item + } + } +} + +impl<'a, E, A, B, I, F> Map>>>, I, F, fn(A, usize)->B> +where + E: Output, + B: Render, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a +{ + pub const fn east ( + size: E::Unit, + get_iter: F, + get_item: impl Fn(A, usize)->B + Send + Sync + ) -> Map< + E, A, + Push>>, + I, F, + impl Fn(A, usize)->Push>> + Send + Sync + > { + Map { + __: PhantomData, + get_iter, + get_item: move |item: A, index: usize|{ + // FIXME: multiply + let mut push: E::Unit = E::Unit::from(0u16); + for _ in 0..index { + push = push + size; + } + Push::x(push, Align::w(Fixed::x(size, get_item(item, index)))) + } + } + } + + pub const fn south ( + size: E::Unit, + get_iter: F, + get_item: impl Fn(A, usize)->B + Send + Sync + ) -> Map< + E, A, + Push>>, + I, F, + impl Fn(A, usize)->Push>> + Send + Sync + > where + E: Output, + B: Render, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a + { + Map { + __: PhantomData, + get_iter, + get_item: move |item: A, index: usize|{ + // FIXME: multiply + let mut push: E::Unit = E::Unit::from(0u16); + for _ in 0..index { + push = push + size; + } + Push::y(push, Align::n(Fixed::y(size, get_item(item, index)))) + } + } + } +} + +impl<'a, E, A, B, I, F, G> Content for Map where + E: Output, + B: Render, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a, + G: Fn(A, usize)->B + Send + Sync +{ + fn layout (&self, area: E::Area) -> E::Area { + let Self { get_iter, get_item, .. } = self; + let mut index = 0; + let [mut min_x, mut min_y] = area.center(); + let [mut max_x, mut max_y] = area.center(); + for item in get_iter() { + let [x,y,w,h] = get_item(item, index).layout(area).xywh(); + min_x = min_x.min(x.into()); + min_y = min_y.min(y.into()); + max_x = max_x.max((x + w).into()); + max_y = max_y.max((y + h).into()); + index += 1; + } + let w = max_x - min_x; + let h = max_y - min_y; + //[min_x.into(), min_y.into(), w.into(), h.into()].into() + area.center_xy([w.into(), h.into()].into()).into() + } + fn render (&self, to: &mut E) { + let Self { get_iter, get_item, .. } = self; + let mut index = 0; + let area = Content::layout(self, to.area()); + for item in get_iter() { + let item = get_item(item, index); + //to.place(area.into(), &item); + to.place(item.layout(area), &item); + index += 1; + } + } +} + +#[inline] pub fn map_south( + item_offset: O::Unit, + item_height: O::Unit, + item: impl Content +) -> impl Content { + Push::y(item_offset, Fixed::y(item_height, Fill::x(item))) +} + +#[inline] pub fn map_south_west( + item_offset: O::Unit, + item_height: O::Unit, + item: impl Content +) -> impl Content { + Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item)))) +} + +#[inline] pub fn map_east( + item_offset: O::Unit, + item_width: O::Unit, + item: impl Content +) -> impl Content { + Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item)))) +} diff --git a/output/src/ops/_collect.rs b/output/src/ops/_collect.rs deleted file mode 100644 index 5559afe..0000000 --- a/output/src/ops/_collect.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Groupings of elements. -use crate::*; - -/// A function or closure that emits renderables. -pub trait Collector: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)) {} - -/// Any function or closure that emits renderables for the given engine matches [CollectCallback]. -impl Collector for F -where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)) {} - -pub trait Render { - fn area (&self, to: E::Area) -> E::Area; - fn render (&self, to: &mut E::Output); -} - -impl> Render for C { - fn area (&self, to: E::Area) -> E::Area { - Content::area(self, to) - } - fn render (&self, to: &mut E::Output) { - Content::render(self, to) - } -} diff --git a/output/src/ops/_reduce.rs b/output/src/ops/_reduce.rs deleted file mode 100644 index dfcc00e..0000000 --- a/output/src/ops/_reduce.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::*; - -pub struct Reduce(pub PhantomData, pub F, pub G) where - A: Send + Sync, B: Send + Sync, - I: Iterator + Send + Sync, - F: Fn() -> I + Send + Sync, - G: Fn(A, B, usize)->A + Send + Sync; - -impl Reduce where - A: Send + Sync, B: Send + Sync, - I: Iterator + Send + Sync, - F: Fn() -> I + Send + Sync, - G: Fn(A, B, usize)->A + Send + Sync -{ - pub const fn new (f: F, g: G) -> Self { Self(Default::default(), f, g) } -} - -impl Content for Reduce where - A: Send + Sync, B: Send + Sync, - I: Iterator + Send + Sync, - F: Fn() -> I + Send + Sync, - G: Fn(A, B, usize)->A + Send + Sync -{ - fn content (&self) -> impl Render { - } -} - -/* - - //pub fn reduce (iterator: I, callback: F) -> Reduce where - //E: Output, - //I: Iterator + Send + Sync, - //R: Render, - //F: Fn(R, T, usize) -> R + Send + Sync - //{ - //Reduce(Default::default(), iterator, callback) - //} -pub struct Reduce(PhantomData<(E, R)>, I, F) where - E: Output, - I: Iterator + Send + Sync, - R: Render, - F: Fn(R, T, usize) -> R + Send + Sync; -impl Content for Reduce where - E: Output, - I: Iterator + Send + Sync, - R: Render, - F: Fn(R, T, usize) -> R + Send + Sync -{ - fn render (&self, to: &mut E) { - todo!() - } -} -*/ - -//macro_rules! define_ops { - //($Trait:ident<$E:ident:$Output:path> { $( - //$(#[$attr:meta $($attr_args:tt)*])* - //( - //$fn:ident - //$(<$($G:ident$(:$Gen:path)?, )+>)? - //$Op:ident - //($($arg:ident:$Arg:ty),*) - //) - //)* }) => { - //impl<$E: $Output> $Trait for E {} - //pub trait $Trait<$E: $Output> { - //$( - //$(#[$attr $($attr_args)*])* - //fn $fn $(<$($G),+>)? - //($($arg:$Arg),*)-> $Op<$($(, $G)+)?> - //$(where $($G: $($Gen + Send + Sync)?),+)? - //{ $Op($($arg),*) } - //)* - //} - //} -//} - -//define_ops! { - //Layout { - //(when ,> - //When(cond: bool, item: A)) - ///// When `cond` is `true`, render `a`, otherwise render `b`. - //(either , B: Render,> - //Either(cond: bool, a: A, b: B)) - ///// If `opt` is `Some(T)` renders `cb(t)`, otherwise nothing. - //(opt B, B: Render,> - //Opt(option: Option, cb: F)) - ///// Maps items of iterator through callback. - //(map , I: Iterator, F: Fn() -> I, G: Fn(A, usize)->B,> - //Map(get_iterator: F, callback: G)) - //} -//} - diff --git a/output/src/ops/map.rs b/output/src/ops/map.rs deleted file mode 100644 index d124569..0000000 --- a/output/src/ops/map.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::*; - -/// Renders items from an iterator. -pub struct Map -where - I: Iterator + Send + Sync, - F: Fn() -> I + Send + Sync, -{ - __: PhantomData<(E, B)>, - /// Function that returns iterator over stacked components - get_iter: F, - /// Function that returns each stacked component - get_item: G, -} - -impl<'a, E, A, B, I, F, G> Map where - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a, -{ - pub const fn new (get_iter: F, get_item: G) -> Self { - Self { - __: PhantomData, - get_iter, - get_item - } - } -} - -impl<'a, E, A, B, I, F> Map>>>, I, F, fn(A, usize)->B> -where - E: Output, - B: Render, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a -{ - pub const fn east ( - size: E::Unit, - get_iter: F, - get_item: impl Fn(A, usize)->B + Send + Sync - ) -> Map< - E, A, - Push>>, - I, F, - impl Fn(A, usize)->Push>> + Send + Sync - > { - Map { - __: PhantomData, - get_iter, - get_item: move |item: A, index: usize|{ - // FIXME: multiply - let mut push: E::Unit = E::Unit::from(0u16); - for _ in 0..index { - push = push + size; - } - Push::x(push, Align::w(Fixed::x(size, get_item(item, index)))) - } - } - } - - pub const fn south ( - size: E::Unit, - get_iter: F, - get_item: impl Fn(A, usize)->B + Send + Sync - ) -> Map< - E, A, - Push>>, - I, F, - impl Fn(A, usize)->Push>> + Send + Sync - > where - E: Output, - B: Render, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a - { - Map { - __: PhantomData, - get_iter, - get_item: move |item: A, index: usize|{ - // FIXME: multiply - let mut push: E::Unit = E::Unit::from(0u16); - for _ in 0..index { - push = push + size; - } - Push::y(push, Align::n(Fixed::y(size, get_item(item, index)))) - } - } - } -} - -impl<'a, E, A, B, I, F, G> Content for Map where - E: Output, - B: Render, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a, - G: Fn(A, usize)->B + Send + Sync -{ - fn layout (&self, area: E::Area) -> E::Area { - let Self { get_iter, get_item, .. } = self; - let mut index = 0; - let [mut min_x, mut min_y] = area.center(); - let [mut max_x, mut max_y] = area.center(); - for item in get_iter() { - let [x,y,w,h] = get_item(item, index).layout(area).xywh(); - min_x = min_x.min(x.into()); - min_y = min_y.min(y.into()); - max_x = max_x.max((x + w).into()); - max_y = max_y.max((y + h).into()); - index += 1; - } - let w = max_x - min_x; - let h = max_y - min_y; - //[min_x.into(), min_y.into(), w.into(), h.into()].into() - area.center_xy([w.into(), h.into()].into()).into() - } - fn render (&self, to: &mut E) { - let Self { get_iter, get_item, .. } = self; - let mut index = 0; - let area = Content::layout(self, to.area()); - for item in get_iter() { - let item = get_item(item, index); - //to.place(area.into(), &item); - to.place(item.layout(area), &item); - index += 1; - } - } -} - -#[inline] pub fn map_south( - item_offset: O::Unit, - item_height: O::Unit, - item: impl Content -) -> impl Content { - Push::y(item_offset, Fixed::y(item_height, Fill::x(item))) -} - -#[inline] pub fn map_south_west( - item_offset: O::Unit, - item_height: O::Unit, - item: impl Content -) -> impl Content { - Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item)))) -} - -#[inline] pub fn map_east( - item_offset: O::Unit, - item_width: O::Unit, - item: impl Content -) -> impl Content { - Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item)))) -} diff --git a/output/src/ops/memo.rs b/output/src/ops/memo.rs deleted file mode 100644 index 8168f2d..0000000 --- a/output/src/ops/memo.rs +++ /dev/null @@ -1,30 +0,0 @@ -//use crate::*; -use std::sync::{Arc, RwLock}; - -#[derive(Debug, Default)] pub struct Memo { - pub value: T, - pub view: Arc> -} - -impl Memo { - pub fn new (value: T, view: U) -> Self { - Self { value, view: Arc::new(view.into()) } - } - pub fn update ( - &mut self, - newval: T, - render: impl Fn(&mut U, &T, &T)->R - ) -> Option { - if newval != self.value { - let result = render(&mut*self.view.write().unwrap(), &newval, &self.value); - self.value = newval; - return Some(result); - } - None - } -} - -/// Clear a pre-allocated buffer, then write into it. -#[macro_export] macro_rules! rewrite { - ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } -} diff --git a/output/src/ops/stack.rs b/output/src/ops/stack.rs deleted file mode 100644 index 59930ce..0000000 --- a/output/src/ops/stack.rs +++ /dev/null @@ -1,109 +0,0 @@ -use crate::*; -use Direction::*; - -pub struct Stack { - __: PhantomData, - direction: Direction, - callback: F -} -impl Stack { - pub fn new (direction: Direction, callback: F) -> Self { - Self { direction, callback, __: Default::default(), } - } - pub fn north (callback: F) -> Self { - Self::new(North, callback) - } - pub fn south (callback: F) -> Self { - Self::new(South, callback) - } - pub fn east (callback: F) -> Self { - Self::new(East, callback) - } - pub fn west (callback: F) -> Self { - Self::new(West, callback) - } -} -impl)->())->()> Content for Stack { - fn layout (&self, to: E::Area) -> E::Area { - let mut x = to.x(); - let mut y = to.y(); - let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); - let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); - (self.callback)(&mut move |component: &dyn Render|{ - let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); - match self.direction { - South => { - y = y.plus(h); - h_used = h_used.plus(h); - h_remaining = h_remaining.minus(h); - w_used = w_used.max(w); - }, - East => { - x = x.plus(w); - w_used = w_used.plus(w); - w_remaining = w_remaining.minus(w); - h_used = h_used.max(h); - }, - North | West => { - todo!() - }, - _ => unreachable!(), - } - }); - match self.direction { - North | West => { - todo!() - }, - South | East => { - [to.x(), to.y(), w_used.into(), h_used.into()].into() - }, - _ => unreachable!(), - } - } - fn render (&self, to: &mut E) { - let mut x = to.x(); - let mut y = to.y(); - let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); - let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); - (self.callback)(&mut move |component: &dyn Render|{ - let layout = component.layout([x, y, w_remaining, h_remaining].into()); - match self.direction { - South => { - y = y.plus(layout.h()); - h_remaining = h_remaining.minus(layout.h()); - h_used = h_used.plus(layout.h()); - to.place(layout, component); - }, - East => { - x = x.plus(layout.w()); - w_remaining = w_remaining.minus(layout.w()); - w_used = w_used.plus(layout.h()); - to.place(layout, component); - }, - North | West => { - todo!() - }, - _ => unreachable!() - } - }); - } -} - -/*Stack::down(|add|{ - let mut i = 0; - for (_, name) in self.dirs.iter() { - if i >= self.scroll { - add(&Tui::bold(i == self.index, name.as_str()))?; - } - i += 1; - } - for (_, name) in self.files.iter() { - if i >= self.scroll { - add(&Tui::bold(i == self.index, name.as_str()))?; - } - i += 1; - } - add(&format!("{}/{i}", self.index))?; - Ok(()) -}));*/ - diff --git a/output/src/ops/thunk.rs b/output/src/ops/thunk.rs deleted file mode 100644 index d0e2877..0000000 --- a/output/src/ops/thunk.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::*; -use std::marker::PhantomData; - -/// Lazily-evaluated [Render]able. -pub struct Thunk, F: Fn()->T>( - PhantomData, - F -); -impl, F: Fn()->T> Thunk { - pub const fn new (thunk: F) -> Self { - Self(PhantomData, thunk) - } -} -impl, F: Fn()->T> Content for Thunk { - fn content (&self) -> impl Render { (self.1)() } -} - -pub struct ThunkBox( - PhantomData, - BoxBox>>, -); -impl ThunkBox { - pub const fn new (thunk: BoxBox>>) -> Self { - Self(PhantomData, thunk) - } -} -impl Content for ThunkBox { - fn content (&self) -> impl Render { (&self.1)() } -} -impl FromBox>>> for ThunkBox { - fn from (f: BoxBox>>) -> Self { - Self(PhantomData, f) - } -} -//impl<'a, E: Output, F: Fn()->Box + 'a> + 'a> From for ThunkBox<'a, E> { - //fn from (f: F) -> Self { - //Self(Default::default(), Box::new(f)) - //} -//} - -pub struct ThunkRender(PhantomData, F); -impl ThunkRender { - pub fn new (render: F) -> Self { Self(PhantomData, render) } -} -impl Content for ThunkRender { - fn render (&self, to: &mut E) { (self.1)(to) } -} - -pub struct ThunkLayout< - E: Output, - F1: Fn(E::Area)->E::Area, - F2: Fn(&mut E) ->( - PhantomData, - F1, - F2 -); -implE::Area, F2: Fn(&mut E)> ThunkLayout { - pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) } -} -impl Content for ThunkLayout -where - E: Output, - F1: Fn(E::Area)->E::Area, - F2: Fn(&mut E) -{ - fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) } - fn render (&self, to: &mut E) { (self.2)(to) } -} diff --git a/output/src/space.rs b/output/src/space.rs index 056e12f..3e1298b 100644 --- a/output/src/space.rs +++ b/output/src/space.rs @@ -1,5 +1,274 @@ -mod area; pub use self::area::*; -mod coordinate; pub use self::coordinate::*; -mod direction; pub use self::direction::*; -mod measure; pub use self::measure::*; -mod size; pub use self::size::*; +use crate::*; +use Direction::*; + +/// A cardinal direction. +#[derive(Copy, Clone, PartialEq, Debug)] +#[cfg_attr(test, derive(Arbitrary))] +pub enum Direction { + North, South, East, West, Above, Below +} + +impl Direction { + pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { + let [x, y, w, h] = area.xywh(); + match self { + North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]), + South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]), + East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]), + West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]), + Above | Below => (area.xywh(), area.xywh()) + } + } +} + +/// A linear coordinate. +pub trait Coordinate: Send + Sync + Copy + + Add + + Sub + + Mul + + Div + + Ord + PartialEq + Eq + + Debug + Display + Default + + From + Into + + Into + + Into +{ + fn zero () -> Self { 0.into() } + fn plus (self, other: Self) -> Self; + fn minus (self, other: Self) -> Self { + if self >= other { + self - other + } else { + 0.into() + } + } +} + +impl Coordinate for u16 { + fn plus (self, other: Self) -> Self { + self.saturating_add(other) + } +} + +pub trait Area: From<[N;4]> + Debug + Copy { + fn x (&self) -> N; + fn y (&self) -> N; + fn w (&self) -> N; + fn h (&self) -> N; + fn zero () -> [N;4] { + [N::zero(), N::zero(), N::zero(), N::zero()] + } + fn from_position (pos: impl Size) -> [N;4] { + let [x, y] = pos.wh(); + [x, y, 0.into(), 0.into()] + } + fn from_size (size: impl Size) -> [N;4] { + let [w, h] = size.wh(); + [0.into(), 0.into(), w, h] + } + fn expect_min (&self, w: N, h: N) -> Usually<&Self> { + if self.w() < w || self.h() < h { + Err(format!("min {w}x{h}").into()) + } else { + Ok(self) + } + } + fn xy (&self) -> [N;2] { + [self.x(), self.y()] + } + fn wh (&self) -> [N;2] { + [self.w(), self.h()] + } + fn xywh (&self) -> [N;4] { + [self.x(), self.y(), self.w(), self.h()] + } + fn clip_h (&self, h: N) -> [N;4] { + [self.x(), self.y(), self.w(), self.h().min(h)] + } + fn clip_w (&self, w: N) -> [N;4] { + [self.x(), self.y(), self.w().min(w), self.h()] + } + fn clip (&self, wh: impl Size) -> [N;4] { + [self.x(), self.y(), wh.w(), wh.h()] + } + fn set_w (&self, w: N) -> [N;4] { + [self.x(), self.y(), w, self.h()] + } + fn set_h (&self, h: N) -> [N;4] { + [self.x(), self.y(), self.w(), h] + } + fn x2 (&self) -> N { + self.x().plus(self.w()) + } + fn y2 (&self) -> N { + self.y().plus(self.h()) + } + fn lrtb (&self) -> [N;4] { + [self.x(), self.x2(), self.y(), self.y2()] + } + fn center (&self) -> [N;2] { + [self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())] + } + fn center_x (&self, n: N) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()] + } + fn center_y (&self, n: N) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n] + } + fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m] + } + fn centered (&self) -> [N;2] { + [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] + } + fn iter_x (&self) -> impl Iterator where N: std::iter::Step { + self.x()..(self.x()+self.w()) + } + fn iter_y (&self) -> impl Iterator where N: std::iter::Step { + self.y()..(self.y()+self.h()) + } +} + +impl Area for (N, N, N, N) { + fn x (&self) -> N { self.0 } + fn y (&self) -> N { self.1 } + fn w (&self) -> N { self.2 } + fn h (&self) -> N { self.3 } +} + +impl Area for [N;4] { + fn x (&self) -> N { self[0] } + fn y (&self) -> N { self[1] } + fn w (&self) -> N { self[2] } + fn h (&self) -> N { self[3] } +} + +pub trait Size: From<[N;2]> + Debug + Copy { + fn x (&self) -> N; + fn y (&self) -> N; + fn w (&self) -> N { self.x() } + fn h (&self) -> N { self.y() } + fn wh (&self) -> [N;2] { [self.x(), self.y()] } + fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } + fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } + fn expect_min (&self, w: N, h: N) -> Usually<&Self> { + if self.w() < w || self.h() < h { + Err(format!("min {w}x{h}").into()) + } else { + Ok(self) + } + } + fn zero () -> [N;2] { + [N::zero(), N::zero()] + } + fn to_area_pos (&self) -> [N;4] { + let [x, y] = self.wh(); + [x, y, 0.into(), 0.into()] + } + fn to_area_size (&self) -> [N;4] { + let [w, h] = self.wh(); + [0.into(), 0.into(), w, h] + } +} + +impl Size for (N, N) { + fn x (&self) -> N { self.0 } + fn y (&self) -> N { self.1 } +} + +impl Size for [N;2] { + fn x (&self) -> N { self[0] } + fn y (&self) -> N { self[1] } +} + +pub trait HasSize { + fn size (&self) -> &Measure; + fn width (&self) -> usize { + self.size().w() + } + fn height (&self) -> usize { + self.size().h() + } +} + +impl>> HasSize for T { + fn size (&self) -> &Measure { + self.get() + } +} + +/// A widget that tracks its render width and height +#[derive(Default)] +pub struct Measure { + _engine: PhantomData, + pub x: Arc, + pub y: Arc, +} + +// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small +impl Content for Measure { + fn render (&self, to: &mut E) { + self.x.store(to.area().w().into(), Relaxed); + self.y.store(to.area().h().into(), Relaxed); + } +} + +impl Clone for Measure { + fn clone (&self) -> Self { + Self { + _engine: Default::default(), + x: self.x.clone(), + y: self.y.clone(), + } + } +} + +impl std::fmt::Debug for Measure { + fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + f.debug_struct("Measure") + .field("width", &self.x) + .field("height", &self.y) + .finish() + } +} + +impl Measure { + pub fn new () -> Self { + Self { + _engine: PhantomData::default(), + x: Arc::new(0.into()), + y: Arc::new(0.into()), + } + } + pub fn set_w (&self, w: impl Into) -> &Self { + self.x.store(w.into(), Relaxed); + self + } + pub fn set_h (&self, h: impl Into) -> &Self { + self.y.store(h.into(), Relaxed); + self + } + pub fn set_wh (&self, w: impl Into, h: impl Into) -> &Self { + self.set_w(w); + self.set_h(h); + self + } + pub fn w (&self) -> usize { + self.x.load(Relaxed) + } + pub fn h (&self) -> usize { + self.y.load(Relaxed) + } + pub fn wh (&self) -> [usize;2] { + [self.w(), self.h()] + } + pub fn format (&self) -> Arc { + format!("{}x{}", self.w(), self.h()).into() + } + pub fn of > (&self, item: T) -> Bsp, T> { + Bsp::b(Fill::xy(self), item) + } +} diff --git a/output/src/space/area.rs b/output/src/space/area.rs deleted file mode 100644 index 4393790..0000000 --- a/output/src/space/area.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::*; -use std::fmt::Debug; - -pub trait Area: From<[N;4]> + Debug + Copy { - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N; - fn h (&self) -> N; - fn zero () -> [N;4] { - [N::zero(), N::zero(), N::zero(), N::zero()] - } - fn from_position (pos: impl Size) -> [N;4] { - let [x, y] = pos.wh(); - [x, y, 0.into(), 0.into()] - } - fn from_size (size: impl Size) -> [N;4] { - let [w, h] = size.wh(); - [0.into(), 0.into(), w, h] - } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { - if self.w() < w || self.h() < h { - Err(format!("min {w}x{h}").into()) - } else { - Ok(self) - } - } - fn xy (&self) -> [N;2] { - [self.x(), self.y()] - } - fn wh (&self) -> [N;2] { - [self.w(), self.h()] - } - fn xywh (&self) -> [N;4] { - [self.x(), self.y(), self.w(), self.h()] - } - fn clip_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), self.h().min(h)] - } - fn clip_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), self.w().min(w), self.h()] - } - fn clip (&self, wh: impl Size) -> [N;4] { - [self.x(), self.y(), wh.w(), wh.h()] - } - fn set_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), w, self.h()] - } - fn set_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), h] - } - fn x2 (&self) -> N { - self.x().plus(self.w()) - } - fn y2 (&self) -> N { - self.y().plus(self.h()) - } - fn lrtb (&self) -> [N;4] { - [self.x(), self.x2(), self.y(), self.y2()] - } - fn center (&self) -> [N;2] { - [self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())] - } - fn center_x (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()] - } - fn center_y (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n] - } - fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m] - } - fn centered (&self) -> [N;2] { - [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] - } - fn iter_x (&self) -> impl Iterator where N: std::iter::Step { - self.x()..(self.x()+self.w()) - } - fn iter_y (&self) -> impl Iterator where N: std::iter::Step { - self.y()..(self.y()+self.h()) - } -} - -impl Area for (N, N, N, N) { - fn x (&self) -> N { self.0 } - fn y (&self) -> N { self.1 } - fn w (&self) -> N { self.2 } - fn h (&self) -> N { self.3 } -} - -impl Area for [N;4] { - fn x (&self) -> N { self[0] } - fn y (&self) -> N { self[1] } - fn w (&self) -> N { self[2] } - fn h (&self) -> N { self[3] } -} diff --git a/output/src/space/coordinate.rs b/output/src/space/coordinate.rs deleted file mode 100644 index 262be9e..0000000 --- a/output/src/space/coordinate.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::fmt::{Debug, Display}; -use std::ops::{Add, Sub, Mul, Div}; - -/// A linear coordinate. -pub trait Coordinate: Send + Sync + Copy - + Add - + Sub - + Mul - + Div - + Ord + PartialEq + Eq - + Debug + Display + Default - + From + Into - + Into - + Into -{ - fn zero () -> Self { 0.into() } - fn plus (self, other: Self) -> Self; - fn minus (self, other: Self) -> Self { - if self >= other { - self - other - } else { - 0.into() - } - } -} - -impl Coordinate for u16 { - fn plus (self, other: Self) -> Self { - self.saturating_add(other) - } -} diff --git a/output/src/space/direction.rs b/output/src/space/direction.rs deleted file mode 100644 index ed9df46..0000000 --- a/output/src/space/direction.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::*; -use crate::Direction::*; - -/// A cardinal direction. -#[derive(Copy, Clone, PartialEq, Debug)] -#[cfg_attr(test, derive(Arbitrary))] -pub enum Direction { - North, South, East, West, Above, Below -} - -impl Direction { - pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { - let [x, y, w, h] = area.xywh(); - match self { - North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]), - South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]), - East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]), - West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]), - Above | Below => (area.xywh(), area.xywh()) - } - } -} diff --git a/output/src/space/measure.rs b/output/src/space/measure.rs deleted file mode 100644 index 9ed5c2c..0000000 --- a/output/src/space/measure.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::*; -use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}}; - -pub trait HasSize { - fn size (&self) -> &Measure; - fn width (&self) -> usize { - self.size().w() - } - fn height (&self) -> usize { - self.size().h() - } -} - -impl>> HasSize for T { - fn size (&self) -> &Measure { - self.get() - } -} - -/// A widget that tracks its render width and height -#[derive(Default)] -pub struct Measure { - _engine: PhantomData, - pub x: Arc, - pub y: Arc, -} - -// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small -impl Content for Measure { - fn render (&self, to: &mut E) { - self.x.store(to.area().w().into(), Relaxed); - self.y.store(to.area().h().into(), Relaxed); - } -} - -impl Clone for Measure { - fn clone (&self) -> Self { - Self { - _engine: Default::default(), - x: self.x.clone(), - y: self.y.clone(), - } - } -} - -impl std::fmt::Debug for Measure { - fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - f.debug_struct("Measure") - .field("width", &self.x) - .field("height", &self.y) - .finish() - } -} - -impl Measure { - pub fn new () -> Self { - Self { - _engine: PhantomData::default(), - x: Arc::new(0.into()), - y: Arc::new(0.into()), - } - } - pub fn set_w (&self, w: impl Into) -> &Self { - self.x.store(w.into(), Relaxed); - self - } - pub fn set_h (&self, h: impl Into) -> &Self { - self.y.store(h.into(), Relaxed); - self - } - pub fn set_wh (&self, w: impl Into, h: impl Into) -> &Self { - self.set_w(w); - self.set_h(h); - self - } - pub fn w (&self) -> usize { - self.x.load(Relaxed) - } - pub fn h (&self) -> usize { - self.y.load(Relaxed) - } - pub fn wh (&self) -> [usize;2] { - [self.w(), self.h()] - } - pub fn format (&self) -> Arc { - format!("{}x{}", self.w(), self.h()).into() - } - pub fn of > (&self, item: T) -> Bsp, T> { - Bsp::b(Fill::xy(self), item) - } -} diff --git a/output/src/space/size.rs b/output/src/space/size.rs deleted file mode 100644 index f9e2d19..0000000 --- a/output/src/space/size.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::*; -use std::fmt::Debug; - -pub trait Size: From<[N;2]> + Debug + Copy { - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N { self.x() } - fn h (&self) -> N { self.y() } - fn wh (&self) -> [N;2] { [self.x(), self.y()] } - fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } - fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { - if self.w() < w || self.h() < h { - Err(format!("min {w}x{h}").into()) - } else { - Ok(self) - } - } - fn zero () -> [N;2] { - [N::zero(), N::zero()] - } - fn to_area_pos (&self) -> [N;4] { - let [x, y] = self.wh(); - [x, y, 0.into(), 0.into()] - } - fn to_area_size (&self) -> [N;4] { - let [w, h] = self.wh(); - [0.into(), 0.into(), w, h] - } -} - -impl Size for (N, N) { - fn x (&self) -> N { self.0 } - fn y (&self) -> N { self.1 } -} - -impl Size for [N;2] { - fn x (&self) -> N { self[0] } - fn y (&self) -> N { self[1] } -} diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 5e11c0f..9edf714 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -50,7 +50,7 @@ impl ToTokens for ViewDef { impl ViewDef { fn generated (&self) -> impl ToTokens { - let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; + let Self(ViewMeta { output }, ViewImpl { block, .. }) = self; let self_ty = &block.self_ty; let builtins = self.builtins(); let exposed = self.exposed(); @@ -74,7 +74,7 @@ impl ViewDef { /// Expressions are handled by built-in functions /// that operate over constants and symbols. fn builtins (&self) -> impl ToTokens { - let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; + let Self(ViewMeta { output }, ViewImpl { .. }) = self; let builtins = builtins_with_boxes_output(quote! { #output }).map(|builtin|quote! { ::tengri::dsl::Val::Exp(_, expr) => return Ok(Some( #builtin::from_dsl(self, expr, ||Box::new("failed to load builtin".into()))? @@ -85,7 +85,7 @@ impl ViewDef { } /// Symbols are handled by user-taked functions that take no parameters but `&self`. fn exposed (&self) -> impl ToTokens { - let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; + let Self(ViewMeta { .. }, ViewImpl { exposed, .. }) = self; let exposed = exposed.iter().map(|(key, value)|write_quote(quote! { #key => return Ok(Some(self.#value().boxed())), })); diff --git a/tui/src/tui_engine.rs b/tui/src/tui_engine.rs index b8d60f4..9cd527d 100644 --- a/tui/src/tui_engine.rs +++ b/tui/src/tui_engine.rs @@ -3,7 +3,6 @@ use std::time::Duration; mod tui_buffer; pub use self::tui_buffer::*; mod tui_input; pub use self::tui_input::*; -mod tui_keys; pub use self::tui_keys::*; mod tui_output; pub use self::tui_output::*; mod tui_perf; pub use self::tui_perf::*; @@ -72,8 +71,7 @@ pub trait TuiRun + Handle + 'static> { fn run (&self, state: &Arc>) -> Usually<()>; } -impl + Handle + Send + Sync + 'static> -TuiRun for Arc> { +impl + Handle + Send + Sync + 'static> TuiRun for Arc> { fn run (&self, state: &Arc>) -> Usually<()> { let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100)); self.write().unwrap().setup()?; diff --git a/tui/src/tui_engine/tui_input.rs b/tui/src/tui_engine/tui_input.rs index 9ee6be6..b58c594 100644 --- a/tui/src/tui_engine/tui_input.rs +++ b/tui/src/tui_engine/tui_input.rs @@ -4,21 +4,36 @@ use std::thread::{spawn, JoinHandle}; use crossterm::event::{poll, read}; #[derive(Debug, Clone)] -pub struct TuiIn( +pub struct TuiIn { /// Exit flag - pub Arc, + pub exited: Arc, /// Input event - pub crossterm::event::Event, -); - -impl Input for TuiIn { - type Event = crossterm::event::Event; - type Handled = bool; - fn event (&self) -> &crossterm::event::Event { &self.1 } - fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) } - fn done (&self) { self.0.store(true, Relaxed); } + pub event: TuiEvent, +} +impl Input for TuiIn { + type Event = TuiEvent; + type Handled = bool; + fn event (&self) -> &TuiEvent { &self.event } + fn is_done (&self) -> bool { self.exited.fetch_and(true, Relaxed) } + fn done (&self) { self.exited.store(true, Relaxed); } +} +#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] +pub struct TuiEvent(crossterm::event::Event); +impl Ord for TuiEvent { + fn cmp (&self, other: &Self) -> std::cmp::Ordering { + self.partial_cmp(other) .unwrap_or_else(||format!("{:?}", self).cmp(&format!("{other:?}"))) // FIXME perf + } +} +impl From for TuiEvent { + fn from (event: crossterm::event::Event) -> Self { + Self(event) + } +} +impl From> for TuiEvent { + fn from (x: Arc) -> Self { + TuiEvent(TuiKey::new(x.as_ref()).build().unwrap_or_else(||panic!("invalid key: {x}"))) + } } - impl TuiIn { /// Spawn the input thread. pub fn run_input + Send + Sync + 'static> ( @@ -35,7 +50,6 @@ impl TuiIn { if poll(timer).is_ok() { let event = read().unwrap(); match event { - crossterm::event::Event::Key(KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, @@ -46,7 +60,8 @@ impl TuiIn { }, _ => { let exited = exited.clone(); - if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) { + let event = event.into(); + if let Err(e) = state.write().unwrap().handle(&TuiIn { exited, event }) { panic!("{e}") } } @@ -59,10 +74,101 @@ impl TuiIn { //#[cfg(feature = "dsl")] //impl DslInput for TuiIn { //fn matches_dsl (&self, token: &str) -> bool { - //if let Some(event) = KeyMatcher::new(token).build() { + //if let Some(event) = TuiKey::new(token).build() { //&event == self.event() //} else { //false //} //} //} + +pub struct TuiKey { + valid: bool, + key: Option, + mods: KeyModifiers, +} + +impl TuiKey { + pub fn new (token: impl AsRef) -> Self { + let token = token.as_ref(); + if token.len() < 2 { + Self { valid: false, key: None, mods: KeyModifiers::NONE } + } else if token.chars().next() != Some('@') { + Self { valid: false, key: None, mods: KeyModifiers::NONE } + } else { + Self { valid: true, key: None, mods: KeyModifiers::NONE }.next(&token[1..]) + } + } + pub fn build (self) -> Option { + if self.valid && self.key.is_some() { + Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods))) + } else { + None + } + } + fn next (mut self, token: &str) -> Self { + let mut tokens = token.split('-').peekable(); + while let Some(token) = tokens.next() { + if tokens.peek().is_some() { + match token { + "ctrl" | "Ctrl" | "c" | "C" => self.mods |= KeyModifiers::CONTROL, + "alt" | "Alt" | "m" | "M" => self.mods |= KeyModifiers::ALT, + "shift" | "Shift" | "s" | "S" => { + self.mods |= KeyModifiers::SHIFT; + // + TODO normalize character case, BackTab, etc. + }, + _ => panic!("unknown modifier {token}"), + } + } else { + self.key = if token.len() == 1 { + Some(KeyCode::Char(token.chars().next().unwrap())) + } else { + Some(Self::named_key(token).unwrap_or_else(||panic!("unknown character {token}"))) + } + } + } + self + } + fn named_key (token: &str) -> Option { + use KeyCode::*; + Some(match token { + "up" => Up, + "down" => Down, + "left" => Left, + "right" => Right, + "esc" | "escape" => Esc, + "enter" | "return" => Enter, + "delete" | "del" => Delete, + "tab" => Tab, + "space" => Char(' '), + "comma" => Char(','), + "period" => Char('.'), + "plus" => Char('+'), + "minus" | "dash" => Char('-'), + "equal" | "equals" => Char('='), + "underscore" => Char('_'), + "backtick" => Char('`'), + "lt" => Char('<'), + "gt" => Char('>'), + "cbopen" | "openbrace" => Char('{'), + "cbclose" | "closebrace" => Char('}'), + "bropen" | "openbracket" => Char('['), + "brclose" | "closebracket" => Char(']'), + "pgup" | "pageup" => PageUp, + "pgdn" | "pagedown" => PageDown, + "f1" => F(1), + "f2" => F(2), + "f3" => F(3), + "f4" => F(4), + "f5" => F(5), + "f6" => F(6), + "f7" => F(7), + "f8" => F(8), + "f9" => F(9), + "f10" => F(10), + "f11" => F(11), + "f12" => F(12), + _ => return None, + }) + } +} diff --git a/tui/src/tui_engine/tui_keys.rs b/tui/src/tui_engine/tui_keys.rs deleted file mode 100644 index cda5899..0000000 --- a/tui/src/tui_engine/tui_keys.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::*; - -pub struct KeyMatcher { - valid: bool, - key: Option, - mods: KeyModifiers, -} - -impl KeyMatcher { - pub fn new (token: impl AsRef) -> Self { - let token = token.as_ref(); - if token.len() < 2 { - Self { valid: false, key: None, mods: KeyModifiers::NONE } - } else if token.chars().next() != Some('@') { - Self { valid: false, key: None, mods: KeyModifiers::NONE } - } else { - Self { valid: true, key: None, mods: KeyModifiers::NONE }.next(&token[1..]) - } - } - pub fn build (self) -> Option { - if self.valid && self.key.is_some() { - Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods))) - } else { - None - } - } - fn next (mut self, token: &str) -> Self { - let mut tokens = token.split('-').peekable(); - while let Some(token) = tokens.next() { - if tokens.peek().is_some() { - match token { - "ctrl" | "Ctrl" | "c" | "C" => self.mods |= KeyModifiers::CONTROL, - "alt" | "Alt" | "m" | "M" => self.mods |= KeyModifiers::ALT, - "shift" | "Shift" | "s" | "S" => { - self.mods |= KeyModifiers::SHIFT; - // + TODO normalize character case, BackTab, etc. - }, - _ => panic!("unknown modifier {token}"), - } - } else { - self.key = if token.len() == 1 { - Some(KeyCode::Char(token.chars().next().unwrap())) - } else { - Some(Self::named_key(token).unwrap_or_else(||panic!("unknown character {token}"))) - } - } - } - self - } - fn named_key (token: &str) -> Option { - use KeyCode::*; - Some(match token { - "up" => Up, - "down" => Down, - "left" => Left, - "right" => Right, - "esc" | "escape" => Esc, - "enter" | "return" => Enter, - "delete" | "del" => Delete, - "tab" => Tab, - "space" => Char(' '), - "comma" => Char(','), - "period" => Char('.'), - "plus" => Char('+'), - "minus" | "dash" => Char('-'), - "equal" | "equals" => Char('='), - "underscore" => Char('_'), - "backtick" => Char('`'), - "lt" => Char('<'), - "gt" => Char('>'), - "cbopen" | "openbrace" => Char('{'), - "cbclose" | "closebrace" => Char('}'), - "bropen" | "openbracket" => Char('['), - "brclose" | "closebracket" => Char(']'), - "pgup" | "pageup" => PageUp, - "pgdn" | "pagedown" => PageDown, - "f1" => F(1), - "f2" => F(2), - "f3" => F(3), - "f4" => F(4), - "f5" => F(5), - "f6" => F(6), - "f7" => F(7), - "f8" => F(8), - "f9" => F(9), - "f10" => F(10), - "f11" => F(11), - "f12" => F(12), - _ => return None, - }) - } -}