From 49a219f764d738392bc9877807d0d22aef7d1050 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 29 Dec 2023 23:57:09 -0500 Subject: [PATCH] Move TUI project; Increment cargo version --- Cargo.lock | 556 +++++++++++++++++++++++++++-- Cargo.toml | 18 +- examples/fibonacci.ds | 2 +- examples/list.ds | 2 + src/bin/tui/app.rs | 88 +++++ src/bin/tui/error.rs | 3 + src/bin/tui/interpreter_display.rs | 53 +++ src/{tui/mod.rs => bin/tui/lib.rs} | 44 ++- src/{ => bin}/tui/log.rs | 9 +- src/bin/tui/main.rs | 67 ++++ src/bin/tui/terminal.rs | 229 ++++++++++++ src/bin/tui/value_display.rs | 52 +++ src/tui/editor.rs | 182 ---------- src/tui/output_display.rs | 33 -- src/tui/search_box.rs | 0 15 files changed, 1061 insertions(+), 277 deletions(-) create mode 100644 src/bin/tui/app.rs create mode 100644 src/bin/tui/error.rs create mode 100644 src/bin/tui/interpreter_display.rs rename src/{tui/mod.rs => bin/tui/lib.rs} (85%) rename src/{ => bin}/tui/log.rs (95%) create mode 100644 src/bin/tui/main.rs create mode 100644 src/bin/tui/terminal.rs create mode 100644 src/bin/tui/value_display.rs delete mode 100644 src/tui/editor.rs delete mode 100644 src/tui/output_display.rs delete mode 100644 src/tui/search_box.rs diff --git a/Cargo.lock b/Cargo.lock index 97acb84..0b724f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.1" @@ -26,6 +38,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "ansi_term" version = "0.12.1" @@ -121,6 +139,9 @@ name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +dependencies = [ + "serde", +] [[package]] name = "bumpalo" @@ -134,6 +155,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + [[package]] name = "cc" version = "1.0.83" @@ -201,6 +228,33 @@ dependencies = [ "winapi", ] +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -213,9 +267,9 @@ version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ab77dbd8adecaf3f0db40581631b995f312a8a5ae3aa9993188bb8f23d83a5b" dependencies = [ - "crossterm", - "strum", - "strum_macros", + "crossterm 0.26.1", + "strum 0.24.1", + "strum_macros 0.24.3", "unicode-width", ] @@ -284,6 +338,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.4.0", + "crossterm_winapi", + "futures-core", + "libc", + "mio", + "parking_lot", + "serde", + "signal-hook", + "signal-hook-mio", + "winapi", +] + [[package]] name = "crossterm_winapi" version = "0.9.1" @@ -314,24 +386,58 @@ dependencies = [ "memchr", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + [[package]] name = "dust-lang" -version = "0.3.92" +version = "0.4.0" dependencies = [ "ansi_term", "cc", "clap", + "color-eyre", "comfy-table", + "crossterm 0.27.0", "csv", + "directories", + "futures", "git2", + "lazy_static", "rand", + "ratatui", "rayon", "reqwest", "rustyline", "serde", "serde_json", + "signal-hook", + "tokio", + "tokio-util", "toml", + "tracing", + "tracing-error", + "tracing-subscriber", "tree-sitter", + "tui-textarea", ] [[package]] @@ -392,6 +498,16 @@ dependencies = [ "str-buf", ] +[[package]] +name = "eyre" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -440,46 +556,87 @@ dependencies = [ ] [[package]] -name = "futures-channel" -version = "0.3.28" +name = "futures" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -549,6 +706,10 @@ name = "hashbrown" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -652,6 +813,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.3" @@ -672,12 +839,27 @@ dependencies = [ "hashbrown 0.14.1", ] +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + [[package]] name = "ipnet" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -710,9 +892,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libgit2-sys" @@ -728,6 +910,17 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.0", + "libc", + "redox_syscall 0.4.1", +] + [[package]] name = "libssh2-sys" version = "0.3.0" @@ -776,6 +969,24 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "lru" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +dependencies = [ + "hashbrown 0.14.1", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.6.4" @@ -808,9 +1019,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", @@ -856,6 +1067,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -925,6 +1146,24 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "parking_lot" version = "0.12.1" @@ -943,11 +1182,17 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "percent-encoding" version = "2.3.0" @@ -1036,6 +1281,25 @@ dependencies = [ "getrandom", ] +[[package]] +name = "ratatui" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb" +dependencies = [ + "bitflags 2.4.0", + "cassowary", + "crossterm 0.27.0", + "indoc", + "itertools", + "lru", + "paste", + "stability", + "strum 0.25.0", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "rayon" version = "1.8.0" @@ -1065,6 +1329,26 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.9.6" @@ -1073,8 +1357,17 @@ checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.3.9", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1085,9 +1378,15 @@ checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.7.5" @@ -1288,6 +1587,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook" version = "0.3.17" @@ -1345,14 +1653,24 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys", ] +[[package]] +name = "stability" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "str-buf" version = "1.0.6" @@ -1371,6 +1689,15 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", +] + [[package]] name = "strum_macros" version = "0.24.3" @@ -1384,6 +1711,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.37", +] + [[package]] name = "syn" version = "1.0.109" @@ -1435,11 +1775,41 @@ checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", "windows-sys", ] +[[package]] +name = "thiserror" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1457,9 +1827,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -1467,10 +1837,23 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.4", + "signal-hook-registry", + "socket2 0.5.5", + "tokio-macros", "windows-sys", ] +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -1483,9 +1866,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -1537,22 +1920,73 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] [[package]] -name = "tracing-core" -version = "0.1.31" +name = "tracing-attributes" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -1571,6 +2005,18 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tui-textarea" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e38ced1f941a9cfc923fbf2fe6858443c42cc5220bfd35bdd3648371e7bd8e" +dependencies = [ + "crossterm 0.27.0", + "ratatui", + "regex", + "unicode-width", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -1621,12 +2067,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "want" version = "0.3.1" @@ -1824,3 +2282,23 @@ dependencies = [ "cfg-if", "windows-sys", ] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] diff --git a/Cargo.toml b/Cargo.toml index 4f68ac5..6186493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "dust-lang" description = "General purpose programming language" -version = "0.3.92" +version = "0.4.0" repository = "https://git.jeffa.io/jeff/dust.git" edition = "2021" license = "MIT" @@ -10,6 +10,9 @@ license = "MIT" name = "dust" path = "src/main.rs" +[[bin]] +name = "tui" + [profile.dev] opt-level = 1 [profile.dev.package."*"] @@ -18,17 +21,30 @@ opt-level = 3 [dependencies] ansi_term = "0.12.1" clap = { version = "4.4.4", features = ["derive"] } +color-eyre = "0.6.2" comfy-table = "7.0.1" +crossterm = { version = "0.27.0", features = ["serde", "event-stream"] } csv = "1.2.2" +directories = "5.0.1" +futures = "0.3.30" git2 = "0.18.1" +lazy_static = "1.4.0" rand = "0.8.5" +ratatui = "0.25.0" rayon = "1.8.0" reqwest = { version = "0.11.20", features = ["blocking", "json"] } rustyline = { version = "12.0.0", features = ["derive", "with-file-history"] } serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" +signal-hook = "0.3.17" +tokio = { version = "1.35.1", features = ["signal-hook-registry", "macros", "signal", "rt", "time", "rt-multi-thread"] } +tokio-util = "0.7.10" toml = "0.8.1" +tracing = "0.1.40" +tracing-error = "0.2.0" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tree-sitter = "0.20.10" +tui-textarea = { version = "0.4.0", features = ["search"] } [build-dependencies] cc = "1.0" diff --git a/examples/fibonacci.ds b/examples/fibonacci.ds index 812362c..7d47b8f 100644 --- a/examples/fibonacci.ds +++ b/examples/fibonacci.ds @@ -6,4 +6,4 @@ fib = (i ) { } } -fib(5) +fib(8) diff --git a/examples/list.ds b/examples/list.ds index 85deb38..69ed50b 100644 --- a/examples/list.ds +++ b/examples/list.ds @@ -5,3 +5,5 @@ y = numbers:1 z = numbers:2 assert_equal(x + y, z) + +numbers diff --git a/src/bin/tui/app.rs b/src/bin/tui/app.rs new file mode 100644 index 0000000..d614ce1 --- /dev/null +++ b/src/bin/tui/app.rs @@ -0,0 +1,88 @@ +use std::path::PathBuf; + +use crossterm::event::KeyCode; +use dust_lang::Map; +use ratatui::Frame; +use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; + +use crate::{interpreter_display::InterpreterDisplay, terminal::Terminal, Action, Elm, Result}; + +pub struct App { + action_rx: UnboundedReceiver, + action_tx: UnboundedSender, + interpreter_display: InterpreterDisplay, + should_quit: bool, +} + +impl App { + pub fn new( + action_rx: UnboundedReceiver, + action_tx: UnboundedSender, + path: PathBuf, + ) -> Result { + let interpreter_display = InterpreterDisplay::new(Map::new(), path)?; + + Ok(App { + action_rx, + action_tx, + interpreter_display, + should_quit: false, + }) + } + + pub async fn run(&mut self) -> Result<()> { + let mut terminal = Terminal::new()?.tick_rate(4.0).frame_rate(30.0); + + terminal.enter()?; + + loop { + if self.should_quit { + break; + } + + if let Some(action) = terminal.next().await { + self.action_tx.send(action)?; + } else { + continue; + }; + + while let Ok(action) = self.action_rx.try_recv() { + if let Action::Render = action { + terminal.draw(|frame| { + self.view(frame); + })?; + } + + let next_action = self.update(action)?; + + if let Some(action) = next_action { + self.action_tx.send(action)?; + } + } + } + + terminal.exit()?; + + Ok(()) + } +} + +impl Elm for App { + fn update(&mut self, message: Action) -> Result> { + match message { + Action::Quit => self.should_quit = true, + Action::Key(key_event) => { + if let KeyCode::Esc = key_event.code { + return Ok(Some(Action::Quit)); + } + } + _ => {} + } + + self.interpreter_display.update(message) + } + + fn view(&self, frame: &mut Frame) { + self.interpreter_display.view(frame) + } +} diff --git a/src/bin/tui/error.rs b/src/bin/tui/error.rs new file mode 100644 index 0000000..f1dd913 --- /dev/null +++ b/src/bin/tui/error.rs @@ -0,0 +1,3 @@ +pub type Result = std::result::Result; + +pub enum Error {} diff --git a/src/bin/tui/interpreter_display.rs b/src/bin/tui/interpreter_display.rs new file mode 100644 index 0000000..8ac54e5 --- /dev/null +++ b/src/bin/tui/interpreter_display.rs @@ -0,0 +1,53 @@ +use std::{fs::read_to_string, path::PathBuf, time::SystemTime}; + +use dust_lang::{Interpreter, Map, Value}; +use ratatui::Frame; + +use crate::{value_display::ValueDisplay, Action, Elm, Result}; + +pub struct InterpreterDisplay { + interpreter: Interpreter, + path: PathBuf, + value_display: ValueDisplay, + modified: SystemTime, +} + +impl InterpreterDisplay { + pub fn new(context: Map, path: PathBuf) -> Result { + let interpreter = Interpreter::new(context)?; + let value_display = ValueDisplay::new(Value::default()); + let modified = SystemTime::now(); + + Ok(Self { + interpreter, + path, + value_display, + modified, + }) + } +} + +impl Elm for InterpreterDisplay { + fn update(&mut self, message: Action) -> Result> { + match message { + Action::Tick => { + let last_modified = self.path.metadata()?.modified()?; + + if last_modified != self.modified { + let source = read_to_string(&self.path)?; + let value = self.interpreter.run(&source)?; + + self.value_display = ValueDisplay::new(value); + self.modified = last_modified; + } + } + _ => {} + } + + self.value_display.update(message) + } + + fn view(&self, frame: &mut Frame) { + self.value_display.view(frame) + } +} diff --git a/src/tui/mod.rs b/src/bin/tui/lib.rs similarity index 85% rename from src/tui/mod.rs rename to src/bin/tui/lib.rs index 3d17658..811d933 100644 --- a/src/tui/mod.rs +++ b/src/bin/tui/lib.rs @@ -1,4 +1,9 @@ -mod log; +pub mod app; +pub mod buffer; +pub mod error; +pub mod interpreter_display; +pub mod log; +pub mod value_display; use std::{ ops::{Deref, DerefMut}, @@ -14,8 +19,9 @@ use crossterm::{ }, terminal::{EnterAlternateScreen, LeaveAlternateScreen}, }; +use dust_lang::Value; use futures::{FutureExt, StreamExt}; -use ratatui::backend::CrosstermBackend as Backend; +use ratatui::{backend::CrosstermBackend as Backend, Frame}; use serde::{Deserialize, Serialize}; use tokio::{ sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, @@ -23,8 +29,13 @@ use tokio::{ }; use tokio_util::sync::CancellationToken; +pub trait Elm { + fn update(&mut self, message: Action) -> Result>; + fn view(&self, frame: &mut Frame); +} + #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum Event { +pub enum Action { Init, Quit, Error, @@ -37,14 +48,15 @@ pub enum Event { Key(KeyEvent), Mouse(MouseEvent), Resize(u16, u16), + UpdateValue(Value), } pub struct Tui { pub terminal: ratatui::Terminal>, pub task: JoinHandle<()>, pub cancellation_token: CancellationToken, - pub event_rx: UnboundedReceiver, - pub event_tx: UnboundedSender, + pub event_rx: UnboundedReceiver, + pub event_tx: UnboundedSender, pub frame_rate: f64, pub tick_rate: f64, pub mouse: bool, @@ -105,7 +117,7 @@ impl Tui { let mut reader = crossterm::event::EventStream::new(); let mut tick_interval = tokio::time::interval(tick_delay); let mut render_interval = tokio::time::interval(render_delay); - _event_tx.send(Event::Init).unwrap(); + _event_tx.send(Action::Init).unwrap(); loop { let tick_delay = tick_interval.tick(); let render_delay = render_interval.tick(); @@ -120,37 +132,37 @@ impl Tui { match evt { CrosstermEvent::Key(key) => { if key.kind == KeyEventKind::Press { - _event_tx.send(Event::Key(key)).unwrap(); + _event_tx.send(Action::Key(key)).unwrap(); } }, CrosstermEvent::Mouse(mouse) => { - _event_tx.send(Event::Mouse(mouse)).unwrap(); + _event_tx.send(Action::Mouse(mouse)).unwrap(); }, CrosstermEvent::Resize(x, y) => { - _event_tx.send(Event::Resize(x, y)).unwrap(); + _event_tx.send(Action::Resize(x, y)).unwrap(); }, CrosstermEvent::FocusLost => { - _event_tx.send(Event::FocusLost).unwrap(); + _event_tx.send(Action::FocusLost).unwrap(); }, CrosstermEvent::FocusGained => { - _event_tx.send(Event::FocusGained).unwrap(); + _event_tx.send(Action::FocusGained).unwrap(); }, CrosstermEvent::Paste(s) => { - _event_tx.send(Event::Paste(s)).unwrap(); + _event_tx.send(Action::Paste(s)).unwrap(); }, } } Some(Err(_)) => { - _event_tx.send(Event::Error).unwrap(); + _event_tx.send(Action::Error).unwrap(); } None => {}, } }, _ = tick_delay => { - _event_tx.send(Event::Tick).unwrap(); + _event_tx.send(Action::Tick).unwrap(); }, _ = render_delay => { - _event_tx.send(Event::Render).unwrap(); + _event_tx.send(Action::Render).unwrap(); }, } } @@ -219,7 +231,7 @@ impl Tui { Ok(()) } - pub async fn next(&mut self) -> Option { + pub async fn next(&mut self) -> Option { self.event_rx.recv().await } } diff --git a/src/tui/log.rs b/src/bin/tui/log.rs similarity index 95% rename from src/tui/log.rs rename to src/bin/tui/log.rs index 25003f7..00bd511 100644 --- a/src/tui/log.rs +++ b/src/bin/tui/log.rs @@ -17,19 +17,18 @@ lazy_static! { pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME")); } -fn project_directory() -> Option { +pub fn project_directory() -> Option { ProjectDirs::from("io", "jeffa", env!("CARGO_PKG_NAME")) } pub fn get_data_dir() -> PathBuf { - let directory = if let Some(s) = DATA_FOLDER.clone() { - s + if let Some(path) = DATA_FOLDER.clone() { + path } else if let Some(proj_dirs) = project_directory() { proj_dirs.data_local_dir().to_path_buf() } else { PathBuf::from(".").join(".data") - }; - directory + } } pub fn initialize_logging() -> Result<()> { diff --git a/src/bin/tui/main.rs b/src/bin/tui/main.rs new file mode 100644 index 0000000..c8f8add --- /dev/null +++ b/src/bin/tui/main.rs @@ -0,0 +1,67 @@ +pub mod app; +pub mod interpreter_display; +pub mod log; +pub mod terminal; +pub mod value_display; + +use std::path::PathBuf; + +use crate::{ + app::App, + log::{get_data_dir, initialize_logging}, +}; +use clap::Parser; +use color_eyre::Result; +use crossterm::event::{KeyEvent, MouseEvent}; +use dust_lang::Value; +use ratatui::Frame; +use serde::{Deserialize, Serialize}; +use tokio::sync::mpsc; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// File with source to be run and watched by the shell. + path: Option, +} + +pub trait Elm { + fn update(&mut self, message: Action) -> Result>; + fn view(&self, frame: &mut Frame); +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum Action { + Init, + Quit, + Error, + Closed, + Tick, + Render, + FocusGained, + FocusLost, + Paste(String), + Key(KeyEvent), + Mouse(MouseEvent), + Resize(u16, u16), + UpdateValue(Value), +} + +#[tokio::main] +async fn main() { + initialize_logging().unwrap(); + + let args = Cli::parse(); + let (action_tx, action_rx) = mpsc::unbounded_channel(); + let path = if let Some(path) = args.path { + PathBuf::from(path) + } else { + PathBuf::from(format!("{}/scratch.ds", get_data_dir().to_string_lossy())) + }; + let mut app = App::new(action_rx, action_tx, path).unwrap(); + let run_result = app.run().await; + + if let Err(report) = run_result { + eprintln!("{report}") + } +} diff --git a/src/bin/tui/terminal.rs b/src/bin/tui/terminal.rs new file mode 100644 index 0000000..cdb943b --- /dev/null +++ b/src/bin/tui/terminal.rs @@ -0,0 +1,229 @@ +use std::{ + io::{stderr, Stderr}, + ops::{Deref, DerefMut}, + time::Duration, +}; + +use color_eyre::eyre::Result; +use crossterm::{ + cursor, + event::{ + DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, + Event, KeyEventKind, + }, + terminal::{EnterAlternateScreen, LeaveAlternateScreen}, +}; +use futures::{FutureExt, StreamExt}; +use ratatui::prelude::CrosstermBackend; +use tokio::{ + sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, + task::JoinHandle, +}; +use tokio_util::sync::CancellationToken; + +use crate::{log, Action}; + +pub struct Terminal { + pub terminal: ratatui::Terminal>, + pub task: JoinHandle<()>, + pub cancellation_token: CancellationToken, + pub event_rx: UnboundedReceiver, + pub event_tx: UnboundedSender, + pub frame_rate: f64, + pub tick_rate: f64, + pub mouse: bool, + pub paste: bool, +} + +impl Terminal { + pub fn new() -> Result { + let tick_rate = 4.0; + let frame_rate = 60.0; + let terminal = ratatui::Terminal::new(CrosstermBackend::new(stderr()))?; + let (event_tx, event_rx) = mpsc::unbounded_channel(); + let cancellation_token = CancellationToken::new(); + let task = tokio::spawn(async {}); + let mouse = false; + let paste = false; + Ok(Self { + terminal, + task, + cancellation_token, + event_rx, + event_tx, + frame_rate, + tick_rate, + mouse, + paste, + }) + } + + pub fn tick_rate(mut self, tick_rate: f64) -> Self { + self.tick_rate = tick_rate; + self + } + + pub fn frame_rate(mut self, frame_rate: f64) -> Self { + self.frame_rate = frame_rate; + self + } + + pub fn mouse(mut self, mouse: bool) -> Self { + self.mouse = mouse; + self + } + + pub fn paste(mut self, paste: bool) -> Self { + self.paste = paste; + self + } + + pub fn start(&mut self) { + let tick_delay = std::time::Duration::from_secs_f64(1.0 / self.tick_rate); + let render_delay = std::time::Duration::from_secs_f64(1.0 / self.frame_rate); + self.cancel(); + self.cancellation_token = CancellationToken::new(); + let _cancellation_token = self.cancellation_token.clone(); + let _event_tx = self.event_tx.clone(); + self.task = tokio::spawn(async move { + let mut reader = crossterm::event::EventStream::new(); + let mut tick_interval = tokio::time::interval(tick_delay); + let mut render_interval = tokio::time::interval(render_delay); + _event_tx.send(Action::Init).unwrap(); + loop { + let tick_delay = tick_interval.tick(); + let render_delay = render_interval.tick(); + let crossterm_event = reader.next().fuse(); + tokio::select! { + _ = _cancellation_token.cancelled() => { + break; + } + maybe_event = crossterm_event => { + match maybe_event { + Some(Ok(evt)) => { + match evt { + Event::Key(key) => { + if key.kind == KeyEventKind::Press { + _event_tx.send(Action::Key(key)).unwrap(); + } + }, + Event::Mouse(mouse) => { + _event_tx.send(Action::Mouse(mouse)).unwrap(); + }, + Event::Resize(x, y) => { + _event_tx.send(Action::Resize(x, y)).unwrap(); + }, + Event::FocusLost => { + _event_tx.send(Action::FocusLost).unwrap(); + }, + Event::FocusGained => { + _event_tx.send(Action::FocusGained).unwrap(); + }, + Event::Paste(s) => { + _event_tx.send(Action::Paste(s)).unwrap(); + }, + } + } + Some(Err(_)) => { + _event_tx.send(Action::Error).unwrap(); + } + None => {}, + } + }, + _ = tick_delay => { + _event_tx.send(Action::Tick).unwrap(); + }, + _ = render_delay => { + _event_tx.send(Action::Render).unwrap(); + }, + } + } + }); + } + + pub fn stop(&self) -> Result<()> { + self.cancel(); + let mut counter = 0; + while !self.task.is_finished() { + std::thread::sleep(Duration::from_millis(1)); + counter += 1; + if counter > 50 { + self.task.abort(); + } + if counter > 100 { + log::error!("Failed to abort task in 100 milliseconds for unknown reason"); + break; + } + } + Ok(()) + } + + pub fn enter(&mut self) -> Result<()> { + crossterm::terminal::enable_raw_mode()?; + crossterm::execute!(std::io::stderr(), EnterAlternateScreen, cursor::Hide)?; + if self.mouse { + crossterm::execute!(std::io::stderr(), EnableMouseCapture)?; + } + if self.paste { + crossterm::execute!(std::io::stderr(), EnableBracketedPaste)?; + } + self.start(); + Ok(()) + } + + pub fn exit(&mut self) -> Result<()> { + self.stop()?; + if crossterm::terminal::is_raw_mode_enabled()? { + self.flush()?; + if self.paste { + crossterm::execute!(std::io::stderr(), DisableBracketedPaste)?; + } + if self.mouse { + crossterm::execute!(std::io::stderr(), DisableMouseCapture)?; + } + crossterm::execute!(std::io::stderr(), LeaveAlternateScreen, cursor::Show)?; + crossterm::terminal::disable_raw_mode()?; + } + Ok(()) + } + + pub fn cancel(&self) { + self.cancellation_token.cancel(); + } + + pub fn suspend(&mut self) -> Result<()> { + self.exit()?; + #[cfg(not(windows))] + signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP)?; + Ok(()) + } + + pub fn resume(&mut self) -> Result<()> { + self.enter()?; + Ok(()) + } + + pub async fn next(&mut self) -> Option { + self.event_rx.recv().await + } +} + +impl Deref for Terminal { + type Target = ratatui::Terminal>; + + fn deref(&self) -> &Self::Target { + &self.terminal + } +} + +impl DerefMut for Terminal { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.terminal + } +} + +impl Drop for Terminal { + fn drop(&mut self) { + self.exit().unwrap(); + } +} diff --git a/src/bin/tui/value_display.rs b/src/bin/tui/value_display.rs new file mode 100644 index 0000000..2124f85 --- /dev/null +++ b/src/bin/tui/value_display.rs @@ -0,0 +1,52 @@ +use dust_lang::Value; +use ratatui::{ + style::{Color, Style}, + widgets::{Block, Borders, List, Paragraph}, + Frame, +}; + +use crate::{Action, Elm, Result}; + +pub struct ValueDisplay { + value: Value, +} + +impl ValueDisplay { + pub fn new(value: Value) -> Self { + ValueDisplay { value } + } +} + +impl Elm for ValueDisplay { + fn update(&mut self, _message: Action) -> Result> { + Ok(None) + } + + fn view(&self, frame: &mut Frame) { + match &self.value { + Value::List(list) => { + let widget = List::new(list.items().iter().map(|value| value.to_string())) + .block(Block::default().title("list").borders(Borders::all())); + + frame.render_widget(widget, frame.size()); + } + Value::Map(_) => todo!(), + Value::Function(_) => todo!(), + Value::String(string) => { + let widget = + Paragraph::new(string.as_str()).style(Style::default().fg(Color::Green)); + + frame.render_widget(widget, frame.size()); + } + Value::Float(_) => todo!(), + Value::Integer(integer) => { + let widget = + Paragraph::new(integer.to_string()).style(Style::default().fg(Color::Red)); + + frame.render_widget(widget, frame.size()); + } + Value::Boolean(_) => todo!(), + Value::Option(_) => todo!(), + } + } +} diff --git a/src/tui/editor.rs b/src/tui/editor.rs deleted file mode 100644 index c88e877..0000000 --- a/src/tui/editor.rs +++ /dev/null @@ -1,182 +0,0 @@ -use dust_lang::Result; -use ratatui::{ - layout::{Constraint, Direction, Layout}, - prelude::*, - style::{Modifier, Style}, - text::{Line, Span}, - widgets::{Block, Borders, Paragraph}, -}; -use std::{ - borrow::Cow, - fmt::Display, - fs::File, - io::{self, Write}, - path::PathBuf, -}; -use tui_textarea::{CursorMove, Input, Key, TextArea}; - -use super::Action; - -pub struct Editor<'a> { - current: usize, - buffers: Vec>, - message: Option>, -} - -impl<'a> Editor<'a> { - pub fn new() -> Result { - Ok(Self { - current: 0, - buffers: Vec::new(), - message: None, - }) - } - - pub fn current_buffer(&self) -> &Buffer { - &self.buffers[self.current] - } - - pub fn add_buffer(&mut self, buffer: Buffer<'a>) { - self.buffers.push(buffer); - } - - pub fn run(&mut self, frame: &mut Frame, areas: &[Rect]) -> Option { - let buffer = &self.buffers[self.current]; - let textarea = &buffer.textarea; - let widget = textarea.widget(); - - frame.render_widget(widget, areas[0]); - - // Render status line - let modified = if buffer.modified { " [modified]" } else { "" }; - let slot = format!("[{}/{}]", self.current + 1, self.buffers.len()); - let path_text = if let Some(path) = &buffer.path { - format!(" {}{} ", path.display(), modified) - } else { - "scratch".to_string() - }; - let (row, col) = textarea.cursor(); - let cursor = format!("({},{})", row + 1, col + 1); - - let status_chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints( - [ - Constraint::Length(slot.len() as u16), - Constraint::Min(1), - Constraint::Length(cursor.len() as u16), - ] - .as_ref(), - ) - .split(areas[1]); - let status_style = Style::default().add_modifier(Modifier::REVERSED); - frame.render_widget(Paragraph::new(slot).style(status_style), status_chunks[0]); - frame.render_widget( - Paragraph::new(path_text).style(status_style), - status_chunks[1], - ); - frame.render_widget(Paragraph::new(cursor).style(status_style), status_chunks[2]); - - // Render message at bottom - let message = if let Some(message) = self.message.take() { - Line::from(Span::raw(message)) - } else { - Line::from(vec![ - Span::raw("Press "), - Span::styled("^Q", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(" to quit, "), - Span::styled("^S", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(" to save, "), - Span::styled("^G", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(" to search, "), - Span::styled("^T", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(" to switch buffer "), - Span::styled("^R", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(" to run"), - ]) - }; - frame.render_widget(Paragraph::new(message), areas[2]); - - match crossterm::event::read().unwrap().into() { - Input { - key: Key::Char('r'), - ctrl: true, - .. - } => return Some(Action::Submit), - Input { - key: Key::Char('q'), - ctrl: true, - .. - } => return Some(Action::Quit), - Input { - key: Key::Char('t'), - ctrl: true, - .. - } => { - self.current = (self.current + 1) % self.buffers.len(); - self.message = Some(format!("Switched to buffer #{}", self.current + 1).into()); - } - Input { - key: Key::Char('s'), - ctrl: true, - .. - } => { - self.buffers[self.current].save().unwrap(); - self.message = Some("Saved!".into()); - } - input => { - let buffer = &mut self.buffers[self.current]; - buffer.modified = buffer.textarea.input(input); - } - } - - None - } -} - -pub struct Buffer<'a> { - textarea: TextArea<'a>, - path: Option, - modified: bool, -} - -impl<'a> Buffer<'a> { - pub fn new(content: String) -> Result { - let mut textarea = TextArea::new(content.lines().map(|line| line.to_string()).collect()); - - textarea.set_line_number_style(Style::default().fg(Color::DarkGray)); - - Ok(Self { - textarea, - path: None, - modified: false, - }) - } - - pub fn content(&self) -> String { - self.textarea.lines().join("\n") - } - - fn save(&mut self) -> io::Result<()> { - if !self.modified { - return Ok(()); - } - - let file = if let Some(path) = &self.path { - File::create(path)? - } else { - File::create("/tmp/dust_buffer")? - }; - - let mut writer = io::BufWriter::new(file); - - for line in self.textarea.lines() { - writer.write_all(line.as_bytes())?; - writer.write_all(b"\n")?; - } - - self.modified = false; - - Ok(()) - } -} diff --git a/src/tui/output_display.rs b/src/tui/output_display.rs deleted file mode 100644 index e908187..0000000 --- a/src/tui/output_display.rs +++ /dev/null @@ -1,33 +0,0 @@ -use dust_lang::Value; -use ratatui::{prelude::Rect, widgets::Paragraph, Frame}; - -pub struct OutputDisplay { - values: Vec, -} - -impl OutputDisplay { - pub fn new() -> Self { - OutputDisplay { values: Vec::new() } - } - - pub fn add_value(&mut self, value: Value) { - self.values.push(value); - } - - pub fn run(&self, frame: &mut Frame, area: Rect) { - for value in &self.values { - match value { - Value::List(_) => todo!(), - Value::Map(_) => todo!(), - Value::Function(_) => todo!(), - Value::String(string) => frame.render_widget(Paragraph::new(string.as_str()), area), - Value::Float(_) => todo!(), - Value::Integer(integer) => { - frame.render_widget(Paragraph::new(integer.to_string()), area) - } - Value::Boolean(_) => todo!(), - Value::Option(_) => todo!(), - } - } - } -} diff --git a/src/tui/search_box.rs b/src/tui/search_box.rs deleted file mode 100644 index e69de29..0000000